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

fix(files): Properly reset all file list filters on view change #49261

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
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 { 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 @@ -489,7 +489,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
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
"@nextcloud/capabilities": "^1.2.0",
"@nextcloud/dialogs": "^6.0.1",
"@nextcloud/event-bus": "^3.3.1",
"@nextcloud/files": "^3.9.0",
"@nextcloud/files": "^3.10.0",
"@nextcloud/initial-state": "^2.2.0",
"@nextcloud/l10n": "^3.1.0",
"@nextcloud/logger": "^3.0.2",
Expand Down
Loading