diff options
author | Ferdinand Thiessen <opensource@fthiessen.de> | 2025-02-06 01:13:40 +0100 |
---|---|---|
committer | Ferdinand Thiessen <opensource@fthiessen.de> | 2025-02-08 14:28:42 +0100 |
commit | fc9108b11abd58ba4d10f19a89ebfffa382c51e6 (patch) | |
tree | 1a8ee9c3f9b2253a5fbbad145b046c5708a50716 /apps | |
parent | beca0a19ee74efb4d3e1f8cc7e0490c88aa22a3f (diff) | |
download | nextcloud-server-fc9108b11abd58ba4d10f19a89ebfffa382c51e6.tar.gz nextcloud-server-fc9108b11abd58ba4d10f19a89ebfffa382c51e6.zip |
fix(files): only send config update requests if user is logged in
Since we use the files app also for public shares it is not guaranteed
that there is a user logged in, in that case the update for user / view
config will fail.
So ensure there is a user or do not send a request.
Also refactor both stores to setup styles to fix (remove) initialization hack,
which causes Typescript issues.
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
Diffstat (limited to 'apps')
-rw-r--r-- | apps/files/src/eventbus.d.ts | 3 | ||||
-rw-r--r-- | apps/files/src/store/userconfig.ts | 78 | ||||
-rw-r--r-- | apps/files/src/store/viewConfig.ts | 150 | ||||
-rw-r--r-- | apps/files/src/types.ts | 8 | ||||
-rw-r--r-- | apps/files/src/views/Navigation.vue | 14 |
5 files changed, 126 insertions, 127 deletions
diff --git a/apps/files/src/eventbus.d.ts b/apps/files/src/eventbus.d.ts index e1fd8c73b4b..ce0611580fe 100644 --- a/apps/files/src/eventbus.d.ts +++ b/apps/files/src/eventbus.d.ts @@ -7,7 +7,8 @@ import type { IFileListFilter, Node } from '@nextcloud/files' declare module '@nextcloud/event-bus' { export interface NextcloudEvents { // mapping of 'event name' => 'event type' - 'files:config:updated': { key: string, value: unknown } + 'files:config:updated': { key: string, value: boolean } + 'files:view-config:updated': { key: string, value: string|number|boolean, view: string } 'files:favorites:removed': Node 'files:favorites:added': Node diff --git a/apps/files/src/store/userconfig.ts b/apps/files/src/store/userconfig.ts index ffe07a91bab..95829c849e8 100644 --- a/apps/files/src/store/userconfig.ts +++ b/apps/files/src/store/userconfig.ts @@ -2,15 +2,16 @@ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors * SPDX-License-Identifier: AGPL-3.0-or-later */ -import type { UserConfig, UserConfigStore } from '../types' -import { defineStore } from 'pinia' +import type { UserConfig } from '../types' +import { getCurrentUser } from '@nextcloud/auth' import { emit, subscribe } from '@nextcloud/event-bus' -import { generateUrl } from '@nextcloud/router' import { loadState } from '@nextcloud/initial-state' +import { generateUrl } from '@nextcloud/router' +import { defineStore } from 'pinia' +import { ref, set } from 'vue' import axios from '@nextcloud/axios' -import Vue from 'vue' -const userConfig = loadState<UserConfig>('files', 'config', { +const initialUserConfig = loadState<UserConfig>('files', 'config', { show_hidden: false, crop_image_previews: true, sort_favorites_first: true, @@ -18,45 +19,38 @@ const userConfig = loadState<UserConfig>('files', 'config', { grid_view: false, }) -export const useUserConfigStore = function(...args) { - const store = defineStore('userconfig', { - state: () => ({ - userConfig, - } as UserConfigStore), +export const useUserConfigStore = defineStore('userconfig', () => { + const userConfig = ref<UserConfig>({ ...initialUserConfig }) - actions: { - /** - * Update the user config local store - * @param key - * @param value - */ - onUpdate(key: string, value: boolean) { - Vue.set(this.userConfig, key, value) - }, + /** + * Update the user config local store + * @param key The config key + * @param value The new value + */ + function onUpdate(key: string, value: boolean): void { + set(userConfig.value, key, value) + } - /** - * 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), { - value, - }) - emit('files:config:updated', { key, value }) - }, - }, - }) + /** + * Update the user config local store AND on server side + * @param key The config key + * @param value The new value + */ + async function update(key: string, value: boolean): Promise<void> { + // only update if a user is logged in (not the case for public shares) + if (getCurrentUser() !== null) { + await axios.put(generateUrl('/apps/files/api/v1/config/{key}', { key }), { + value, + }) + } + emit('files:config:updated', { key, value }) + } - const userConfigStore = store(...args) + // Register the event listener + subscribe('files:config:updated', ({ key, value }) => onUpdate(key, value)) - // Make sure we only register the listeners once - if (!userConfigStore._initialized) { - subscribe('files:config:updated', function({ key, value }: { key: string, value: boolean }) { - userConfigStore.onUpdate(key, value) - }) - userConfigStore._initialized = true + return { + userConfig, + update, } - - return userConfigStore -} +}) diff --git a/apps/files/src/store/viewConfig.ts b/apps/files/src/store/viewConfig.ts index 76786306d60..a902cedb6fa 100644 --- a/apps/files/src/store/viewConfig.ts +++ b/apps/files/src/store/viewConfig.ts @@ -2,95 +2,95 @@ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { defineStore } from 'pinia' +import type { ViewConfigs, ViewId, ViewConfig } from '../types' + +import { getCurrentUser } from '@nextcloud/auth' import { emit, subscribe } from '@nextcloud/event-bus' -import { generateUrl } from '@nextcloud/router' import { loadState } from '@nextcloud/initial-state' +import { generateUrl } from '@nextcloud/router' +import { defineStore } from 'pinia' +import { ref, set } from 'vue' import axios from '@nextcloud/axios' -import Vue from 'vue' -import type { ViewConfigs, ViewConfigStore, ViewId, ViewConfig } from '../types' +const initialViewConfig = loadState('files', 'viewConfigs', {}) as ViewConfigs -const viewConfig = loadState('files', 'viewConfigs', {}) as ViewConfigs +export const useViewConfigStore = defineStore('viewconfig', () => { -export const useViewConfigStore = function(...args) { - const store = defineStore('viewconfig', { - state: () => ({ - viewConfig, - } as ViewConfigStore), + const viewConfigs = ref({ ...initialViewConfig }) - getters: { - getConfig: (state) => (view: ViewId): ViewConfig => state.viewConfig[view] || {}, + /** + * Get the config for a specific view + * @param viewid Id of the view to fet the config for + */ + function getConfig(viewid: ViewId): ViewConfig { + return viewConfigs.value[viewid] || {} + } - getConfigs: (state) => (): ViewConfigs => state.viewConfig, - }, + /** + * Update the view config local store + * @param viewId The id of the view to update + * @param key The config key to update + * @param value The new value + */ + function onUpdate(viewId: ViewId, key: string, value: string | number | boolean): void { + if (!(viewId in viewConfigs.value)) { + set(viewConfigs.value, viewId, {}) + } + set(viewConfigs.value[viewId], key, value) + } - actions: { - /** - * Update the view config local store - * @param view - * @param key - * @param value - */ - onUpdate(view: ViewId, key: string, value: string | number | boolean) { - if (!this.viewConfig[view]) { - Vue.set(this.viewConfig, view, {}) - } - Vue.set(this.viewConfig[view], key, value) - }, + /** + * Update the view config local store AND on server side + * @param view Id of the view to update + * @param key Config key to update + * @param value New value + */ + async function update(view: ViewId, key: string, value: string | number | boolean): Promise<void> { + if (getCurrentUser() !== null) { + await axios.put(generateUrl('/apps/files/api/v1/views'), { + value, + view, + key, + }) + } - /** - * Update the view config local store AND on server side - * @param view - * @param key - * @param value - */ - async update(view: ViewId, key: string, value: string | number | boolean) { - axios.put(generateUrl('/apps/files/api/v1/views'), { - value, - view, - key, - }) + emit('files:view-config:updated', { view, key, value }) + } - emit('files:viewconfig:updated', { view, key, value }) - }, + /** + * Set the sorting key AND sort by ASC + * The key param must be a valid key of a File object + * If not found, will be searched within the File attributes + * @param key Key to sort by + * @param view View to set the sorting key for + */ + function setSortingBy(key = 'basename', view = 'files'): void { + // Save new config + update(view, 'sorting_mode', key) + update(view, 'sorting_direction', 'asc') + } - /** - * Set the sorting key AND sort by ASC - * The key param must be a valid key of a File object - * If not found, will be searched within the File attributes - * @param key Key to sort by - * @param view View to set the sorting key for - */ - setSortingBy(key = 'basename', view = 'files') { - // Save new config - this.update(view, 'sorting_mode', key) - this.update(view, 'sorting_direction', 'asc') - }, + /** + * Toggle the sorting direction + * @param viewId id of the view to set the sorting order for + */ + function toggleSortingDirection(viewId = 'files'): void { + const config = viewConfigs.value[viewId] || { sorting_direction: 'asc' } + const newDirection = config.sorting_direction === 'asc' ? 'desc' : 'asc' - /** - * Toggle the sorting direction - * @param view view to set the sorting order for - */ - toggleSortingDirection(view = 'files') { - const config = this.getConfig(view) || { sorting_direction: 'asc' } - const newDirection = config.sorting_direction === 'asc' ? 'desc' : 'asc' + // Save new config + update(viewId, 'sorting_direction', newDirection) + } - // Save new config - this.update(view, 'sorting_direction', newDirection) - }, - }, - }) + // Initialize event listener + subscribe('files:view-config:updated', ({ view, key, value }) => onUpdate(view, key, value)) - const viewConfigStore = store(...args) + return { + viewConfigs, - // Make sure we only register the listeners once - if (!viewConfigStore._initialized) { - subscribe('files:viewconfig:updated', function({ view, key, value }: { view: ViewId, key: string, value: boolean }) { - viewConfigStore.onUpdate(view, key, value) - }) - viewConfigStore._initialized = true + getConfig, + setSortingBy, + toggleSortingDirection, + update, } - - return viewConfigStore -} +}) diff --git a/apps/files/src/types.ts b/apps/files/src/types.ts index 673cb06e182..62ba53b89d2 100644 --- a/apps/files/src/types.ts +++ b/apps/files/src/types.ts @@ -50,7 +50,13 @@ export interface PathOptions { // User config store export interface UserConfig { - [key: string]: boolean + [key: string]: boolean|undefined + + show_hidden: boolean + crop_image_previews: boolean + sort_favorites_first: boolean + sort_folders_first: boolean + grid_view: boolean } export interface UserConfigStore { userConfig: UserConfig diff --git a/apps/files/src/views/Navigation.vue b/apps/files/src/views/Navigation.vue index 4202a81c886..21b2bce2ccd 100644 --- a/apps/files/src/views/Navigation.vue +++ b/apps/files/src/views/Navigation.vue @@ -159,14 +159,12 @@ export default defineComponent({ methods: { async loadExpandedViews() { - const viewConfigs = this.viewConfigStore.getConfigs() - const viewsToLoad: View[] = (Object.entries(viewConfigs) as Array<[string, ViewConfig]>) - // eslint-disable-next-line @typescript-eslint/no-unused-vars - .filter(([viewId, config]) => config.expanded === true) - // eslint-disable-next-line @typescript-eslint/no-unused-vars - .map(([viewId, config]) => this.views.find(view => view.id === viewId)) - .filter(Boolean) // Only registered views - .filter(view => view.loadChildViews && !view.loaded) + const viewsToLoad: View[] = (Object.entries(this.viewConfigStore.viewConfigs) as Array<[string, ViewConfig]>) + .filter(([, config]) => config.expanded === true) + .map(([viewId]) => this.views.find(view => view.id === viewId)) + // eslint-disable-next-line no-use-before-define + .filter(Boolean as unknown as ((u: unknown) => u is View)) + .filter((view) => view.loadChildViews && !view.loaded) for (const view of viewsToLoad) { await view.loadChildViews(view) } |