diff options
author | skjnldsv <skjnldsv@protonmail.com> | 2024-12-13 11:51:36 +0100 |
---|---|---|
committer | skjnldsv <skjnldsv@protonmail.com> | 2024-12-13 11:51:36 +0100 |
commit | 8e02db6a162a18c51b20fc76ac8a40f7411eb7ff (patch) | |
tree | ff1e79f92bd88717af7afc213fc1160daa1ab4fa | |
parent | 0862632b2c8f11cd5d3742a7061e2797ec59f230 (diff) | |
download | nextcloud-server-feat/files-shortcuts-2.tar.gz nextcloud-server-feat/files-shortcuts-2.zip |
chore(files): add hotkey service testsfeat/files-shortcuts-2
Signed-off-by: skjnldsv <skjnldsv@protonmail.com>
-rw-r--r-- | apps/files/src/actions/sidebarAction.ts | 2 | ||||
-rw-r--r-- | apps/files/src/components/FilesListTableHeader.vue | 3 | ||||
-rw-r--r-- | apps/files/src/main.ts | 6 | ||||
-rw-r--r-- | apps/files/src/services/HotKeysService.spec.ts | 161 | ||||
-rw-r--r-- | apps/files/src/services/HotKeysService.ts | 45 | ||||
-rw-r--r-- | apps/files/src/services/Recent.ts | 4 | ||||
-rw-r--r-- | apps/files/src/store/active.ts | 13 | ||||
-rw-r--r-- | apps/files/src/store/index.ts | 9 | ||||
-rw-r--r-- | apps/files/src/store/userconfig.ts | 4 | ||||
-rw-r--r-- | apps/files/src/utils/actionUtils.ts | 6 | ||||
-rw-r--r-- | apps/files/src/views/FilesList.vue | 25 | ||||
-rw-r--r-- | apps/files/src/views/Settings.vue | 10 | ||||
-rw-r--r-- | apps/systemtags/src/components/SystemTagPicker.vue | 2 | ||||
-rw-r--r-- | apps/systemtags/src/files_actions/inlineSystemTagsAction.ts | 2 | ||||
-rw-r--r-- | apps/systemtags/src/init.ts | 8 | ||||
-rw-r--r-- | apps/systemtags/src/logger.ts | 2 | ||||
-rw-r--r-- | apps/systemtags/src/services/HotKeysService.ts | 24 | ||||
-rw-r--r-- | apps/systemtags/src/services/api.ts | 2 | ||||
-rw-r--r-- | apps/systemtags/src/services/files.ts | 5 | ||||
-rw-r--r-- | apps/systemtags/src/services/logger.ts | 10 |
20 files changed, 278 insertions, 65 deletions
diff --git a/apps/files/src/actions/sidebarAction.ts b/apps/files/src/actions/sidebarAction.ts index 37305578383..0b8ad91741e 100644 --- a/apps/files/src/actions/sidebarAction.ts +++ b/apps/files/src/actions/sidebarAction.ts @@ -56,7 +56,7 @@ export const action = new FileAction({ await window.OCA.Files.Sidebar.open(node.path) // Silently update current fileid - window.OCP.Files.Router.goToRoute( + window.OCP?.Files?.Router?.goToRoute( null, { view: view.id, fileid: String(node.fileid) }, { ...window.OCP.Files.Router.query, dir, opendetails: 'true' }, diff --git a/apps/files/src/components/FilesListTableHeader.vue b/apps/files/src/components/FilesListTableHeader.vue index 783b0813b49..b7818a9a40f 100644 --- a/apps/files/src/components/FilesListTableHeader.vue +++ b/apps/files/src/components/FilesListTableHeader.vue @@ -200,6 +200,9 @@ export default defineComponent({ }, resetSelection() { + if (this.isNoneSelected) { + return + } this.selectionStore.reset() }, diff --git a/apps/files/src/main.ts b/apps/files/src/main.ts index 26fa7cdca95..b4755df8eed 100644 --- a/apps/files/src/main.ts +++ b/apps/files/src/main.ts @@ -2,12 +2,13 @@ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors * SPDX-License-Identifier: AGPL-3.0-or-later */ +import type { Pinia } from 'pinia' import { getCSPNonce } from '@nextcloud/auth' import { getNavigation } from '@nextcloud/files' import { PiniaVuePlugin } from 'pinia' import Vue from 'vue' -import { pinia } from './store/index.ts' +import { getPinia } from './store/index.ts' import { registerHotkeys } from './services/HotKeysService.ts' import FilesApp from './FilesApp.vue' import router from './router/router' @@ -23,6 +24,7 @@ declare global { OCP: Nextcloud.v29.OCP // eslint-disable-next-line @typescript-eslint/no-explicit-any OCA: Record<string, any> + _nc_files_pinia: Pinia } } @@ -55,5 +57,5 @@ Object.assign(window.OCA.Files.Settings, { Setting: SettingsModel }) const FilesAppVue = Vue.extend(FilesApp) new FilesAppVue({ router: (window.OCP.Files.Router as RouterService)._router, - pinia, + pinia: getPinia(), }).$mount('#content') diff --git a/apps/files/src/services/HotKeysService.spec.ts b/apps/files/src/services/HotKeysService.spec.ts new file mode 100644 index 00000000000..dfe9f66601b --- /dev/null +++ b/apps/files/src/services/HotKeysService.spec.ts @@ -0,0 +1,161 @@ +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +import { describe, it, vi, expect, beforeEach, beforeAll } from 'vitest' +import { File, Permission, View } from '@nextcloud/files' +import axios from '@nextcloud/axios' + +import { getPinia } from '../store/index.ts' +import { useActiveStore } from '../store/active.ts' + +import { action as deleteAction } from '../actions/deleteAction.ts' +import { action as favoriteAction } from '../actions/favoriteAction.ts' +import { action as renameAction } from '../actions/renameAction.ts' +import { action as sidebarAction } from '../actions/sidebarAction.ts' +import { registerHotkeys } from './HotKeysService.ts' +import { useUserConfigStore } from '../store/userconfig.ts' +import { subscribe } from '@nextcloud/event-bus' + +let file: File +const view = { + id: 'files', + name: 'Files', +} as View + +vi.mock('../actions/sidebarAction.ts', { spy: true }) +vi.mock('../actions/deleteAction.ts', { spy: true }) +vi.mock('../actions/favoriteAction.ts', { spy: true }) +vi.mock('../actions/renameAction.ts', { spy: true }) + +describe('HotKeysService testing', () => { + const activeStore = useActiveStore(getPinia()) + + const goToRouteMock = vi.fn() + + beforeAll(() => { + registerHotkeys() + }) + + beforeEach(() => { + // Make sure the router is reset before each test + goToRouteMock.mockClear() + + // Make sure the file is reset before each test + file = new File({ + id: 1, + source: 'https://cloud.domain.com/remote.php/dav/files/admin/foobar.txt', + owner: 'admin', + mime: 'text/plain', + permissions: Permission.ALL, + }) + + // Setting the view first as it reset the active node + activeStore.onChangedView(view) + activeStore.setActiveNode(file) + + window.OCA = { Files: { Sidebar: { open: () => {}, setActiveTab: () => {} } } } + // @ts-expect-error We only mock what needed, we do not need Files.Router.goTo or Files.Navigation + window.OCP = { Files: { Router: { goToRoute: goToRouteMock, params: {}, query: {} } } } + }) + + it('Pressing d should open the sidebar once', () => { + window.dispatchEvent(new KeyboardEvent('keydown', { key: 'd', code: 'KeyD' })) + + // Modifier keys should not trigger the action + window.dispatchEvent(new KeyboardEvent('keydown', { key: 'd', code: 'KeyD', ctrlKey: true })) + window.dispatchEvent(new KeyboardEvent('keydown', { key: 'd', code: 'KeyD', altKey: true })) + window.dispatchEvent(new KeyboardEvent('keydown', { key: 'd', code: 'KeyD', shiftKey: true })) + window.dispatchEvent(new KeyboardEvent('keydown', { key: 'd', code: 'KeyD', metaKey: true })) + + expect(sidebarAction.enabled).toHaveReturnedWith(true) + expect(sidebarAction.exec).toHaveBeenCalledOnce() + }) + + it('Pressing F2 should rename the file', () => { + window.dispatchEvent(new KeyboardEvent('keydown', { key: 'F2', code: 'F2' })) + + // Modifier keys should not trigger the action + window.dispatchEvent(new KeyboardEvent('keydown', { key: 'F2', code: 'F2', ctrlKey: true })) + window.dispatchEvent(new KeyboardEvent('keydown', { key: 'F2', code: 'F2', altKey: true })) + window.dispatchEvent(new KeyboardEvent('keydown', { key: 'F2', code: 'F2', shiftKey: true })) + window.dispatchEvent(new KeyboardEvent('keydown', { key: 'F2', code: 'F2', metaKey: true })) + + expect(renameAction.enabled).toHaveReturnedWith(true) + expect(renameAction.exec).toHaveBeenCalledOnce() + }) + + it('Pressing s should toggle favorite', () => { + vi.spyOn(axios, 'post').mockImplementationOnce(() => Promise.resolve()) + window.dispatchEvent(new KeyboardEvent('keydown', { key: 's', code: 'KeyS' })) + + // Modifier keys should not trigger the action + window.dispatchEvent(new KeyboardEvent('keydown', { key: 's', code: 'KeyS', ctrlKey: true })) + window.dispatchEvent(new KeyboardEvent('keydown', { key: 's', code: 'KeyS', altKey: true })) + window.dispatchEvent(new KeyboardEvent('keydown', { key: 's', code: 'KeyS', shiftKey: true })) + window.dispatchEvent(new KeyboardEvent('keydown', { key: 's', code: 'KeyS', metaKey: true })) + + expect(favoriteAction.enabled).toHaveReturnedWith(true) + expect(favoriteAction.exec).toHaveBeenCalledOnce() + }) + + it('Pressing Delete should delete the file', async () => { + vi.spyOn(deleteAction._action, 'exec').mockResolvedValue(() => true) + + window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Delete', code: 'Delete' })) + + // Modifier keys should not trigger the action + window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Delete', code: 'Delete', ctrlKey: true })) + window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Delete', code: 'Delete', altKey: true })) + window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Delete', code: 'Delete', shiftKey: true })) + window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Delete', code: 'Delete', metaKey: true })) + + expect(deleteAction.enabled).toHaveReturnedWith(true) + expect(deleteAction.exec).toHaveBeenCalledOnce() + }) + + it('Pressing alt+up should go to parent directory', () => { + expect(goToRouteMock).toHaveBeenCalledTimes(0) + window.OCP.Files.Router.query = { dir: '/foo/bar' } + + window.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp', code: 'ArrowUp', altKey: true })) + + expect(goToRouteMock).toHaveBeenCalledOnce() + expect(goToRouteMock.mock.calls[0][2].dir).toBe('/foo') + }) + + it('Pressing v should toggle grid view', async () => { + vi.spyOn(axios, 'put').mockImplementationOnce(() => Promise.resolve()) + + const userConfigStore = useUserConfigStore(getPinia()) + const currentGridConfig = userConfigStore.userConfig.grid_view + + // Wait for the user config to be updated + // or timeout after 500ms + const waitForUserConfig = () => new Promise((resolve) => { + subscribe('files:config:updated', resolve) + setTimeout(resolve, 500) + }) + + window.dispatchEvent(new KeyboardEvent('keydown', { key: 'v', code: 'KeyV' })) + await waitForUserConfig() + expect(userConfigStore.userConfig.grid_view).toBe(!currentGridConfig) + + // Modifier keys should not trigger the action + window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Delete', code: 'Delete', ctrlKey: true })) + await waitForUserConfig() + expect(userConfigStore.userConfig.grid_view).toBe(!currentGridConfig) + + window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Delete', code: 'Delete', altKey: true })) + await waitForUserConfig() + expect(userConfigStore.userConfig.grid_view).toBe(!currentGridConfig) + + window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Delete', code: 'Delete', shiftKey: true })) + await waitForUserConfig() + expect(userConfigStore.userConfig.grid_view).toBe(!currentGridConfig) + + window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Delete', code: 'Delete', metaKey: true })) + await waitForUserConfig() + expect(userConfigStore.userConfig.grid_view).toBe(!currentGridConfig) + }) +}) diff --git a/apps/files/src/services/HotKeysService.ts b/apps/files/src/services/HotKeysService.ts index 381fc582441..8318554ea99 100644 --- a/apps/files/src/services/HotKeysService.ts +++ b/apps/files/src/services/HotKeysService.ts @@ -2,16 +2,22 @@ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors * SPDX-License-Identifier: AGPL-3.0-or-later */ - +import { dirname } from 'path' import { useHotKey } from '@nextcloud/vue/dist/Composables/useHotKey.js' import { action as deleteAction } from '../actions/deleteAction.ts' -import { action as renameAction } from '../actions/renameAction.ts' import { action as favoriteAction } from '../actions/favoriteAction.ts' +import { action as renameAction } from '../actions/renameAction.ts' import { action as sidebarAction } from '../actions/sidebarAction.ts' import { executeAction } from '../utils/actionUtils.ts' +import { useUserConfigStore } from '../store/userconfig.ts' import logger from '../logger.ts' +/** + * This register the hotkeys for the Files app. + * As much as possible, we try to have all the hotkeys in one place. + * Please make sure to add tests for the hotkeys after adding a new one. + */ export const registerHotkeys = function() { // d opens the sidebar useHotKey('d', () => executeAction(sidebarAction), { @@ -37,5 +43,40 @@ export const registerHotkeys = function() { prevent: true, }) + // alt+up go to parent directory + useHotKey('ArrowUp', goToParentDir, { + stop: true, + prevent: true, + alt: true, + }) + + // v toggle grid view + useHotKey('v', toggleGridView, { + stop: true, + prevent: true, + }) + logger.debug('Hotkeys registered') } + +const goToParentDir = function() { + const params = window.OCP.Files.Router?.params || {} + const query = window.OCP.Files.Router?.query || {} + + const currentDir = (query?.dir || '/') as string + const parentDir = dirname(currentDir) + + logger.debug('Navigating to parent directory', { parentDir }) + window.OCP.Files.Router.goToRoute( + null, + { ...params }, + { ...query, dir: parentDir }, + ) +} + +const toggleGridView = function() { + const userConfigStore = useUserConfigStore() + const value = userConfigStore?.userConfig?.grid_view + logger.debug('Toggling grid view', { old: value, new: !value }) + userConfigStore.update('grid_view', !value) +} diff --git a/apps/files/src/services/Recent.ts b/apps/files/src/services/Recent.ts index 0a953087781..d0ca285b05c 100644 --- a/apps/files/src/services/Recent.ts +++ b/apps/files/src/services/Recent.ts @@ -9,7 +9,7 @@ import { getCurrentUser } from '@nextcloud/auth' import { Folder, Permission, davGetRecentSearch, davRootPath, davRemoteURL, davResultToNode } from '@nextcloud/files' import { CancelablePromise } from 'cancelable-promise' import { useUserConfigStore } from '../store/userconfig.ts' -import { pinia } from '../store/index.ts' +import { getPinia } from '../store/index.ts' import { client } from './WebdavClient.ts' import { getBaseUrl } from '@nextcloud/router' @@ -32,7 +32,7 @@ const resultToNode = (stat: FileStat) => davResultToNode(stat, davRootPath, getB * @param path Path to search for recent changes */ export const getContents = (path = '/'): CancelablePromise<ContentsWithRoot> => { - const store = useUserConfigStore(pinia) + const store = useUserConfigStore(getPinia()) /** * Filter function that returns only the visible nodes - or hidden if explicitly configured diff --git a/apps/files/src/store/active.ts b/apps/files/src/store/active.ts index 84715e090f3..2efb823b232 100644 --- a/apps/files/src/store/active.ts +++ b/apps/files/src/store/active.ts @@ -11,6 +11,7 @@ import { getNavigation } from '@nextcloud/files' import { subscribe } from '@nextcloud/event-bus' import logger from '../logger.ts' +import type { set } from 'lodash' export const useActiveStore = function(...args) { const store = defineStore('active', { @@ -34,6 +35,12 @@ export const useActiveStore = function(...args) { this.activeNode = null }, + onDeletedNode(node: Node) { + if (this.activeNode && this.activeNode.source === node.source) { + this.clearActiveNode() + } + }, + setActiveAction(action: FileAction) { this.activeAction = action }, @@ -42,12 +49,6 @@ export const useActiveStore = function(...args) { this.activeAction = null }, - onDeletedNode(node: Node) { - if (this.activeNode && this.activeNode.source === node.source) { - this.clearActiveNode() - } - }, - onChangedView(view: View|null = null) { logger.debug('Setting active view', { view }) this.activeView = view diff --git a/apps/files/src/store/index.ts b/apps/files/src/store/index.ts index 00676b3bc8e..3ba667ffd2f 100644 --- a/apps/files/src/store/index.ts +++ b/apps/files/src/store/index.ts @@ -5,4 +5,11 @@ import { createPinia } from 'pinia' -export const pinia = createPinia() +export const getPinia = () => { + if (window._nc_files_pinia) { + return window._nc_files_pinia + } + + window._nc_files_pinia = createPinia() + return window._nc_files_pinia +} diff --git a/apps/files/src/store/userconfig.ts b/apps/files/src/store/userconfig.ts index ffe07a91bab..6fd2ad886bc 100644 --- a/apps/files/src/store/userconfig.ts +++ b/apps/files/src/store/userconfig.ts @@ -27,8 +27,6 @@ export const useUserConfigStore = function(...args) { actions: { /** * Update the user config local store - * @param key - * @param value */ onUpdate(key: string, value: boolean) { Vue.set(this.userConfig, key, value) @@ -36,8 +34,6 @@ export const useUserConfigStore = function(...args) { /** * Update the user config local store AND on server side - * @param key - * @param value */ async update(key: string, value: boolean) { await axios.put(generateUrl('/apps/files/api/v1/config/' + key), { diff --git a/apps/files/src/utils/actionUtils.ts b/apps/files/src/utils/actionUtils.ts index 87b8b6f44d3..730a1149229 100644 --- a/apps/files/src/utils/actionUtils.ts +++ b/apps/files/src/utils/actionUtils.ts @@ -9,7 +9,7 @@ import { showError, showSuccess } from '@nextcloud/dialogs' import { t } from '@nextcloud/l10n' import Vue from 'vue' -import { pinia } from '../store' +import { getPinia } from '../store' import { useActiveStore } from '../store/active' import logger from '../logger' @@ -19,8 +19,8 @@ import logger from '../logger' * @param action The action to execute */ export const executeAction = async (action: FileAction) => { - const activeStore = useActiveStore(pinia) - const currentDir = (window.OCP.Files.Router?.query?.dir || '/') as string + const activeStore = useActiveStore(getPinia()) + const currentDir = (window?.OCP?.Files?.Router?.query?.dir || '/') as string const currentNode = activeStore.activeNode const currentView = activeStore.activeView diff --git a/apps/files/src/views/FilesList.vue b/apps/files/src/views/FilesList.vue index 6ce385776f2..0f301abe024 100644 --- a/apps/files/src/views/FilesList.vue +++ b/apps/files/src/views/FilesList.vue @@ -514,21 +514,6 @@ export default defineComponent({ }, }, - created() { - // v toggle grid view - useHotKey('v', this.toggleGridView, { - stop: true, - prevent: true, - }) - - // alt+up go to parent directory - useHotKey('ArrowUp', this.goToPreviousDir, { - stop: true, - prevent: true, - alt: true, - }) - }, - mounted() { this.filtersStore.init() this.fetchContent() @@ -726,16 +711,6 @@ export default defineComponent({ } this.dirContentsFiltered = nodes }, - - goToPreviousDir() { - const prevDir = dirname(this.directory) - logger.debug('Navigating to parent directory', { prevDir }) - window.OCP.Files.Router.goToRoute( - null, - { ...this.$route.params }, - { ...this.$route.query, dir: prevDir }, - ) - }, }, }) </script> diff --git a/apps/files/src/views/Settings.vue b/apps/files/src/views/Settings.vue index ba1086e700f..e4d786a1e84 100644 --- a/apps/files/src/views/Settings.vue +++ b/apps/files/src/views/Settings.vue @@ -90,7 +90,7 @@ <h3>{{ t('files', 'Actions') }}</h3> <dl> <div> - <dt><kbd>A</kbd></dt> + <dt><kbd>a</kbd></dt> <dd class="shortcut-description"> {{ t('files', 'Open the actions menu for a file') }} </dd> @@ -108,11 +108,17 @@ </dd> </div> <div> - <dt><kbd>S</kbd></dt> + <dt><kbd>s</kbd></dt> <dd class="shortcut-description"> {{ t('files', 'Favorite or remove a file from favorites') }} </dd> </div> + <div> + <dt><kbd>t</kbd></dt> + <dd class="shortcut-description"> + {{ t('files', 'Manage tags for a file') }} + </dd> + </div> </dl> <h3>{{ t('files', 'Selection') }}</h3> diff --git a/apps/systemtags/src/components/SystemTagPicker.vue b/apps/systemtags/src/components/SystemTagPicker.vue index 652ea262409..e17fb1a7bc5 100644 --- a/apps/systemtags/src/components/SystemTagPicker.vue +++ b/apps/systemtags/src/components/SystemTagPicker.vue @@ -151,7 +151,7 @@ import TagIcon from 'vue-material-design-icons/Tag.vue' import { createTag, fetchTag, fetchTags, getTagObjects, setTagObjects, updateTag } from '../services/api' import { getNodeSystemTags, setNodeSystemTags } from '../utils' import { elementColor, invertTextColor, isDarkModeEnabled } from '../utils/colorUtils' -import logger from '../services/logger' +import logger from '../logger.ts' const debounceUpdateTag = debounce(updateTag, 500) const mainBackgroundColor = getComputedStyle(document.body) diff --git a/apps/systemtags/src/files_actions/inlineSystemTagsAction.ts b/apps/systemtags/src/files_actions/inlineSystemTagsAction.ts index cc6cb55c632..cab2f61906b 100644 --- a/apps/systemtags/src/files_actions/inlineSystemTagsAction.ts +++ b/apps/systemtags/src/files_actions/inlineSystemTagsAction.ts @@ -12,7 +12,7 @@ import '../css/fileEntryInlineSystemTags.scss' import { elementColor, isDarkModeEnabled } from '../utils/colorUtils' import { fetchTags } from '../services/api' import { getNodeSystemTags } from '../utils' -import logger from '../services/logger' +import logger from '../logger.ts' // Init tag cache const cache: TagWithId[] = [] diff --git a/apps/systemtags/src/init.ts b/apps/systemtags/src/init.ts index 54ba0d604ee..fdf645e6044 100644 --- a/apps/systemtags/src/init.ts +++ b/apps/systemtags/src/init.ts @@ -3,10 +3,12 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ import { registerDavProperty, registerFileAction } from '@nextcloud/files' +import { registerHotkeys } from './services/HotKeysService' +import { registerSystemTagsView } from './files_views/systemtagsView' + import { action as bulkSystemTagsAction } from './files_actions/bulkSystemTagsAction' import { action as inlineSystemTagsAction } from './files_actions/inlineSystemTagsAction' import { action as openInFilesAction } from './files_actions/openInFilesAction' -import { registerSystemTagsView } from './files_views/systemtagsView' registerDavProperty('nc:system-tags') registerFileAction(bulkSystemTagsAction) @@ -14,3 +16,7 @@ registerFileAction(inlineSystemTagsAction) registerFileAction(openInFilesAction) registerSystemTagsView() + +document.addEventListener('DOMContentLoaded', () => { + registerHotkeys() +}) diff --git a/apps/systemtags/src/logger.ts b/apps/systemtags/src/logger.ts index fa02888bdb8..5a37e3874ed 100644 --- a/apps/systemtags/src/logger.ts +++ b/apps/systemtags/src/logger.ts @@ -5,7 +5,7 @@ import { getLoggerBuilder } from '@nextcloud/logger' -export const logger = getLoggerBuilder() +export default getLoggerBuilder() .setApp('systemtags') .detectUser() .build() diff --git a/apps/systemtags/src/services/HotKeysService.ts b/apps/systemtags/src/services/HotKeysService.ts new file mode 100644 index 00000000000..3661531fcb5 --- /dev/null +++ b/apps/systemtags/src/services/HotKeysService.ts @@ -0,0 +1,24 @@ +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +import { useHotKey } from '@nextcloud/vue/dist/Composables/useHotKey.js' + +import { action as manageTagAction } from '../files_actions/bulkSystemTagsAction.ts' +import { executeAction } from '../../../files/src/utils/actionUtils.ts' +import logger from '../logger.ts' + +/** + * This register the hotkeys for the Files app. + * As much as possible, we try to have all the hotkeys in one place. + * Please make sure to add tests for the hotkeys after adding a new one. + */ +export const registerHotkeys = function() { + // t opens the tag management dialog + useHotKey('t', () => executeAction(manageTagAction), { + stop: true, + prevent: true, + }) + + logger.debug('Hotkeys registered') +} diff --git a/apps/systemtags/src/services/api.ts b/apps/systemtags/src/services/api.ts index c0b39fde6bd..ef44fa82dae 100644 --- a/apps/systemtags/src/services/api.ts +++ b/apps/systemtags/src/services/api.ts @@ -12,7 +12,7 @@ import { t } from '@nextcloud/l10n' import { davClient } from './davClient.js' import { formatTag, parseIdFromLocation, parseTags } from '../utils' -import { logger } from '../logger.js' +import logger from '../logger.ts' import { emit } from '@nextcloud/event-bus' export const fetchTagsPayload = `<?xml version="1.0"?> diff --git a/apps/systemtags/src/services/files.ts b/apps/systemtags/src/services/files.ts index 8759a99d560..d61b7ff4825 100644 --- a/apps/systemtags/src/services/files.ts +++ b/apps/systemtags/src/services/files.ts @@ -7,10 +7,11 @@ import type { FileStat, ResponseDataDetailed } from 'webdav' import type { ServerTagWithId, Tag, TagWithId } from '../types.js' import { t } from '@nextcloud/l10n' -import { davClient } from './davClient.js' + import { createTag, fetchTagsPayload } from './api.js' +import { davClient } from './davClient.js' import { formatTag, parseTags } from '../utils.js' -import { logger } from '../logger.js' +import logger from '../logger.ts' export const fetchTagsForFile = async (fileId: number): Promise<TagWithId[]> => { const path = '/systemtags-relations/files/' + fileId diff --git a/apps/systemtags/src/services/logger.ts b/apps/systemtags/src/services/logger.ts deleted file mode 100644 index 8cce9f5f92e..00000000000 --- a/apps/systemtags/src/services/logger.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors - * SPDX-License-Identifier: AGPL-3.0-or-later - */ -import { getLoggerBuilder } from '@nextcloud/logger' - -export default getLoggerBuilder() - .setApp('systemtags') - .detectUser() - .build() |