Skip to content

Commit

Permalink
Merge pull request #6065 from owncloud/pagination-to-composable
Browse files Browse the repository at this point in the history
[full-ci] refactor pagination into composable, cleanup store, views and tests
  • Loading branch information
fschade authored Nov 29, 2021
2 parents 8a14548 + aac56f1 commit 9e1ebde
Show file tree
Hide file tree
Showing 47 changed files with 883 additions and 555 deletions.
51 changes: 15 additions & 36 deletions packages/web-app-files/src/components/AppBar/ViewOptions.vue
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
</li>
<li class="files-view-options-list-item">
<oc-page-size
v-model="itemsPerPageModel"
v-model="itemsPerPage"
data-testid="files-pagination-size"
:label="$gettext('Items per page')"
:options="[100, 500, 1000, $gettext('All')]"
Expand All @@ -53,12 +53,24 @@

<script>
import { mapMutations, mapState, mapActions } from 'vuex'
import { watch } from '@vue/composition-api'
import { useRouteQuery, useDefaults } from '../../composables'
export default {
setup() {
const { pagination: paginationDefaults } = useDefaults()
const itemsPerPage = useRouteQuery('items-per-page', paginationDefaults.perPage.value)
watch(itemsPerPage, (v) => {
paginationDefaults.perPage.value = v
})
return {
itemsPerPage
}
},
computed: {
...mapState('Files', ['areHiddenFilesShown']),
...mapState('Files/sidebar', { sidebarClosed: 'closed' }),
...mapState('Files/pagination', ['itemsPerPage']),
viewOptionsButtonLabel() {
return this.$gettext('Display customization options of the files list')
Expand All @@ -81,44 +93,11 @@ export default {
set(value) {
this.SET_HIDDEN_FILES_VISIBILITY(value)
}
},
itemsPerPageModel: {
get() {
return this.itemsPerPage
},
set(value) {
this.updateQuery(value)
}
}
},
watch: {
$route: {
handler(route) {
if (Object.prototype.hasOwnProperty.call(route.query, 'items-per-page')) {
this.SET_ITEMS_PER_PAGE(route.query['items-per-page'])
return
}
this.updateQuery()
},
immediate: true
}
},
methods: {
...mapMutations('Files', ['SET_HIDDEN_FILES_VISIBILITY']),
...mapActions('Files/sidebar', { toggleSidebar: 'toggle' }),
...mapMutations('Files/pagination', ['SET_ITEMS_PER_PAGE']),
updateQuery(limit = this.itemsPerPageModel) {
const query = { ...this.$route.query, 'items-per-page': limit }
this.SET_ITEMS_PER_PAGE(limit)
this.$router.replace({ query }).catch(() => {})
}
...mapActions('Files/sidebar', { toggleSidebar: 'toggle' })
}
}
</script>
Expand Down
14 changes: 9 additions & 5 deletions packages/web-app-files/src/components/FilesList/Pagination.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,16 @@
</template>

<script>
import { mapState, mapGetters } from 'vuex'
export default {
computed: {
...mapState('Files/pagination', ['currentPage']),
...mapGetters('Files/pagination', ['pages'])
props: {
pages: {
type: Number,
required: true
},
currentPage: {
type: Number,
required: true
}
}
}
</script>
46 changes: 28 additions & 18 deletions packages/web-app-files/src/components/Search/List.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<div class="files-search-result">
<no-content-message v-if="!activeFilesCurrentPage.length" class="files-empty" icon="folder">
<no-content-message v-if="!paginatedResources.length" class="files-empty" icon="folder">
<template #message>
<p class="oc-text-muted">
<span v-if="!!$route.query.term" v-translate>No resource found</span>
Expand All @@ -12,7 +12,7 @@
v-else
class="files-table"
:class="{ 'files-table-squashed': false }"
:resources="activeFilesCurrentPage"
:resources="paginatedResources"
:target-route="{ name: 'files-personal' }"
:are-paths-displayed="true"
:are-thumbnails-displayed="displayThumbnails"
Expand All @@ -22,9 +22,9 @@
@rowMounted="rowMounted"
>
<template #footer>
<pagination />
<pagination :pages="paginationPages" :current-page="paginationPage" />
<list-info
v-if="activeFilesCurrentPage.length > 0"
v-if="paginatedResources.length > 0"
class="uk-width-1-1 oc-my-s"
:files="totalFilesCount.files"
:folders="totalFilesCount.folders"
Expand All @@ -36,28 +36,24 @@
</template>

<script>
import { useStore, useRouteQuery, usePagination, useDefaults } from '../../composables'
import { VisibilityObserver } from 'web-pkg/src/observer'
import { ImageType, ImageDimension } from '../../constants'
import NoContentMessage from '../FilesList/NoContentMessage.vue'
import Pagination from '../../components/FilesList/Pagination.vue'
import debounce from 'lodash-es/debounce'
import { mapMutations, mapGetters, mapActions } from 'vuex'
import { computed } from '@vue/composition-api'
import ListInfo from '../FilesList/ListInfo.vue'
import Pagination from '../FilesList/Pagination.vue'
import MixinFileActions from '../../mixins/fileActions'
import MixinFilesListFilter from '../../mixins/filesListFilter'
import MixinFilesListScrolling from '../../mixins/filesListScrolling'
import MixinFilesListPagination from '../../mixins/filesListPagination'
const visibilityObserver = new VisibilityObserver()
export default {
components: { ListInfo, NoContentMessage, Pagination },
mixins: [
MixinFileActions,
MixinFilesListFilter,
MixinFilesListScrolling,
MixinFilesListPagination
],
components: { ListInfo, Pagination, NoContentMessage },
mixins: [MixinFileActions, MixinFilesListFilter, MixinFilesListScrolling],
props: {
searchResults: {
type: Array,
Expand All @@ -66,18 +62,32 @@ export default {
}
}
},
setup() {
const store = useStore()
const { pagination: paginationDefaults } = useDefaults()
const paginationPageQuery = useRouteQuery('page', '1')
const paginationPage = computed(() => parseInt(String(paginationPageQuery.value)))
const paginationPerPageQuery = useRouteQuery('items-per-page', paginationDefaults.perPage.value)
const { items: paginatedResources, total: paginationPages } = usePagination({
page: paginationPage,
perPage: computed(() => parseInt(String(paginationPerPageQuery.value))),
items: computed(() => store.getters['Files/activeFiles'])
})
return {
paginatedResources,
paginationPages,
paginationPage
}
},
computed: {
...mapGetters(['configuration']),
...mapGetters('Files', ['activeFilesCurrentPage', 'totalFilesCount', 'totalFilesSize']),
...mapGetters('Files', ['totalFilesCount', 'totalFilesSize']),
displayThumbnails() {
return !this.configuration.options.disablePreviews
}
},
watch: {
$route: {
handler: '$_filesListPagination_updateCurrentPage',
immediate: true
},
searchResults: {
handler: function () {
this.CLEAR_CURRENT_FILES_LIST()
Expand Down
5 changes: 4 additions & 1 deletion packages/web-app-files/src/composables/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
export * from './useFileListHeaderPosition'
export * from './router'
export * from './store'
export * from './useDefaults'
export * from './useFileListHeaderPosition'
export * from './usePagination'

declare module 'vue/types/vue' {
interface Vue {
Expand Down
2 changes: 2 additions & 0 deletions packages/web-app-files/src/composables/router/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './useRouteQuery'
export * from './useRouter'
25 changes: 25 additions & 0 deletions packages/web-app-files/src/composables/router/useRouteQuery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { customRef, Ref } from '@vue/composition-api'
import { useRouter } from './useRouter'

type QueryValue = string | (string | null)[]

export const useRouteQuery = (name: string, defaultValue?: QueryValue): Ref<QueryValue> => {
const router = useRouter()

return customRef<QueryValue>((track, trigger) => {
router.afterEach((to, from) => to.query[name] !== from.query[name] && trigger())

return {
get() {
track()
return router.currentRoute.query[name] || defaultValue
},
async set(v) {
await router.replace({
query: { [name]: v }
})
trigger()
}
}
})
}
6 changes: 6 additions & 0 deletions packages/web-app-files/src/composables/router/useRouter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { getCurrentInstance } from '@vue/composition-api'
import VueRouter from 'vue-router'

export const useRouter = (): VueRouter => {
return getCurrentInstance().proxy.$router
}
4 changes: 2 additions & 2 deletions packages/web-app-files/src/composables/store/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export * from './mutationSubscription'
export * from './store'
export * from './useMutationSubscription'
export * from './useStore'
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { onMounted, onBeforeUnmount } from '@vue/composition-api'
import { MutationPayload, SubscribeOptions } from 'vuex'
import { useStore } from './store'
import { useStore } from './useStore'

export const useMutationSubscription = <P extends MutationPayload>(
mutations: string[],
Expand Down
38 changes: 38 additions & 0 deletions packages/web-app-files/src/composables/useDefaults/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { customRef, Ref } from '@vue/composition-api'

interface Defaults {
pagination: {
perPage: Ref
}
}

let defaults: Defaults

/**
* interim solution till options // configuration api is in place
*/
export const useDefaults = (): Defaults => {
if (defaults) {
return defaults
}

defaults = {
pagination: {
perPage: customRef<string>((track, trigger) => {
const defaultValue = '100'
return {
get() {
track()
return window.localStorage.getItem('oc_filesPageLimit') || defaultValue
},
set(v) {
window.localStorage.setItem('oc_filesPageLimit', v)
trigger()
}
}
})
}
}

return defaults
}
34 changes: 34 additions & 0 deletions packages/web-app-files/src/composables/usePagination/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { ref, computed, ComputedRef, unref } from '@vue/composition-api'
import { MaybeRef } from '../utils'

interface PaginationOptions<T> {
page: MaybeRef<number>
perPage: MaybeRef<number>
items: MaybeRef<Array<T>>
}

interface PaginationResult<T> {
items: ComputedRef<Array<T>>
total: ComputedRef<number>
}

export function usePagination<T>(options: PaginationOptions<T>): PaginationResult<T> {
const page = ref(options.page)
const perPage = ref(options.perPage)
const total = computed(() => Math.ceil(unref(options.items).length / perPage.value) || 1)
const items = computed(() => {
if (!perPage.value) {
return unref(options.items)
}

const start = (page.value - 1) * perPage.value
const end = start + perPage.value

return unref(options.items).slice(start, end)
})

return {
items,
total
}
}
1 change: 1 addition & 0 deletions packages/web-app-files/src/composables/utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './types'
3 changes: 3 additions & 0 deletions packages/web-app-files/src/composables/utils/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { Ref } from '@vue/composition-api'

export type MaybeRef<T> = T | Ref<T>
4 changes: 2 additions & 2 deletions packages/web-app-files/src/mixins/actions/emptyTrashBin.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { mapActions, mapGetters } from 'vuex'

export default {
computed: {
...mapGetters('Files', ['activeFilesCurrentPage']),
...mapGetters('Files', ['activeFiles']),
$_emptyTrashBin_items() {
return [
{
Expand All @@ -18,7 +18,7 @@ export default {
// empty trash bin is not available for individual resources, but only for the trash bin as a whole
return resources.length === 0
},
isDisabled: () => this.activeFilesCurrentPage.length === 0,
isDisabled: () => this.activeFiles.length === 0,
componentType: 'oc-button',
class: 'oc-files-actions-empty-trash-bin-trigger',
variation: 'danger'
Expand Down
20 changes: 1 addition & 19 deletions packages/web-app-files/src/mixins/filesListFilter.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Registry } from '../services'
import { mapMutations, mapGetters } from 'vuex'
import { mapMutations } from 'vuex'

export default {
mounted() {
Expand Down Expand Up @@ -39,27 +39,9 @@ export default {
this.CLEAR_FILES_SEARCHED()
},
immediate: true
},
pages() {
if (!this.$route.query.page) {
return
}

if (this.$route.query.page <= this.pages) {
return
}

this.$router.push({
name: this.$route.name,
query: { ...this.$route.query, page: this.pages },
params: this.$route.params
})
}
},
methods: {
...mapMutations('Files', ['CLEAR_FILES_SEARCHED', 'LOAD_FILES_SEARCHED'])
},
computed: {
...mapGetters('Files/pagination', ['pages'])
}
}
Loading

0 comments on commit 9e1ebde

Please sign in to comment.