aboutsummaryrefslogtreecommitdiffstats
path: root/apps/files/src/views
diff options
context:
space:
mode:
Diffstat (limited to 'apps/files/src/views')
-rw-r--r--apps/files/src/views/FilesList.vue19
-rw-r--r--apps/files/src/views/Navigation.cy.ts97
-rw-r--r--apps/files/src/views/Navigation.vue65
3 files changed, 98 insertions, 83 deletions
diff --git a/apps/files/src/views/FilesList.vue b/apps/files/src/views/FilesList.vue
index 493437225d6..fabf1cae6b9 100644
--- a/apps/files/src/views/FilesList.vue
+++ b/apps/files/src/views/FilesList.vue
@@ -107,7 +107,7 @@
</template>
<script lang="ts">
-import type { View, ContentsWithRoot } from '@nextcloud/files'
+import type { ContentsWithRoot } from '@nextcloud/files'
import type { Upload } from '@nextcloud/upload'
import type { CancelablePromise } from 'cancelable-promise'
import type { ComponentPublicInstance } from 'vue'
@@ -137,6 +137,7 @@ import AccountPlusIcon from 'vue-material-design-icons/AccountPlus.vue'
import ViewGridIcon from 'vue-material-design-icons/ViewGrid.vue'
import { action as sidebarAction } from '../actions/sidebarAction.ts'
+import { useNavigation } from '../composables/useNavigation.ts'
import { useFilesStore } from '../store/files.ts'
import { usePathsStore } from '../store/paths.ts'
import { useSelectionStore } from '../store/selection.ts'
@@ -186,10 +187,13 @@ export default defineComponent({
const uploaderStore = useUploaderStore()
const userConfigStore = useUserConfigStore()
const viewConfigStore = useViewConfigStore()
+ const { currentView } = useNavigation()
const enableGridView = (loadState('core', 'config', [])['enable_non-accessible_features'] ?? true)
return {
+ currentView,
+
filesStore,
pathsStore,
selectionStore,
@@ -228,10 +232,6 @@ export default defineComponent({
return this.userConfigStore.userConfig
},
- currentView(): View {
- return this.$navigation.active || this.$navigation.views.find((view) => view.id === (this.$route.params?.view ?? 'files'))!
- },
-
pageHeading(): string {
return this.currentView?.name ?? t('files', 'Files')
},
@@ -475,7 +475,7 @@ export default defineComponent({
subscribe('files:node:deleted', this.onNodeDeleted)
subscribe('files:node:updated', this.onUpdatedNode)
subscribe('nextcloud:unified-search.search', this.onSearch)
- subscribe('nextcloud:unified-search.reset', this.onSearch)
+ subscribe('nextcloud:unified-search.reset', this.resetSearch)
// reload on settings change
this.unsubscribeStoreCallback = this.userConfigStore.$subscribe(() => this.fetchContent(), { deep: true })
@@ -485,7 +485,7 @@ export default defineComponent({
unsubscribe('files:node:deleted', this.onNodeDeleted)
unsubscribe('files:node:updated', this.onUpdatedNode)
unsubscribe('nextcloud:unified-search.search', this.onSearch)
- unsubscribe('nextcloud:unified-search.reset', this.onSearch)
+ unsubscribe('nextcloud:unified-search.reset', this.resetSearch)
this.unsubscribeStoreCallback()
},
@@ -656,6 +656,9 @@ export default defineComponent({
* Reset the search query
*/
resetSearch() {
+ // Reset debounced calls to not set the query again
+ this.onSearch.clear()
+ // Reset filter query
this.filterText = ''
},
@@ -668,7 +671,7 @@ export default defineComponent({
if (window?.OCA?.Files?.Sidebar?.setActiveTab) {
window.OCA.Files.Sidebar.setActiveTab('sharing')
}
- sidebarAction.exec(this.currentFolder, this.currentView, this.currentFolder.path)
+ sidebarAction.exec(this.currentFolder, this.currentView!, this.currentFolder.path)
},
toggleGridView() {
this.userConfigStore.update('grid_view', !this.userConfig.grid_view)
diff --git a/apps/files/src/views/Navigation.cy.ts b/apps/files/src/views/Navigation.cy.ts
index a555a04a910..46360a2357a 100644
--- a/apps/files/src/views/Navigation.cy.ts
+++ b/apps/files/src/views/Navigation.cy.ts
@@ -2,22 +2,38 @@
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
-import FolderSvg from '@mdi/svg/svg/folder.svg'
-import ShareSvg from '@mdi/svg/svg/share-variant.svg'
+import type { Navigation } from '@nextcloud/files'
+import FolderSvg from '@mdi/svg/svg/folder.svg?raw'
import { createTestingPinia } from '@pinia/testing'
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', {
@@ -44,29 +60,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')
@@ -76,21 +94,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')
@@ -100,22 +113,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')
@@ -143,23 +151,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 71e9bf38068..b69c6d5f7f2 100644
--- a/apps/files/src/views/Navigation.vue
+++ b/apps/files/src/views/Navigation.vue
@@ -45,7 +45,7 @@
:name="t('files', 'Files settings')"
data-cy-files-navigation-settings-button
@click.prevent.stop="openSettings">
- <Cog slot="icon" :size="20" />
+ <IconCog slot="icon" :size="20" />
</NcAppNavigationItem>
</ul>
</template>
@@ -61,22 +61,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,
@@ -86,7 +90,12 @@ export default {
setup() {
const viewConfigStore = useViewConfigStore()
+ const { currentView, views } = useNavigation()
+
return {
+ currentView,
+ views,
+
viewConfigStore,
}
},
@@ -98,18 +107,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
@@ -137,24 +141,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
@@ -165,9 +172,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)
},
@@ -221,10 +232,8 @@ export default {
onSettingsClose() {
this.settingsOpened = false
},
-
- t: translate,
},
-}
+})
</script>
<style scoped lang="scss">