summaryrefslogtreecommitdiffstats
path: root/apps
diff options
context:
space:
mode:
authorJohn Molakvoæ <skjnldsv@protonmail.com>2023-02-04 21:13:06 +0100
committerJohn Molakvoæ <skjnldsv@protonmail.com>2023-04-06 14:49:29 +0200
commit03c32774b060f48a900be5f9f943de84c298cca5 (patch)
tree74bea842d02548e9e02454e778e2b32c5a81a78b /apps
parent29a7f7f6efd2a9791fdcfb9f9f7e862bafd8da82 (diff)
downloadnextcloud-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.vue5
-rw-r--r--apps/files/src/components/FileEntry.vue24
-rw-r--r--apps/files/src/components/FilesListHeader.vue34
-rw-r--r--apps/files/src/components/FilesListVirtual.vue13
-rw-r--r--apps/files/src/main.js9
-rw-r--r--apps/files/src/store/files.ts105
-rw-r--r--apps/files/src/store/index.ts16
-rw-r--r--apps/files/src/store/paths.ts48
-rw-r--r--apps/files/src/store/selection.ts37
-rw-r--r--apps/files/src/types.ts17
-rw-r--r--apps/files/src/views/FilesList.vue41
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">