diff options
author | John Molakvoæ <skjnldsv@protonmail.com> | 2023-02-04 21:13:06 +0100 |
---|---|---|
committer | John Molakvoæ <skjnldsv@protonmail.com> | 2023-04-06 14:49:29 +0200 |
commit | 03c32774b060f48a900be5f9f943de84c298cca5 (patch) | |
tree | 74bea842d02548e9e02454e778e2b32c5a81a78b /apps | |
parent | 29a7f7f6efd2a9791fdcfb9f9f7e862bafd8da82 (diff) | |
download | nextcloud-server-03c32774b060f48a900be5f9f943de84c298cca5.tar.gz nextcloud-server-03c32774b060f48a900be5f9f943de84c298cca5.zip |
feat(files): switch to pinia
Signed-off-by: John Molakvoæ <skjnldsv@protonmail.com>
Diffstat (limited to 'apps')
-rw-r--r-- | apps/files/src/components/BreadCrumbs.vue | 5 | ||||
-rw-r--r-- | apps/files/src/components/FileEntry.vue | 24 | ||||
-rw-r--r-- | apps/files/src/components/FilesListHeader.vue | 34 | ||||
-rw-r--r-- | apps/files/src/components/FilesListVirtual.vue | 13 | ||||
-rw-r--r-- | apps/files/src/main.js | 9 | ||||
-rw-r--r-- | apps/files/src/store/files.ts | 105 | ||||
-rw-r--r-- | apps/files/src/store/index.ts | 16 | ||||
-rw-r--r-- | apps/files/src/store/paths.ts | 48 | ||||
-rw-r--r-- | apps/files/src/store/selection.ts | 37 | ||||
-rw-r--r-- | apps/files/src/types.ts | 17 | ||||
-rw-r--r-- | apps/files/src/views/FilesList.vue | 41 |
11 files changed, 172 insertions, 177 deletions
diff --git a/apps/files/src/components/BreadCrumbs.vue b/apps/files/src/components/BreadCrumbs.vue index 15fd35667ec..7d7ebc7f1b3 100644 --- a/apps/files/src/components/BreadCrumbs.vue +++ b/apps/files/src/components/BreadCrumbs.vue @@ -12,8 +12,9 @@ import NcBreadcrumbs from '@nextcloud/vue/dist/Components/NcBreadcrumbs.js' import NcBreadcrumb from '@nextcloud/vue/dist/Components/NcBreadcrumb.js' import { basename } from 'path' +import Vue from 'vue' -export default { +export default Vue.extend({ name: 'BreadCrumbs', components: { @@ -45,7 +46,7 @@ export default { }) }, }, -} +}) </script> <style lang="scss" scoped> diff --git a/apps/files/src/components/FileEntry.vue b/apps/files/src/components/FileEntry.vue index de340917b69..204976061df 100644 --- a/apps/files/src/components/FileEntry.vue +++ b/apps/files/src/components/FileEntry.vue @@ -47,12 +47,15 @@ import { Folder, File } from '@nextcloud/files' import { Fragment } from 'vue-fragment' import { join } from 'path' import { translate } from '@nextcloud/l10n' -import NcCheckboxRadioSwitch from '@nextcloud/vue/dist/Components/NcCheckboxRadioSwitch.js' import FolderIcon from 'vue-material-design-icons/Folder.vue' +import NcCheckboxRadioSwitch from '@nextcloud/vue/dist/Components/NcCheckboxRadioSwitch.js' +import Vue from 'vue' import logger from '../logger' +import { useSelectionStore } from '../store/selection' +import { useFilesStore } from '../store/files' -export default { +export default Vue.extend({ name: 'FileEntry', components: { @@ -72,6 +75,15 @@ export default { }, }, + setup() { + const filesStore = useFilesStore() + const selectionStore = useSelectionStore() + return { + filesStore, + selectionStore, + } + }, + computed: { dir() { // Remove any trailing slash but leave root slash @@ -104,11 +116,11 @@ export default { selectedFiles: { get() { - return this.$store.state.selection.selected + return this.selectionStore.selected }, set(selection) { logger.debug('Added node to selection', { selection }) - this.$store.dispatch('selection/set', selection) + this.selectionStore.set(selection) }, }, }, @@ -121,12 +133,12 @@ export default { * @return {Folder|File} */ getNode(fileId) { - return this.$store.getters['files/getNode'](fileId) + return this.filesStore.getNode(fileId) }, t: translate, }, -} +}) </script> <style scoped lang="scss"> diff --git a/apps/files/src/components/FilesListHeader.vue b/apps/files/src/components/FilesListHeader.vue index 588d86709da..c5593b90dfd 100644 --- a/apps/files/src/components/FilesListHeader.vue +++ b/apps/files/src/components/FilesListHeader.vue @@ -36,13 +36,16 @@ </template> <script lang="ts"> +import { File, Folder } from '@nextcloud/files' import { translate } from '@nextcloud/l10n' import NcCheckboxRadioSwitch from '@nextcloud/vue/dist/Components/NcCheckboxRadioSwitch.js' +import Vue from 'vue' import logger from '../logger' -import { File, Folder } from '@nextcloud/files' +import { useSelectionStore } from '../store/selection' +import { useFilesStore } from '../store/files' -export default { +export default Vue.extend({ name: 'FilesListHeader', components: { @@ -56,6 +59,15 @@ export default { }, }, + setup() { + const filesStore = useFilesStore() + const selectionStore = useSelectionStore() + return { + filesStore, + selectionStore, + } + }, + computed: { dir() { // Remove any trailing slash but leave root slash @@ -63,12 +75,14 @@ export default { }, selectAllBind() { + const label = this.isNoneSelected || this.isSomeSelected + ? this.t('files', 'Select all') + : this.t('files', 'Unselect all') return { - ariaLabel: this.isNoneSelected || this.isSomeSelected - ? this.t('files', 'Select all') - : this.t('files', 'Unselect all'), + 'aria-label': label, checked: this.isAllSelected, indeterminate: this.isSomeSelected, + title: label, } }, @@ -85,7 +99,7 @@ export default { }, selectedFiles() { - return this.$store.state.selection.selected + return this.selectionStore.selected }, }, @@ -97,23 +111,23 @@ export default { * @return {Folder|File} */ getNode(fileId) { - return this.$store.getters['files/getNode'](fileId) + return this.filesStore.getNode(fileId) }, onToggleAll(selected) { if (selected) { const selection = this.nodes.map(node => node.attributes.fileid.toString()) logger.debug('Added all nodes to selection', { selection }) - this.$store.dispatch('selection/set', selection) + this.selectionStore.set(selection) } else { logger.debug('Cleared selection') - this.$store.dispatch('selection/reset') + this.selectionStore.reset() } }, t: translate, }, -} +}) </script> <style scoped lang="scss"> diff --git a/apps/files/src/components/FilesListVirtual.vue b/apps/files/src/components/FilesListVirtual.vue index 9228179a96c..081875ecf0d 100644 --- a/apps/files/src/components/FilesListVirtual.vue +++ b/apps/files/src/components/FilesListVirtual.vue @@ -44,11 +44,12 @@ import { Folder, File } from '@nextcloud/files' import { translate, translatePlural } from '@nextcloud/l10n' import VirtualList from 'vue-virtual-scroll-list' +import Vue from 'vue' import FileEntry from './FileEntry.vue' import FilesListHeader from './FilesListHeader.vue' -export default { +export default Vue.extend({ name: 'FilesListVirtual', components: { @@ -94,7 +95,7 @@ export default { t: translate, }, -} +}) </script> <style scoped lang="scss"> @@ -116,6 +117,14 @@ export default { width: 100%; } + thead { + // Pinned on top when scrolling + position: sticky; + z-index: 10; + top: 0; + background-color: var(--color-main-background); + } + thead, .files-list__row { border-bottom: 1px solid var(--color-border); } diff --git a/apps/files/src/main.js b/apps/files/src/main.js index 3d1c88755f0..56332374051 100644 --- a/apps/files/src/main.js +++ b/apps/files/src/main.js @@ -3,6 +3,8 @@ import './legacy/filelistSearch.js' import processLegacyFilesViews from './legacy/navigationMapper.js' import Vue from 'vue' +import { createPinia, PiniaVuePlugin } from 'pinia' + import NavigationService from './services/Navigation.ts' import NavigationView from './views/Navigation.vue' @@ -12,7 +14,6 @@ import SettingsService from './services/Settings.js' import SettingsModel from './models/Setting.js' import router from './router/router.js' -import store from './store/index.ts' // Init private and public Files namespace window.OCA.Files = window.OCA.Files ?? {} @@ -38,6 +39,10 @@ const FilesNavigationRoot = new View({ }) FilesNavigationRoot.$mount('#app-navigation-files') +// Init Pinia store +Vue.use(PiniaVuePlugin) +const pinia = createPinia() + // Init content list view const ListView = Vue.extend(FilesListView) const FilesList = new ListView({ @@ -46,7 +51,7 @@ const FilesList = new ListView({ Navigation, }, router, - store, + pinia, }) FilesList.$mount('#app-content-vue') diff --git a/apps/files/src/store/files.ts b/apps/files/src/store/files.ts index e9760e2bc85..c0ec1f7814b 100644 --- a/apps/files/src/store/files.ts +++ b/apps/files/src/store/files.ts @@ -21,77 +21,50 @@ */ /* eslint-disable */ import type { Folder, Node } from '@nextcloud/files' -import Vue from 'vue' -import type { FileStore, RootStore, RootOptions, Service } from '../types' +import type { FilesStore, RootsStore, RootOptions, Service, FilesState } from '../types' -const state = { - files: {} as FileStore, - roots: {} as RootStore, -} +import { defineStore } from 'pinia' +import Vue from 'vue' +import logger from '../logger' -const getters = { - /** - * Get a file or folder by id - */ - getNode: (state) => (id: number): Node|undefined => state.files[id], +export const useFilesStore = defineStore('files', { + state: (): FilesState => ({ + files: {} as FilesStore, + roots: {} as RootsStore, + }), - /** - * Get a list of files or folders by their IDs - * Does not return undefined values - */ - getNodes: (state) => (ids: number[]): Node[] => ids - .map(id => state.files[id]) - .filter(Boolean), - /** - * Get a file or folder by id - */ - getRoot: (state) => (service: Service): Folder|undefined => state.roots[service], -} + getters: { + /** + * Get a file or folder by id + */ + getNode: (state) => (id: number): Node|undefined => state.files[id], -const mutations = { - updateNodes: (state, nodes: Node[]) => { - nodes.forEach(node => { - if (!node.attributes.fileid) { - return - } - Vue.set(state.files, node.attributes.fileid, node) - // state.files = { - // ...state.files, - // [node.attributes.fileid]: node, - // } - }) + /** + * Get a list of files or folders by their IDs + * Does not return undefined values + */ + getNodes: (state) => (ids: number[]): Node[] => ids + .map(id => state.files[id]) + .filter(Boolean), + /** + * Get a file or folder by id + */ + getRoot: (state) => (service: Service): Folder|undefined => state.roots[service], }, - setRoot: (state, { service, root }: RootOptions) => { - state.roots = { - ...state.roots, - [service]: root, - } - } -} - -const actions = { - /** - * Insert valid nodes into the store. - * Roots (that does _not_ have a fileid) should - * be defined in the roots store - */ - addNodes: (context, nodes: Node[]) => { - context.commit('updateNodes', nodes) - }, + actions: { + updateNodes(nodes: Node[]) { + nodes.forEach(node => { + if (!node.attributes.fileid) { + logger.warn('Trying to update/set a node without fileid', node) + return + } + Vue.set(this.files, node.attributes.fileid, node) + }) + }, - /** - * Set the root of a service - */ - setRoot(context, { service, root }: RootOptions) { - context.commit('setRoot', { service, root }) + setRoot({ service, root }: RootOptions) { + Vue.set(this.roots, service, root) + } } -} - -export default { - namespaced: true, - state, - getters, - mutations, - actions, -} +}) diff --git a/apps/files/src/store/index.ts b/apps/files/src/store/index.ts deleted file mode 100644 index 52007fef892..00000000000 --- a/apps/files/src/store/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -import Vue from 'vue' -import Vuex, { Store } from 'vuex' - -import files from './files' -import paths from './paths' -import selection from './selection' - -Vue.use(Vuex) - -export default new Store({ - modules: { - files, - paths, - selection, - }, -}) diff --git a/apps/files/src/store/paths.ts b/apps/files/src/store/paths.ts index d6b23578da7..3573dd9c54e 100644 --- a/apps/files/src/store/paths.ts +++ b/apps/files/src/store/paths.ts @@ -20,52 +20,34 @@ * */ /* eslint-disable */ -import type { Folder } from '@nextcloud/files' +import type { PathOptions, ServicesState } from '../types' + +import { defineStore } from 'pinia' import Vue from 'vue' -import type { PathOptions, ServicePaths, ServiceStore } from '../types' -const module = { - state: { - services: { - files: {} as ServicePaths, - } as ServiceStore, - }, +export const usePathsStore = defineStore('paths', { + state: (): ServicesState => ({}), getters: { - getPath(state: { services: ServiceStore }) { + getPath: (state) => { return (service: string, path: string): number|undefined => { - if (!state.services[service]) { + if (!state[service]) { return undefined } - return state.services[service][path] + return state[service][path] } }, }, - mutations: { - addPath: (state, opts: PathOptions) => { + actions: { + addPath(payload: PathOptions) { // If it doesn't exists, init the service state - if (!state.services[opts.service]) { - // TODO: investigate why Vue.set is not working - state.services = { - [opts.service]: {} as ServicePaths, - ...state.services - } + if (!this[payload.service]) { + Vue.set(this, payload.service, {}) } - // Now we can set the path - Vue.set(state.services[opts.service], opts.path, opts.fileid) - } - }, - - actions: { - addPath: (context, opts: PathOptions) => { - context.commit('addPath', opts) + // Now we can set the provided path + Vue.set(this[payload.service], payload.path, payload.fileid) }, } -} - -export default { - namespaced: true, - ...module, -} +}) diff --git a/apps/files/src/store/selection.ts b/apps/files/src/store/selection.ts index 3ec61848c98..56e1b3dcbb0 100644 --- a/apps/files/src/store/selection.ts +++ b/apps/files/src/store/selection.ts @@ -20,32 +20,27 @@ * */ /* eslint-disable */ -import type { Folder } from '@nextcloud/files' +import { defineStore } from 'pinia' import Vue from 'vue' -import type { PathOptions, ServicePaths, ServiceStore } from '../types' -const module = { - state: { +export const useSelectionStore = defineStore('selection', { + state: () => ({ selected: [] as number[] - }, - - mutations: { - set: (state, selection: number[]) => { - Vue.set(state, 'selected', selection) - } - }, + }), actions: { - set: (context, selection = [] as number[]) => { - context.commit('set', selection) + /** + * Set the selection of fileIds + */ + set(selection = [] as number[]) { + Vue.set(this, 'selected', selection) }, - reset(context) { - context.commit('set', []) + + /** + * Reset the selection + */ + reset() { + Vue.set(this, 'selected', []) } } -} - -export default { - namespaced: true, - ...module, -} +}) diff --git a/apps/files/src/types.ts b/apps/files/src/types.ts index 1c7068985d8..a60b74294c0 100644 --- a/apps/files/src/types.ts +++ b/apps/files/src/types.ts @@ -27,11 +27,16 @@ import type { Node } from '@nextcloud/files' export type Service = string // Files store -export type FileStore = { +export type FilesState = { + files: FilesStore, + roots: RootsStore, +} + +export type FilesStore = { [id: number]: Node } -export type RootStore = { +export type RootsStore = { [service: Service]: Folder } @@ -41,12 +46,12 @@ export interface RootOptions { } // Paths store -export type ServicePaths = { - [path: string]: number +export type ServicesState = { + [service: Service]: PathsStore } -export type ServiceStore = { - [service: Service]: ServicePaths +export type PathsStore = { + [path: string]: number } export interface PathOptions { diff --git a/apps/files/src/views/FilesList.vue b/apps/files/src/views/FilesList.vue index adc8a3bcb0f..abca146138e 100644 --- a/apps/files/src/views/FilesList.vue +++ b/apps/files/src/views/FilesList.vue @@ -75,8 +75,12 @@ import Navigation from '../services/Navigation' import FilesListVirtual from '../components/FilesListVirtual.vue' import { ContentsWithRoot } from '../services/Navigation' import { join } from 'path' +import Vue from 'vue' +import { usePathsStore } from '../store/paths' +import { useFilesStore } from '../store/files' +import { useSelectionStore } from '../store/selection' -export default { +export default Vue.extend({ name: 'FilesList', components: { @@ -97,6 +101,17 @@ export default { }, }, + setup() { + const pathsStore = usePathsStore() + const filesStore = useFilesStore() + const selectionStore = useSelectionStore() + return { + filesStore, + pathsStore, + selectionStore, + } + }, + data() { return { loading: true, @@ -134,10 +149,10 @@ export default { */ currentFolder() { if (this.dir === '/') { - return this.$store.getters['files/getRoot'](this.currentViewId) + return this.filesStore.getRoot(this.currentViewId) } - const fileId = this.$store.getters['paths/getPath'](this.currentViewId, this.dir) - return this.$store.getters['files/getNode'](fileId) + const fileId = this.pathsStore.getPath(this.currentViewId, this.dir) + return this.filesStore.getNode(fileId) }, /** @@ -182,14 +197,14 @@ export default { } logger.debug('View changed', { newView, oldView }) - this.$store.dispatch('selection/reset') + this.selectionStore.reset() this.fetchContent() }, dir(newDir, oldDir) { logger.debug('Directory changed', { newDir, oldDir }) // TODO: preserve selection on browsing? - this.$store.dispatch('selection/reset') + this.selectionStore.reset() this.fetchContent() }, @@ -226,7 +241,7 @@ export default { logger.debug('Fetched contents', { dir, folder, contents }) // Update store - this.$store.dispatch('files/addNodes', contents) + this.filesStore.updateNodes(contents) // Define current directory children folder.children = contents.map(node => node.attributes.fileid) @@ -234,12 +249,12 @@ export default { // If we're in the root dir, define the root if (dir === '/') { console.debug('files', 'Setting root', { service: currentView.id, folder }) - this.$store.dispatch('files/setRoot', { service: currentView.id, root: folder }) + this.filesStore.setRoot({ service: currentView.id, root: folder }) } else // Otherwise, add the folder to the store if (folder.attributes.fileid) { - this.$store.dispatch('files/addNodes', [folder]) - this.$store.dispatch('paths/addPath', { service: currentView.id, fileid: folder.attributes.fileid, path: dir }) + this.filesStore.updateNodes([folder]) + this.pathsStore.addPath({ service: currentView.id, fileid: folder.attributes.fileid, path: dir }) } else { // If we're here, the view API messed up logger.error('Invalid root folder returned', { dir, folder, currentView }) @@ -248,7 +263,7 @@ export default { // Update paths store const folders = contents.filter(node => node.type === 'folder') folders.forEach(node => { - this.$store.dispatch('paths/addPath', { service: currentView.id, fileid: node.attributes.fileid, path: join(dir, node.basename) }) + this.pathsStore.addPath({ service: currentView.id, fileid: node.attributes.fileid, path: join(dir, node.basename) }) }) } catch (error) { logger.error('Error while fetching content', { error }) @@ -265,12 +280,12 @@ export default { * @return {Folder|File} */ getNode(fileId) { - return this.$store.getters['files/getNode'](fileId) + return this.filesStore.getNode(fileId) }, t: translate, }, -} +}) </script> <style scoped lang="scss"> |