mirror of
https://github.com/nextcloud/server.git
synced 2026-06-09 00:32:29 -04:00
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>
This commit is contained in:
parent
8144ab6cc7
commit
8ab40e5065
5 changed files with 136 additions and 137 deletions
3
apps/files/src/eventbus.d.ts
vendored
3
apps/files/src/eventbus.d.ts
vendored
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 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 })
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const userConfigStore = store(...args)
|
||||
|
||||
// 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
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
|
||||
return userConfigStore
|
||||
}
|
||||
/**
|
||||
* 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 })
|
||||
}
|
||||
|
||||
// Register the event listener
|
||||
subscribe('files:config:updated', ({ key, value }) => onUpdate(key, value))
|
||||
|
||||
return {
|
||||
userConfig,
|
||||
update,
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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] || {},
|
||||
|
||||
getConfigs: (state) => (): ViewConfigs => state.viewConfig,
|
||||
},
|
||||
|
||||
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
|
||||
* @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: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
|
||||
*/
|
||||
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 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
|
||||
this.update(view, 'sorting_direction', newDirection)
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const viewConfigStore = store(...args)
|
||||
|
||||
// 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
|
||||
/**
|
||||
* 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] || {}
|
||||
}
|
||||
|
||||
return viewConfigStore
|
||||
}
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,
|
||||
})
|
||||
}
|
||||
|
||||
emit('files:view-config: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')
|
||||
}
|
||||
|
||||
/**
|
||||
* 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'
|
||||
|
||||
// Save new config
|
||||
update(viewId, 'sorting_direction', newDirection)
|
||||
}
|
||||
|
||||
// Initialize event listener
|
||||
subscribe('files:view-config:updated', ({ view, key, value }) => onUpdate(view, key, value))
|
||||
|
||||
return {
|
||||
viewConfigs,
|
||||
|
||||
getConfig,
|
||||
setSortingBy,
|
||||
toggleSortingDirection,
|
||||
update,
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue