[verified] add Nextcloud file comment shortcut
This commit is contained in:
@@ -0,0 +1,53 @@
|
|||||||
|
# Korean comments action for Nextcloud 34
|
||||||
|
|
||||||
|
Minimal custom app that adds **댓글** to each eligible Files row action menu and opens that node's comments UI. On Nextcloud 34 with Activity enabled, comments are integrated into sidebar tab `activity`; if Activity is disabled, the app falls back to tab `comments`.
|
||||||
|
|
||||||
|
## Why these APIs
|
||||||
|
|
||||||
|
The installed Nextcloud 34.0.4 bundle uses `@nextcloud/files` 4.0.0. Its own Comments app implements the same flow with `registerFileAction()`, `getSidebar()`, `sidebar.open(node, tabId)`, and `sidebar.setActiveTab(tabId)`. The installed Activity app registers sidebar tab ID `activity`; Comments registers tab ID `comments` only when Activity integration is unavailable.
|
||||||
|
|
||||||
|
The PHP bootstrap listens for `OCA\Files\Event\LoadAdditionalScriptsEvent`, the supported event fired while Files is rendered, and loads the frontend with `OCP\Util::addInitScript()`.
|
||||||
|
|
||||||
|
## Build and automated checks
|
||||||
|
|
||||||
|
Use Node 24, matching the installed package engines:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm ci
|
||||||
|
npm run typecheck
|
||||||
|
npm test
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
The production entry is generated as `js/korean_comments_action-main.mjs`. `Util::addInitScript(Application::APP_ID, 'main')` resolves that app-prefixed asset.
|
||||||
|
|
||||||
|
## Deployment commands (not run during this investigation)
|
||||||
|
|
||||||
|
Copy the app directory to `/data/nextcloud/app/custom_apps/korean_comments_action`, retaining `appinfo/`, `lib/`, and generated `js/` (source/build metadata may also be retained), then run OCC in the Nextcloud PHP container as the web user:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
php occ app:enable korean_comments_action
|
||||||
|
```
|
||||||
|
|
||||||
|
Disable/rollback with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
php occ app:disable korean_comments_action
|
||||||
|
```
|
||||||
|
|
||||||
|
## Manual acceptance test
|
||||||
|
|
||||||
|
1. Open **Files → Personal files**, then open a file row's `…` menu. Confirm one **댓글** item appears and is not an inline icon.
|
||||||
|
2. Click **댓글**. Confirm the details sidebar opens for that exact filename, the **Activity** tab is active, existing comments load, and the comment composer is visible.
|
||||||
|
3. On the same file, switch to another sidebar tab and click **댓글** again. Confirm it switches back to Activity without closing the sidebar.
|
||||||
|
4. With the sidebar still open, use **댓글** on another file. Confirm the sidebar header and comments belong to the second file.
|
||||||
|
5. Select multiple rows. Confirm the action is not offered. Confirm it is also absent for nodes outside the supported `/files/<user>` tree or with `Permission.NONE`.
|
||||||
|
6. Optional fallback check on a test instance: disable Activity while leaving Comments enabled, reload Files, and confirm **댓글** opens the standalone **Comments** tab.
|
||||||
|
|
||||||
|
## Primary references
|
||||||
|
|
||||||
|
- Files event: https://docs.nextcloud.com/server/latest/developer_manual/basics/events.html
|
||||||
|
- Files frontend API: https://nextcloud-libraries.github.io/nextcloud-files/
|
||||||
|
- `registerFileAction`: https://nextcloud-libraries.github.io/nextcloud-files/functions/index.registerFileAction.html
|
||||||
|
- `getSidebar`: https://nextcloud-libraries.github.io/nextcloud-files/functions/index.getSidebar.html
|
||||||
|
- Bootstrap API: https://docs.nextcloud.com/server/latest/developer_manual/app_development/bootstrap.html
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<info xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:noNamespaceSchemaLocation="https://apps.nextcloud.com/schema/apps/info.xsd">
|
||||||
|
<id>korean_comments_action</id>
|
||||||
|
<name>Korean comments action</name>
|
||||||
|
<summary>Adds a 댓글 action to the Files row menu</summary>
|
||||||
|
<description>Adds a Korean-labeled file action that opens the Activity/comments sidebar.</description>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
<licence>agpl</licence>
|
||||||
|
<author>Hera (헤라)</author>
|
||||||
|
<namespace>KoreanCommentsAction</namespace>
|
||||||
|
<category>files</category>
|
||||||
|
<dependencies>
|
||||||
|
<nextcloud min-version="34" max-version="34"/>
|
||||||
|
</dependencies>
|
||||||
|
</info>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
/* extracted by css-entry-points-plugin */
|
||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,190 @@
|
|||||||
|
SPDX-License-Identifier: (MPL-2.0 OR Apache-2.0)
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
SPDX-License-Identifier: BlueOak-1.0.0
|
||||||
|
SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
SPDX-License-Identifier: ISC
|
||||||
|
SPDX-License-Identifier: MIT
|
||||||
|
SPDX-License-Identifier: undefined
|
||||||
|
SPDX-FileCopyrightText: Borewit
|
||||||
|
SPDX-FileCopyrightText: David Myers <hello@davidmyers.dev>
|
||||||
|
SPDX-FileCopyrightText: Dr.-Ing. Mario Heiderich, Cure53 <mario@cure53.de> (https://cure53.de/)
|
||||||
|
SPDX-FileCopyrightText: Evan You
|
||||||
|
SPDX-FileCopyrightText: Feross Aboukhadijeh
|
||||||
|
SPDX-FileCopyrightText: GitHub Inc.
|
||||||
|
SPDX-FileCopyrightText: Irakli Gozalishvili <rfobic@gmail.com> (http://jeditoolkit.com)
|
||||||
|
SPDX-FileCopyrightText: Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me/)
|
||||||
|
SPDX-FileCopyrightText: James Halliday
|
||||||
|
SPDX-FileCopyrightText: Jonas Schade <derzade@gmail.com>
|
||||||
|
SPDX-FileCopyrightText: Jordan Harbamd <ljharb@gmail.com>
|
||||||
|
SPDX-FileCopyrightText: Jordan Harband
|
||||||
|
SPDX-FileCopyrightText: Jordan Harband <ljharb@gmail.com>
|
||||||
|
SPDX-FileCopyrightText: Joyent
|
||||||
|
SPDX-FileCopyrightText: Nathan Rajlich <nathan@tootallnate.net> (http://n8.io/)
|
||||||
|
SPDX-FileCopyrightText: Nextcloud GmbH and Nextcloud contributors
|
||||||
|
SPDX-FileCopyrightText: Raynos <raynos2@gmail.com>
|
||||||
|
SPDX-FileCopyrightText: Sindre Sorhus
|
||||||
|
SPDX-FileCopyrightText: inherits developers
|
||||||
|
SPDX-FileCopyrightText: korean-comments-action developers
|
||||||
|
SPDX-FileCopyrightText: readable-stream developers
|
||||||
|
SPDX-FileCopyrightText: string_decoder developers
|
||||||
|
|
||||||
|
This file is generated from multiple sources. Included packages:
|
||||||
|
- @file-type/xml
|
||||||
|
- version: 0.4.4
|
||||||
|
- license: MIT
|
||||||
|
- @nextcloud/auth
|
||||||
|
- version: 2.6.0
|
||||||
|
- license: GPL-3.0-or-later
|
||||||
|
- @nextcloud/browser-storage
|
||||||
|
- version: 0.5.0
|
||||||
|
- license: GPL-3.0-or-later
|
||||||
|
- @nextcloud/event-bus
|
||||||
|
- version: 3.3.3
|
||||||
|
- license: GPL-3.0-or-later
|
||||||
|
- @nextcloud/files
|
||||||
|
- version: 4.0.0
|
||||||
|
- license: AGPL-3.0-or-later
|
||||||
|
- @nextcloud/initial-state
|
||||||
|
- version: 3.0.0
|
||||||
|
- license: GPL-3.0-or-later
|
||||||
|
- @nextcloud/l10n
|
||||||
|
- version: 3.4.1
|
||||||
|
- license: GPL-3.0-or-later
|
||||||
|
- @nextcloud/logger
|
||||||
|
- version: 3.0.3
|
||||||
|
- license: GPL-3.0-or-later
|
||||||
|
- available-typed-arrays
|
||||||
|
- version: 1.0.7
|
||||||
|
- license: MIT
|
||||||
|
- call-bind
|
||||||
|
- version: 1.0.9
|
||||||
|
- license: MIT
|
||||||
|
- call-bind-apply-helpers
|
||||||
|
- version: 1.0.2
|
||||||
|
- license: MIT
|
||||||
|
- call-bound
|
||||||
|
- version: 1.0.4
|
||||||
|
- license: MIT
|
||||||
|
- define-data-property
|
||||||
|
- version: 1.1.4
|
||||||
|
- license: MIT
|
||||||
|
- dompurify
|
||||||
|
- version: 3.4.15
|
||||||
|
- license: (MPL-2.0 OR Apache-2.0)
|
||||||
|
- dunder-proto
|
||||||
|
- version: 1.0.1
|
||||||
|
- license: MIT
|
||||||
|
- es-define-property
|
||||||
|
- version: 1.0.1
|
||||||
|
- license: MIT
|
||||||
|
- es-errors
|
||||||
|
- version: 1.3.0
|
||||||
|
- license: MIT
|
||||||
|
- es-object-atoms
|
||||||
|
- version: 1.1.2
|
||||||
|
- license: MIT
|
||||||
|
- events
|
||||||
|
- version: 3.3.0
|
||||||
|
- license: MIT
|
||||||
|
- for-each
|
||||||
|
- version: 0.3.5
|
||||||
|
- license: MIT
|
||||||
|
- function-bind
|
||||||
|
- version: 1.1.2
|
||||||
|
- license: MIT
|
||||||
|
- generator-function
|
||||||
|
- version: 2.0.1
|
||||||
|
- license: MIT
|
||||||
|
- get-intrinsic
|
||||||
|
- version: 1.3.0
|
||||||
|
- license: MIT
|
||||||
|
- get-proto
|
||||||
|
- version: 1.0.1
|
||||||
|
- license: MIT
|
||||||
|
- gopd
|
||||||
|
- version: 1.2.0
|
||||||
|
- license: MIT
|
||||||
|
- has-property-descriptors
|
||||||
|
- version: 1.0.2
|
||||||
|
- license: MIT
|
||||||
|
- has-symbols
|
||||||
|
- version: 1.1.0
|
||||||
|
- license: MIT
|
||||||
|
- has-tostringtag
|
||||||
|
- version: 1.0.2
|
||||||
|
- license: MIT
|
||||||
|
- hasown
|
||||||
|
- version: 2.0.4
|
||||||
|
- license: MIT
|
||||||
|
- inherits
|
||||||
|
- version: 2.0.4
|
||||||
|
- license: ISC
|
||||||
|
- is-arguments
|
||||||
|
- version: 1.2.0
|
||||||
|
- license: MIT
|
||||||
|
- is-callable
|
||||||
|
- version: 1.2.7
|
||||||
|
- license: MIT
|
||||||
|
- is-generator-function
|
||||||
|
- version: 1.1.2
|
||||||
|
- license: MIT
|
||||||
|
- is-regex
|
||||||
|
- version: 1.2.1
|
||||||
|
- license: MIT
|
||||||
|
- is-svg
|
||||||
|
- version: 6.1.0
|
||||||
|
- license: MIT
|
||||||
|
- is-typed-array
|
||||||
|
- version: 1.1.15
|
||||||
|
- license: MIT
|
||||||
|
- korean-comments-action
|
||||||
|
- version: 1.0.0
|
||||||
|
- license: undefined
|
||||||
|
- math-intrinsics
|
||||||
|
- version: 1.1.0
|
||||||
|
- license: MIT
|
||||||
|
- possible-typed-array-names
|
||||||
|
- version: 1.1.0
|
||||||
|
- license: MIT
|
||||||
|
- readable-stream
|
||||||
|
- version: 3.6.2
|
||||||
|
- license: MIT
|
||||||
|
- safe-buffer
|
||||||
|
- version: 5.2.1
|
||||||
|
- license: MIT
|
||||||
|
- safe-regex-test
|
||||||
|
- version: 1.1.0
|
||||||
|
- license: MIT
|
||||||
|
- sax
|
||||||
|
- version: 1.6.1
|
||||||
|
- license: BlueOak-1.0.0
|
||||||
|
- semver
|
||||||
|
- version: 7.8.5
|
||||||
|
- license: ISC
|
||||||
|
- set-function-length
|
||||||
|
- version: 1.2.2
|
||||||
|
- license: MIT
|
||||||
|
- stream-browserify
|
||||||
|
- version: 3.0.0
|
||||||
|
- license: MIT
|
||||||
|
- string_decoder
|
||||||
|
- version: 1.3.0
|
||||||
|
- license: MIT
|
||||||
|
- typescript-event-target
|
||||||
|
- version: 1.1.2
|
||||||
|
- license: MIT
|
||||||
|
- util
|
||||||
|
- version: 0.12.5
|
||||||
|
- license: MIT
|
||||||
|
- util-deprecate
|
||||||
|
- version: 1.0.2
|
||||||
|
- license: MIT
|
||||||
|
- vite
|
||||||
|
- version: 7.3.6
|
||||||
|
- license: MIT
|
||||||
|
- vite-plugin-node-polyfills
|
||||||
|
- version: 0.28.0
|
||||||
|
- license: MIT
|
||||||
|
- which-typed-array
|
||||||
|
- version: 1.1.24
|
||||||
|
- license: MIT
|
||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace OCA\KoreanCommentsAction\AppInfo;
|
||||||
|
|
||||||
|
use OCA\Files\Event\LoadAdditionalScriptsEvent;
|
||||||
|
use OCA\KoreanCommentsAction\Listener\LoadFilesScript;
|
||||||
|
use OCP\AppFramework\App;
|
||||||
|
use OCP\AppFramework\Bootstrap\IBootContext;
|
||||||
|
use OCP\AppFramework\Bootstrap\IBootstrap;
|
||||||
|
use OCP\AppFramework\Bootstrap\IRegistrationContext;
|
||||||
|
|
||||||
|
final class Application extends App implements IBootstrap {
|
||||||
|
public const APP_ID = 'korean_comments_action';
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
parent::__construct(self::APP_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function register(IRegistrationContext $context): void {
|
||||||
|
$context->registerEventListener(
|
||||||
|
LoadAdditionalScriptsEvent::class,
|
||||||
|
LoadFilesScript::class,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function boot(IBootContext $context): void {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace OCA\KoreanCommentsAction\Listener;
|
||||||
|
|
||||||
|
use OCA\Files\Event\LoadAdditionalScriptsEvent;
|
||||||
|
use OCA\KoreanCommentsAction\AppInfo\Application;
|
||||||
|
use OCP\EventDispatcher\Event;
|
||||||
|
use OCP\EventDispatcher\IEventListener;
|
||||||
|
use OCP\Util;
|
||||||
|
|
||||||
|
/** @template-implements IEventListener<LoadAdditionalScriptsEvent> */
|
||||||
|
final class LoadFilesScript implements IEventListener {
|
||||||
|
public function handle(Event $event): void {
|
||||||
|
if (!$event instanceof LoadAdditionalScriptsEvent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Util::addInitScript(Application::APP_ID, 'main');
|
||||||
|
}
|
||||||
|
}
|
||||||
Generated
+6626
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"name": "korean-comments-action",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"build": "vite build",
|
||||||
|
"typecheck": "tsc --noEmit",
|
||||||
|
"test": "vitest run"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@nextcloud/files": "4.0.0",
|
||||||
|
"@nextcloud/initial-state": "3.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@nextcloud/vite-config": "2.5.4",
|
||||||
|
"@types/node": "24.10.4",
|
||||||
|
"typescript": "6.0.3",
|
||||||
|
"vite": "7.3.6",
|
||||||
|
"vitest": "^4.1.11"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^24.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
+69
@@ -0,0 +1,69 @@
|
|||||||
|
import type { IFileAction } from '@nextcloud/files'
|
||||||
|
import { getSidebar, Permission, registerFileAction } from '@nextcloud/files'
|
||||||
|
import { loadState } from '@nextcloud/initial-state'
|
||||||
|
|
||||||
|
const COMMENT_ICON = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M18 8H6V6h12v2m0 3H6V9h12v2m0 3H6v-2h12v2m4-10v18l-4-4H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2Z"/></svg>'
|
||||||
|
|
||||||
|
type ActionContext = Parameters<NonNullable<IFileAction['enabled']>>[0]
|
||||||
|
|
||||||
|
function commentsTab(context: ActionContext): 'activity' | 'comments' | undefined {
|
||||||
|
const [node] = context.nodes
|
||||||
|
if (!node) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
const tabs = getSidebar().getTabs({
|
||||||
|
node,
|
||||||
|
folder: context.folder,
|
||||||
|
view: context.view,
|
||||||
|
})
|
||||||
|
|
||||||
|
const commentsUseActivity = loadState<boolean>('comments', 'activityEnabled', false)
|
||||||
|
if (commentsUseActivity && tabs.some(({ id }) => id === 'activity')) {
|
||||||
|
return 'activity'
|
||||||
|
}
|
||||||
|
if (tabs.some(({ id }) => id === 'comments')) {
|
||||||
|
return 'comments'
|
||||||
|
}
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
export const action: IFileAction = {
|
||||||
|
id: 'korean-comments-action',
|
||||||
|
displayName: () => '댓글',
|
||||||
|
title: () => '댓글',
|
||||||
|
iconSvgInline: () => COMMENT_ICON,
|
||||||
|
enabled(context) {
|
||||||
|
const [node] = context.nodes
|
||||||
|
return context.nodes.length === 1
|
||||||
|
&& node !== undefined
|
||||||
|
&& node.root.startsWith('/files/')
|
||||||
|
&& node.permissions !== Permission.NONE
|
||||||
|
&& getSidebar().available
|
||||||
|
&& commentsTab(context) !== undefined
|
||||||
|
},
|
||||||
|
async exec(context) {
|
||||||
|
const [node] = context.nodes
|
||||||
|
const tabId = commentsTab(context)
|
||||||
|
if (!node || !tabId) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const sidebar = getSidebar()
|
||||||
|
if (sidebar.isOpen && sidebar.node?.source === node.source) {
|
||||||
|
sidebar.setActiveTab(tabId)
|
||||||
|
} else {
|
||||||
|
sidebar.open(node, tabId)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Could not open the comments sidebar', error)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
inline: () => false,
|
||||||
|
order: -45,
|
||||||
|
}
|
||||||
|
|
||||||
|
registerFileAction(action)
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||||
|
|
||||||
|
const mocks = vi.hoisted(() => ({
|
||||||
|
getSidebar: vi.fn(),
|
||||||
|
registerFileAction: vi.fn(),
|
||||||
|
loadState: vi.fn(),
|
||||||
|
open: vi.fn(),
|
||||||
|
setActiveTab: vi.fn(),
|
||||||
|
getTabs: vi.fn(),
|
||||||
|
}))
|
||||||
|
|
||||||
|
vi.mock('@nextcloud/files', () => ({
|
||||||
|
getSidebar: mocks.getSidebar,
|
||||||
|
registerFileAction: mocks.registerFileAction,
|
||||||
|
Permission: { NONE: 0 },
|
||||||
|
}))
|
||||||
|
|
||||||
|
vi.mock('@nextcloud/initial-state', () => ({
|
||||||
|
loadState: mocks.loadState,
|
||||||
|
}))
|
||||||
|
|
||||||
|
import { action } from '../src/main'
|
||||||
|
|
||||||
|
const node = {
|
||||||
|
root: '/files/alice',
|
||||||
|
permissions: 1,
|
||||||
|
source: 'https://cloud.example/remote.php/dav/files/alice/report.pdf',
|
||||||
|
}
|
||||||
|
const context = {
|
||||||
|
nodes: [node],
|
||||||
|
folder: {},
|
||||||
|
view: {},
|
||||||
|
contents: [node],
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
mocks.open.mockReset()
|
||||||
|
mocks.setActiveTab.mockReset()
|
||||||
|
mocks.getTabs.mockReset()
|
||||||
|
mocks.loadState.mockReset()
|
||||||
|
mocks.loadState.mockReturnValue(true)
|
||||||
|
mocks.getTabs.mockReturnValue([{ id: 'activity' }])
|
||||||
|
mocks.getSidebar.mockReturnValue({
|
||||||
|
available: true,
|
||||||
|
isOpen: false,
|
||||||
|
node: undefined,
|
||||||
|
open: mocks.open,
|
||||||
|
setActiveTab: mocks.setActiveTab,
|
||||||
|
getTabs: mocks.getTabs,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Korean comments file action', () => {
|
||||||
|
it('registers a non-inline 댓글 action', () => {
|
||||||
|
expect(mocks.registerFileAction).toHaveBeenCalledWith(action)
|
||||||
|
expect(action.displayName(context as never)).toBe('댓글')
|
||||||
|
expect(action.inline?.(context as never)).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('opens the integrated Activity tab', async () => {
|
||||||
|
expect(action.enabled?.(context as never)).toBe(true)
|
||||||
|
expect(await action.exec(context as never)).toBeNull()
|
||||||
|
expect(mocks.open).toHaveBeenCalledWith(node, 'activity')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('switches an already-open sidebar to Activity', async () => {
|
||||||
|
mocks.getSidebar.mockReturnValue({
|
||||||
|
available: true,
|
||||||
|
isOpen: true,
|
||||||
|
node,
|
||||||
|
open: mocks.open,
|
||||||
|
setActiveTab: mocks.setActiveTab,
|
||||||
|
getTabs: mocks.getTabs,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(await action.exec(context as never)).toBeNull()
|
||||||
|
expect(mocks.setActiveTab).toHaveBeenCalledWith('activity')
|
||||||
|
expect(mocks.open).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('falls back to the standalone Comments tab', async () => {
|
||||||
|
mocks.getTabs.mockReturnValue([{ id: 'comments' }])
|
||||||
|
|
||||||
|
expect(await action.exec(context as never)).toBeNull()
|
||||||
|
expect(mocks.open).toHaveBeenCalledWith(node, 'comments')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('hides when Activity exists without Comments integration', () => {
|
||||||
|
mocks.loadState.mockReturnValue(false)
|
||||||
|
mocks.getTabs.mockReturnValue([{ id: 'activity' }])
|
||||||
|
|
||||||
|
expect(action.enabled?.(context as never)).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "Bundler",
|
||||||
|
"strict": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"types": ["node", "vitest/globals"]
|
||||||
|
},
|
||||||
|
"include": ["src/**/*.ts", "tests/**/*.ts", "vite.config.ts"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { createAppConfig } from '@nextcloud/vite-config'
|
||||||
|
import { resolve } from 'node:path'
|
||||||
|
|
||||||
|
export default createAppConfig(
|
||||||
|
{
|
||||||
|
main: resolve('src/main.ts'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
createEmptyCSSEntryPoints: true,
|
||||||
|
},
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user