Skip to content

Commit

Permalink
fix(files): Properly reset all file list filters on view change
Browse files Browse the repository at this point in the history
Signed-off-by: Ferdinand Thiessen <[email protected]>
  • Loading branch information
susnux committed Nov 19, 2024
1 parent 935f0d2 commit 82e6814
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 71 deletions.
6 changes: 4 additions & 2 deletions apps/files/src/filters/FilenameFilter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
*/

import type { IFileListFilterChip, INode } from '@nextcloud/files'
import { subscribe } from '@nextcloud/event-bus'
import { FileListFilter } from '@nextcloud/files'

/**
Expand All @@ -16,7 +15,6 @@ export class FilenameFilter extends FileListFilter {

constructor() {
super('files:filename', 5)
subscribe('files:navigation:changed', () => this.updateQuery(''))
}

public filter(nodes: INode[]): INode[] {
Expand All @@ -27,6 +25,10 @@ export class FilenameFilter extends FileListFilter {
})
}

public reset(): void {
this.updateQuery('')
}

public updateQuery(query: string) {
query = (query || '').trim()

Expand Down
6 changes: 4 additions & 2 deletions apps/files/src/filters/ModifiedFilter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
*/
import type { IFileListFilterChip, INode } from '@nextcloud/files'

import { subscribe } from '@nextcloud/event-bus'
import { FileListFilter, registerFileListFilter } from '@nextcloud/files'
import { t } from '@nextcloud/l10n'
import Vue from 'vue'
Expand Down Expand Up @@ -58,7 +57,6 @@ class ModifiedFilter extends FileListFilter {

constructor() {
super('files:modified', 50)
subscribe('files:navigation:changed', () => this.setPreset())
}

public mount(el: HTMLElement) {
Expand All @@ -85,6 +83,10 @@ class ModifiedFilter extends FileListFilter {
return nodes.filter((node) => node.mtime === undefined || this.currentPreset!.filter(node.mtime.getTime()))
}

public reset(): void {
this.setPreset()
}

public setPreset(preset?: ITimePreset) {
this.currentPreset = preset
this.filterUpdated()
Expand Down
6 changes: 4 additions & 2 deletions apps/files/src/filters/TypeFilter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
*/
import type { IFileListFilterChip, INode } from '@nextcloud/files'

import { subscribe } from '@nextcloud/event-bus'
import { FileListFilter, registerFileListFilter } from '@nextcloud/files'
import { t } from '@nextcloud/l10n'
import Vue from 'vue'
Expand Down Expand Up @@ -94,7 +93,6 @@ class TypeFilter extends FileListFilter {

constructor() {
super('files:type', 10)
subscribe('files:navigation:changed', () => this.setPreset())
}

public async mount(el: HTMLElement) {
Expand Down Expand Up @@ -141,6 +139,10 @@ class TypeFilter extends FileListFilter {
})
}

public reset(): void {
this.setPreset()
}

public setPreset(presets?: ITypePreset[]) {
this.currentPresets = presets
this.filterUpdated()
Expand Down
187 changes: 123 additions & 64 deletions apps/files/src/store/filters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,74 +7,133 @@ import { subscribe } from '@nextcloud/event-bus'
import { getFileListFilters } from '@nextcloud/files'
import { defineStore } from 'pinia'
import logger from '../logger'
import { computed, ref } from 'vue'

export const useFiltersStore = defineStore('filters', {
state: () => ({
chips: {} as Record<string, IFileListFilterChip[]>,
filters: [] as IFileListFilter[],
filtersChanged: false,
}),

getters: {
/**
* Currently active filter chips
* @param state Internal state
*/
activeChips(state): IFileListFilterChip[] {
return Object.values(state.chips).flat()
},

/**
* Filters sorted by order
* @param state Internal state
*/
sortedFilters(state): IFileListFilter[] {
return state.filters.sort((a, b) => a.order - b.order)
},

/**
* All filters that provide a UI for visual controlling the filter state
*/
filtersWithUI(): Required<IFileListFilter>[] {
return this.sortedFilters.filter((filter) => 'mount' in filter) as Required<IFileListFilter>[]
},
},

actions: {
addFilter(filter: IFileListFilter) {
filter.addEventListener('update:chips', this.onFilterUpdateChips)
filter.addEventListener('update:filter', this.onFilterUpdate)
this.filters.push(filter)
logger.debug('New file list filter registered', { id: filter.id })
},

removeFilter(filterId: string) {
const index = this.filters.findIndex(({ id }) => id === filterId)
if (index > -1) {
const [filter] = this.filters.splice(index, 1)
filter.removeEventListener('update:chips', this.onFilterUpdateChips)
filter.removeEventListener('update:filter', this.onFilterUpdate)
logger.debug('Files list filter unregistered', { id: filterId })
}
},
/**
* Check if the given value is an instance file list filter with mount function
* @param value The filter to check
*/
function isFileListFilterWithUi(value: IFileListFilter): value is Required<IFileListFilter> {
return 'mount' in value
}

export const useFiltersStore = defineStore('filters', () => {
const chips = ref<Record<string, IFileListFilterChip[]>>({})
const filters = ref<IFileListFilter[]>([])
const filtersChanged = ref(false)


Check failure on line 25 in apps/files/src/store/filters.ts

View workflow job for this annotation

GitHub Actions / NPM lint

More than 1 blank line not allowed
/**
* Currently active filter chips
*/
const activeChips = computed<IFileListFilterChip[]>(
() => Object.values(chips.value).flat(),
)

/**
* Filters sorted by order
*/
const sortedFilters = computed<IFileListFilter[]>(
() => filters.value.sort((a, b) => a.order - b.order),
)

/**
* All filters that provide a UI for visual controlling the filter state
*/
const filtersWithUI = computed<Required<IFileListFilter>[]>(
() => sortedFilters.value.filter(isFileListFilterWithUi)
)

/**
* Register a new filter on the store.
* This will subscribe the store to the filters events.
*
* @param filter The filter to add
*/
function addFilter(filter: IFileListFilter) {
filter.addEventListener('update:chips', onFilterUpdateChips)
filter.addEventListener('update:filter', onFilterUpdate)

onFilterUpdate() {
this.filtersChanged = true
},
filters.value.push(filter)
logger.debug('New file list filter registered', { id: filter.id })
}

onFilterUpdateChips(event: FilterUpdateChipsEvent) {
const id = (event.target as IFileListFilter).id
this.chips = { ...this.chips, [id]: [...event.detail] }
/**
* Unregister a filter from the store.
* This will remove the filter from the store and unsubscribe the store from the filer events.
* @param filterId Id of the filter to remove
*/
function removeFilter(filterId: string) {
const index = filters.value.findIndex(({ id }) => id === filterId)
if (index > -1) {
const [filter] = filters.value.splice(index, 1)
filter.removeEventListener('update:chips', onFilterUpdateChips)
filter.removeEventListener('update:filter', onFilterUpdate)
logger.debug('Files list filter unregistered', { id: filterId })
}
}

logger.debug('File list filter chips updated', { filter: id, chips: event.detail })
},
/**
* Event handler for filter update events
* @private
*/
function onFilterUpdate() {
filtersChanged.value = true
}

init() {
subscribe('files:filter:added', this.addFilter)
subscribe('files:filter:removed', this.removeFilter)
for (const filter of getFileListFilters()) {
this.addFilter(filter)
/**
* Event handler for filter chips updates
* @param event The update event
* @private
*/
function onFilterUpdateChips(event: FilterUpdateChipsEvent) {
const id = (event.target as IFileListFilter).id
chips.value = {
...chips.value,
[id]: [...event.detail],
}

logger.debug('File list filter chips updated', { filter: id, chips: event.detail })
}

/**
* Event handler that resets all filters if the file list view was changed.
* @private
*/
function onViewChanged() {
logger.debug('Reset all file list filters - view changed')

for (const filter of filters.value) {
if (filter.reset !== undefined) {
filter.reset()
}
},
},
}
}

// Initialize the store
{

Check failure on line 114 in apps/files/src/store/filters.ts

View workflow job for this annotation

GitHub Actions / NPM lint

Nested block is redundant
subscribe('files:filter:added', addFilter)
subscribe('files:filter:removed', removeFilter)
for (const filter of getFileListFilters()) {
addFilter(filter)
}

subscribe('files:navigation:changed', onViewChanged)
}

return {
// state
chips,
filters,
filtersWithUI,
filtersChanged,

// getters / computed
activeChips,
sortedFilters,

// actions / methods
addFilter,
removeFilter,
}
})
1 change: 0 additions & 1 deletion apps/files/src/views/FilesList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -522,7 +522,6 @@ export default defineComponent({
},
mounted() {
this.filtersStore.init()
this.fetchContent()
subscribe('files:node:deleted', this.onNodeDeleted)
Expand Down
4 changes: 4 additions & 0 deletions apps/files_sharing/src/files_filters/AccountFilter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ class AccountFilter extends FileListFilter {
})
}

public reset(): void {
this.currentInstance?.resetFilter()
}

public setAccounts(accounts?: IAccountData[]) {
this.filterAccounts = accounts
let chips: IFileListFilterChip[] = []
Expand Down

0 comments on commit 82e6814

Please sign in to comment.