refactor(files): Adjust useNavigation composable to enforce active view

In some cases it is guaranteed that we have a proper active view,
e.g. when the file list is loaded (so within FileEntry* components).
This does not change anything but the Typescript types,
so the `currentView` is typed as `View` instead of `View | null´.

Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
This commit is contained in:
Ferdinand Thiessen 2024-11-14 23:24:47 +01:00
parent 67a02fa611
commit c06723c44a
No known key found for this signature in database
GPG key ID: 45FAE7268762B400
6 changed files with 31 additions and 25 deletions

View file

@ -74,7 +74,7 @@
<!-- View columns -->
<td v-for="column in columns"
:key="column.id"
:class="`files-list__row-${currentView?.id}-${column.id}`"
:class="`files-list__row-${currentView.id}-${column.id}`"
class="files-list__row-column-custom"
:data-cy-files-list-row-column-custom="column.id"
@click="openDetailsIfAvailable">
@ -135,7 +135,8 @@ export default defineComponent({
const filesStore = useFilesStore()
const renamingStore = useRenamingStore()
const selectionStore = useSelectionStore()
const { currentView } = useNavigation()
// The file list is guaranteed to be only shown with active view - thus we can set the `loaded` flag
const { currentView } = useNavigation(true)
const {
directory: currentDir,
fileId: currentFileId,
@ -180,7 +181,7 @@ export default defineComponent({
if (this.filesListWidth < 512 || this.compact) {
return []
}
return this.currentView?.columns || []
return this.currentView.columns || []
},
size() {

View file

@ -77,8 +77,8 @@
</template>
<script lang="ts">
import type { PropType, ShallowRef } from 'vue'
import type { FileAction, Node, View } from '@nextcloud/files'
import type { PropType } from 'vue'
import type { FileAction, Node } from '@nextcloud/files'
import { DefaultType, NodeStatus } from '@nextcloud/files'
import { showError, showSuccess } from '@nextcloud/dialogs'
@ -133,12 +133,12 @@ export default defineComponent({
},
setup() {
const { currentView } = useNavigation()
// The file list is guaranteed to be only shown with active view - thus we can set the `loaded` flag
const { currentView } = useNavigation(true)
const enabledFileActions = inject<FileAction[]>('enabledFileActions', [])
return {
// The file list is guaranteed to be only shown with active view
currentView: currentView as ShallowRef<View>,
currentView,
enabledFileActions,
}
},

View file

@ -94,7 +94,8 @@ export default defineComponent({
},
setup() {
const { currentView } = useNavigation()
// The file list is guaranteed to be only shown with active view - thus we can set the `loaded` flag
const { currentView } = useNavigation(true)
const { directory } = useRouteParameters()
const renamingStore = useRenamingStore()
@ -143,7 +144,7 @@ export default defineComponent({
}
}
if (this.defaultFileAction && this.currentView) {
if (this.defaultFileAction) {
const displayName = this.defaultFileAction.displayName([this.source], this.currentView)
return {
is: 'button',
@ -247,8 +248,8 @@ export default defineComponent({
if (status) {
showSuccess(t('files', 'Renamed "{oldName}" to "{newName}"', { oldName, newName }))
this.$nextTick(() => {
const nameContainter = this.$refs.basename as HTMLElement | undefined
nameContainter?.focus()
const nameContainer = this.$refs.basename as HTMLElement | undefined
nameContainer?.focus()
})
} else {
// Was cancelled - meaning the renaming state is just reset

View file

@ -107,7 +107,8 @@ export default defineComponent({
const filesStore = useFilesStore()
const renamingStore = useRenamingStore()
const selectionStore = useSelectionStore()
const { currentView } = useNavigation()
// The file list is guaranteed to be only shown with active view - thus we can set the `loaded` flag
const { currentView } = useNavigation(true)
const {
directory: currentDir,
fileId: currentFileId,

View file

@ -2,6 +2,8 @@
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import type { Navigation, View } from '@nextcloud/files'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { mount } from '@vue/test-utils'
import { defineComponent } from 'vue'
@ -9,8 +11,6 @@ import { defineComponent } from 'vue'
import { useNavigation } from './useNavigation'
import * as nextcloudFiles from '@nextcloud/files'
const { Navigation, View } = nextcloudFiles
// Just a wrapper so we can test the composable
const TestComponent = defineComponent({
template: '<div></div>',
@ -29,7 +29,7 @@ describe('Composables: useNavigation', () => {
describe('currentView', () => {
beforeEach(() => {
navigation = new Navigation()
navigation = new nextcloudFiles.Navigation()
spy.mockImplementation(() => navigation)
})
@ -39,7 +39,7 @@ describe('Composables: useNavigation', () => {
})
it('should return already active navigation', async () => {
const view = new View({ getContents: () => Promise.reject(new Error()), icon: '<svg></svg>', id: 'view-1', name: 'My View 1', order: 0 })
const view = new nextcloudFiles.View({ getContents: () => Promise.reject(new Error()), icon: '<svg></svg>', id: 'view-1', name: 'My View 1', order: 0 })
navigation.register(view)
navigation.setActive(view)
// Now the navigation is already set it should take the active navigation
@ -48,7 +48,7 @@ describe('Composables: useNavigation', () => {
})
it('should be reactive on updating active navigation', async () => {
const view = new View({ getContents: () => Promise.reject(new Error()), icon: '<svg></svg>', id: 'view-1', name: 'My View 1', order: 0 })
const view = new nextcloudFiles.View({ getContents: () => Promise.reject(new Error()), icon: '<svg></svg>', id: 'view-1', name: 'My View 1', order: 0 })
navigation.register(view)
const wrapper = mount(TestComponent)
@ -63,7 +63,7 @@ describe('Composables: useNavigation', () => {
describe('views', () => {
beforeEach(() => {
navigation = new Navigation()
navigation = new nextcloudFiles.Navigation()
spy.mockImplementation(() => navigation)
})
@ -73,7 +73,7 @@ describe('Composables: useNavigation', () => {
})
it('should return already registered views', () => {
const view = new View({ getContents: () => Promise.reject(new Error()), icon: '<svg></svg>', id: 'view-1', name: 'My View 1', order: 0 })
const view = new nextcloudFiles.View({ getContents: () => Promise.reject(new Error()), icon: '<svg></svg>', id: 'view-1', name: 'My View 1', order: 0 })
// register before mount
navigation.register(view)
// now mount and check that the view is listed
@ -82,8 +82,8 @@ describe('Composables: useNavigation', () => {
})
it('should be reactive on registering new views', () => {
const view = new View({ getContents: () => Promise.reject(new Error()), icon: '<svg></svg>', id: 'view-1', name: 'My View 1', order: 0 })
const view2 = new View({ getContents: () => Promise.reject(new Error()), icon: '<svg></svg>', id: 'view-2', name: 'My View 2', order: 1 })
const view = new nextcloudFiles.View({ getContents: () => Promise.reject(new Error()), icon: '<svg></svg>', id: 'view-1', name: 'My View 1', order: 0 })
const view2 = new nextcloudFiles.View({ getContents: () => Promise.reject(new Error()), icon: '<svg></svg>', id: 'view-2', name: 'My View 2', order: 1 })
// register before mount
navigation.register(view)

View file

@ -11,18 +11,21 @@ import { onMounted, onUnmounted, shallowRef, triggerRef } from 'vue'
/**
* Composable to get the currently active files view from the files navigation
* @param _loaded If set enforce a current view is loaded
*/
export function useNavigation() {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export function useNavigation<T extends boolean>(_loaded?: T) {
type MaybeView = T extends true ? View : (View | null);
const navigation = getNavigation()
const views: ShallowRef<View[]> = shallowRef(navigation.views)
const currentView: ShallowRef<View | null> = shallowRef(navigation.active)
const currentView: ShallowRef<MaybeView> = shallowRef(navigation.active as MaybeView)
/**
* Event listener to update the `currentView`
* @param event The update event
*/
function onUpdateActive(event: CustomEvent<View|null>) {
currentView.value = event.detail
currentView.value = event.detail as MaybeView
}
/**