Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[stable31] fix(files): only send config update requests if user is logged in #50736

Draft
wants to merge 2 commits into
base: stable31
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion apps/files/src/eventbus.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
78 changes: 36 additions & 42 deletions apps/files/src/store/userconfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,61 +2,55 @@
* 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,
sort_folders_first: true,
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
}
})
150 changes: 75 additions & 75 deletions apps/files/src/store/viewConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
})
8 changes: 7 additions & 1 deletion apps/files/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
14 changes: 6 additions & 8 deletions apps/files/src/views/Navigation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
Loading