mirror of
https://github.com/nextcloud/server.git
synced 2026-04-23 15:21:00 -04:00
refactor(files): move hotkey handling to composable
This is a composable - not a service, because it is using the `useHotKey` composable. At this moment it works, but in general its only safe to put composables into `setup`-context. This makes it future prove. Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
This commit is contained in:
parent
db8dd9f7f6
commit
3a5769e8f9
4 changed files with 82 additions and 53 deletions
|
|
@ -12,11 +12,10 @@
|
|||
<script lang="ts">
|
||||
import { isPublicShare } from '@nextcloud/sharing/public'
|
||||
import { defineComponent } from 'vue'
|
||||
|
||||
import NcContent from '@nextcloud/vue/components/NcContent'
|
||||
|
||||
import Navigation from './views/Navigation.vue'
|
||||
import FilesList from './views/FilesList.vue'
|
||||
import { useHotKeys } from './composables/useHotKeys'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'FilesApp',
|
||||
|
|
@ -28,6 +27,9 @@ export default defineComponent({
|
|||
},
|
||||
|
||||
setup() {
|
||||
// Register global hotkeys
|
||||
useHotKeys()
|
||||
|
||||
const isPublic = isPublicShare()
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,14 @@
|
|||
/**
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import type { Location } from 'vue-router'
|
||||
|
||||
import { File, Folder, Permission, View } from '@nextcloud/files'
|
||||
import { describe, it, vi, expect, beforeEach, beforeAll, afterEach } from 'vitest'
|
||||
import { nextTick } from 'vue'
|
||||
import { enableAutoDestroy, mount } from '@vue/test-utils'
|
||||
import { describe, it, vi, expect, beforeEach, afterEach } from 'vitest'
|
||||
import { defineComponent, nextTick } from 'vue'
|
||||
import axios from '@nextcloud/axios'
|
||||
|
||||
import { getPinia } from '../store/index.ts'
|
||||
|
|
@ -15,38 +19,64 @@ 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 { useHotKeys } from './useHotKeys.ts'
|
||||
import { useUserConfigStore } from '../store/userconfig.ts'
|
||||
|
||||
// this is the mocked current route
|
||||
const route = vi.hoisted(() => ({
|
||||
name: 'test',
|
||||
params: {
|
||||
fileId: 123,
|
||||
},
|
||||
query: {
|
||||
openFile: 'false',
|
||||
dir: '/parent/dir',
|
||||
},
|
||||
}))
|
||||
|
||||
// mocked router
|
||||
const router = vi.hoisted(() => ({
|
||||
push: vi.fn<(route: Location) => void>(),
|
||||
}))
|
||||
|
||||
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 })
|
||||
|
||||
vi.mock('vue-router/composables', () => ({
|
||||
useRoute: vi.fn(() => route),
|
||||
useRouter: vi.fn(() => router),
|
||||
}))
|
||||
|
||||
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 })
|
||||
const TestComponent = defineComponent({
|
||||
name: 'test',
|
||||
setup() {
|
||||
useHotKeys()
|
||||
},
|
||||
template: '<div />',
|
||||
})
|
||||
|
||||
describe('HotKeysService testing', () => {
|
||||
const activeStore = useActiveStore(getPinia())
|
||||
|
||||
const goToRouteMock = vi.fn()
|
||||
|
||||
let initialState: HTMLInputElement
|
||||
|
||||
enableAutoDestroy(afterEach)
|
||||
|
||||
afterEach(() => {
|
||||
document.body.removeChild(initialState)
|
||||
})
|
||||
|
||||
beforeAll(() => {
|
||||
registerHotkeys()
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
// Make sure the router is reset before each test
|
||||
goToRouteMock.mockClear()
|
||||
router.push.mockClear()
|
||||
|
||||
// Make sure the file is reset before each test
|
||||
file = new File({
|
||||
|
|
@ -66,9 +96,6 @@ describe('HotKeysService testing', () => {
|
|||
activeStore.activeNode = file
|
||||
|
||||
window.OCA = { Files: { Sidebar: { open: () => {}, setActiveTab: () => {} } } }
|
||||
// We only mock what needed, we do not need Files.Router.goTo or Files.Navigation
|
||||
window.OCP = { Files: { Router: { goToRoute: goToRouteMock, params: {}, query: {} } } }
|
||||
|
||||
initialState = document.createElement('input')
|
||||
initialState.setAttribute('type', 'hidden')
|
||||
initialState.setAttribute('id', 'initial-state-files_trashbin-config')
|
||||
|
|
@ -76,6 +103,8 @@ describe('HotKeysService testing', () => {
|
|||
allow_delete: true,
|
||||
})))
|
||||
document.body.appendChild(initialState)
|
||||
|
||||
mount(TestComponent)
|
||||
})
|
||||
|
||||
it('Pressing d should open the sidebar once', () => {
|
||||
|
|
@ -135,13 +164,11 @@ describe('HotKeysService testing', () => {
|
|||
})
|
||||
|
||||
it('Pressing alt+up should go to parent directory', () => {
|
||||
expect(goToRouteMock).toHaveBeenCalledTimes(0)
|
||||
window.OCP.Files.Router.query = { dir: '/foo/bar' }
|
||||
|
||||
expect(router.push).toHaveBeenCalledTimes(0)
|
||||
dispatchEvent({ key: 'ArrowUp', code: 'ArrowUp', altKey: true })
|
||||
|
||||
expect(goToRouteMock).toHaveBeenCalledOnce()
|
||||
expect(goToRouteMock.mock.calls[0][2].dir).toBe('/foo')
|
||||
expect(router.push).toHaveBeenCalledOnce()
|
||||
expect(router.push.mock.calls[0][0].query?.dir).toBe('/parent')
|
||||
})
|
||||
|
||||
it('Pressing v should toggle grid view', async () => {
|
||||
|
|
@ -4,13 +4,15 @@
|
|||
*/
|
||||
import { useHotKey } from '@nextcloud/vue/composables/useHotKey'
|
||||
import { dirname } from 'path'
|
||||
import { useRoute, useRouter } from 'vue-router/composables'
|
||||
|
||||
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 { executeAction } from '../utils/actionUtils.ts'
|
||||
import { useUserConfigStore } from '../store/userconfig.ts'
|
||||
import { useRouteParameters } from './useRouteParameters.ts'
|
||||
import { executeAction } from '../utils/actionUtils.ts'
|
||||
import logger from '../logger.ts'
|
||||
|
||||
/**
|
||||
|
|
@ -18,7 +20,12 @@ import logger from '../logger.ts'
|
|||
* 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() {
|
||||
export function useHotKeys(): void {
|
||||
const userConfigStore = useUserConfigStore()
|
||||
const { directory } = useRouteParameters()
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
|
||||
// d opens the sidebar
|
||||
useHotKey('d', () => executeAction(sidebarAction), {
|
||||
stop: true,
|
||||
|
|
@ -57,26 +64,23 @@ export const registerHotkeys = function() {
|
|||
})
|
||||
|
||||
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)
|
||||
|
||||
/**
|
||||
* Use the router to go to the parent directory
|
||||
*/
|
||||
function goToParentDir() {
|
||||
const dir = dirname(directory.value)
|
||||
|
||||
logger.debug('Navigating to parent directory', { dir })
|
||||
router.push({ params: { ...route.params }, query: { ...route.query, dir } })
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle the grid view
|
||||
*/
|
||||
function toggleGridView() {
|
||||
const value = userConfigStore.userConfig.grid_view
|
||||
logger.debug('Toggling grid view', { old: value, new: !value })
|
||||
userConfigStore.update('grid_view', !value)
|
||||
}
|
||||
}
|
||||
|
|
@ -8,7 +8,6 @@ import { PiniaVuePlugin } from 'pinia'
|
|||
import Vue from 'vue'
|
||||
|
||||
import { getPinia } from './store/index.ts'
|
||||
import { registerHotkeys } from './services/HotKeysService.ts'
|
||||
import FilesApp from './FilesApp.vue'
|
||||
import router from './router/router'
|
||||
import RouterService from './services/RouterService'
|
||||
|
|
@ -40,9 +39,6 @@ if (!window.OCP.Files.Router) {
|
|||
// Init Pinia store
|
||||
Vue.use(PiniaVuePlugin)
|
||||
|
||||
// Init HotKeys AFTER pinia is set up
|
||||
registerHotkeys()
|
||||
|
||||
// Init Files App Settings Service
|
||||
const Settings = new SettingsService()
|
||||
Object.assign(window.OCA.Files, { Settings })
|
||||
|
|
|
|||
Loading…
Reference in a new issue