diff --git a/apps/files/src/components/BreadCrumbs.vue b/apps/files/src/components/BreadCrumbs.vue
index 823c9554d8c..df7931a4a5b 100644
--- a/apps/files/src/components/BreadCrumbs.vue
+++ b/apps/files/src/components/BreadCrumbs.vue
@@ -52,6 +52,7 @@
diff --git a/apps/files/src/views/Navigation.cy.ts b/apps/files/src/views/Navigation.cy.ts
index 07d9eee80cb..fe7800f32c9 100644
--- a/apps/files/src/views/Navigation.cy.ts
+++ b/apps/files/src/views/Navigation.cy.ts
@@ -1,19 +1,40 @@
-import FolderSvg from '@mdi/svg/svg/folder.svg'
-import ShareSvg from '@mdi/svg/svg/share-variant.svg'
+/**
+ * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+import type { Navigation } from '@nextcloud/files'
+
import { createTestingPinia } from '@pinia/testing'
+import FolderSvg from '@mdi/svg/svg/folder.svg?raw'
import NavigationView from './Navigation.vue'
-import router from '../router/router'
import { useViewConfigStore } from '../store/viewConfig'
import { Folder, View, getNavigation } from '@nextcloud/files'
import Vue from 'vue'
+import router from '../router/router'
+
+const resetNavigation = () => {
+ const nav = getNavigation()
+ ;[...nav.views].forEach(({ id }) => nav.remove(id))
+ nav.setActive(null)
+}
+
+const createView = (id: string, name: string, parent?: string) => new View({
+ id,
+ name,
+ getContents: async () => ({ folder: {} as Folder, contents: [] }),
+ icon: FolderSvg,
+ order: 1,
+ parent,
+})
describe('Navigation renders', () => {
- delete window._nc_navigation
- const Navigation = getNavigation()
+ let Navigation: Navigation
before(() => {
+ delete window._nc_navigation
+ Navigation = getNavigation()
Vue.prototype.$navigation = Navigation
cy.mockInitialState('files', 'storageStats', {
@@ -40,29 +61,31 @@ describe('Navigation renders', () => {
})
describe('Navigation API', () => {
- delete window._nc_navigation
- const Navigation = getNavigation()
+ let Navigation: Navigation
+
+ before(async () => {
+ delete window._nc_navigation
+ Navigation = getNavigation()
- before(() => {
Vue.prototype.$navigation = Navigation
+ await router.replace({ name: 'filelist', params: { view: 'files' } })
})
+ beforeEach(() => resetNavigation())
+
it('Check API entries rendering', () => {
- Navigation.register(new View({
- id: 'files',
- name: 'Files',
- getContents: async () => ({ folder: {} as Folder, contents: [] }),
- icon: FolderSvg,
- order: 1,
- }))
+ Navigation.register(createView('files', 'Files'))
+ console.warn(Navigation.views)
cy.mount(NavigationView, {
+ router,
global: {
- plugins: [createTestingPinia({
- createSpy: cy.spy,
- })],
+ plugins: [
+ createTestingPinia({
+ createSpy: cy.spy,
+ }),
+ ],
},
- router,
})
cy.get('[data-cy-files-navigation]').should('be.visible')
@@ -72,21 +95,16 @@ describe('Navigation API', () => {
})
it('Adds a new entry and render', () => {
- Navigation.register(new View({
- id: 'sharing',
- name: 'Sharing',
- getContents: async () => ({ folder: {} as Folder, contents: [] }),
- icon: ShareSvg,
- order: 2,
- }))
+ Navigation.register(createView('files', 'Files'))
+ Navigation.register(createView('sharing', 'Sharing'))
cy.mount(NavigationView, {
+ router,
global: {
plugins: [createTestingPinia({
createSpy: cy.spy,
})],
},
- router,
})
cy.get('[data-cy-files-navigation]').should('be.visible')
@@ -96,22 +114,17 @@ describe('Navigation API', () => {
})
it('Adds a new children, render and open menu', () => {
- Navigation.register(new View({
- id: 'sharingin',
- name: 'Shared with me',
- getContents: async () => ({ folder: {} as Folder, contents: [] }),
- parent: 'sharing',
- icon: ShareSvg,
- order: 1,
- }))
+ Navigation.register(createView('files', 'Files'))
+ Navigation.register(createView('sharing', 'Sharing'))
+ Navigation.register(createView('sharingin', 'Shared with me', 'sharing'))
cy.mount(NavigationView, {
+ router,
global: {
plugins: [createTestingPinia({
createSpy: cy.spy,
})],
},
- router,
})
cy.wrap(useViewConfigStore()).as('viewConfigStore')
@@ -139,23 +152,18 @@ describe('Navigation API', () => {
})
it('Throws when adding a duplicate entry', () => {
- expect(() => {
- Navigation.register(new View({
- id: 'files',
- name: 'Files',
- getContents: async () => ({ folder: {} as Folder, contents: [] }),
- icon: FolderSvg,
- order: 1,
- }))
- }).to.throw('View id files is already registered')
+ Navigation.register(createView('files', 'Files'))
+ expect(() => Navigation.register(createView('files', 'Files')))
+ .to.throw('View id files is already registered')
})
})
describe('Quota rendering', () => {
- delete window._nc_navigation
- const Navigation = getNavigation()
+ let Navigation: Navigation
before(() => {
+ delete window._nc_navigation
+ Navigation = getNavigation()
Vue.prototype.$navigation = Navigation
})
diff --git a/apps/files/src/views/Navigation.vue b/apps/files/src/views/Navigation.vue
index 020986d7c85..7df1b70b1a4 100644
--- a/apps/files/src/views/Navigation.vue
+++ b/apps/files/src/views/Navigation.vue
@@ -62,7 +62,7 @@
:name="t('files', 'Files settings')"
data-cy-files-navigation-settings-button
@click.prevent.stop="openSettings">
-
+
@@ -78,22 +78,26 @@
import type { View } from '@nextcloud/files'
import { emit } from '@nextcloud/event-bus'
-import { translate } from '@nextcloud/l10n'
-import Cog from 'vue-material-design-icons/Cog.vue'
+import { translate as t } from '@nextcloud/l10n'
+import { defineComponent } from 'vue'
+
+import IconCog from 'vue-material-design-icons/Cog.vue'
import NcAppNavigation from '@nextcloud/vue/dist/Components/NcAppNavigation.js'
import NcAppNavigationItem from '@nextcloud/vue/dist/Components/NcAppNavigationItem.js'
import NcIconSvgWrapper from '@nextcloud/vue/dist/Components/NcIconSvgWrapper.js'
+import NavigationQuota from '../components/NavigationQuota.vue'
+import SettingsModal from './Settings.vue'
+import { useNavigation } from '../composables/useNavigation'
import { useViewConfigStore } from '../store/viewConfig.ts'
import logger from '../logger.js'
-import NavigationQuota from '../components/NavigationQuota.vue'
-import SettingsModal from './Settings.vue'
-export default {
+export default defineComponent({
name: 'Navigation',
components: {
- Cog,
+ IconCog,
+
NavigationQuota,
NcAppNavigation,
NcAppNavigationItem,
@@ -103,7 +107,12 @@ export default {
setup() {
const viewConfigStore = useViewConfigStore()
+ const { currentView, views } = useNavigation()
+
return {
+ currentView,
+ views,
+
viewConfigStore,
}
},
@@ -115,18 +124,13 @@ export default {
},
computed: {
+ /**
+ * The current view ID from the route params
+ */
currentViewId() {
return this.$route?.params?.view || 'files'
},
- currentView(): View {
- return this.views.find(view => view.id === this.currentViewId)!
- },
-
- views(): View[] {
- return this.$navigation.views
- },
-
parentViews(): View[] {
return this.views
// filter child views
@@ -154,24 +158,27 @@ export default {
},
watch: {
- currentView(view, oldView) {
- if (view.id !== oldView?.id) {
- this.$navigation.setActive(view)
- logger.debug(`Navigation changed from ${oldView.id} to ${view.id}`, { from: oldView, to: view })
-
+ currentViewId(newView, oldView) {
+ if (this.currentViewId !== this.currentView?.id) {
+ // This is guaranteed to be a view because `currentViewId` falls back to the default 'files' view
+ const view = this.views.find(({ id }) => id === this.currentViewId)!
+ // The the new view as active
this.showView(view)
+ logger.debug(`Navigation changed from ${oldView} to ${newView}`, { to: view })
}
},
},
beforeMount() {
- if (this.currentView) {
- logger.debug('Navigation mounted. Showing requested view', { view: this.currentView })
- this.showView(this.currentView)
- }
+ // This is guaranteed to be a view because `currentViewId` falls back to the default 'files' view
+ const view = this.views.find(({ id }) => id === this.currentViewId)!
+ this.showView(view)
+ logger.debug('Navigation mounted. Showing requested view', { view })
},
methods: {
+ t,
+
/**
* Only use exact route matching on routes with child views
* Because if a view does not have children (like the files view) then multiple routes might be matched for it
@@ -182,9 +189,13 @@ export default {
return this.childViews[view.id]?.length > 0
},
+ /**
+ * Set the view as active on the navigation and handle internal state
+ * @param view View to set active
+ */
showView(view: View) {
// Closing any opened sidebar
- window?.OCA?.Files?.Sidebar?.close?.()
+ window.OCA?.Files?.Sidebar?.close?.()
this.$navigation.setActive(view)
emit('files:navigation:changed', view)
},
@@ -238,10 +249,8 @@ export default {
onSettingsClose() {
this.settingsOpened = false
},
-
- t: translate,
},
-}
+})