Skip to content

Commit 8b2be4f

Browse files
authored
Merge pull request #1483 from nextcloud-libraries/feat/registry
feat(registry): add new registry to keep track of registration changes
2 parents 1772f17 + 6465083 commit 8b2be4f

19 files changed

Lines changed: 283 additions & 202 deletions

__tests__/actions/fileAction.spec.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import type { Folder, Node } from '../../lib/node/index.ts'
99

1010
import { beforeEach, describe, expect, test, vi } from 'vitest'
1111
import { DefaultType, getFileActions, registerFileAction } from '../../lib/actions/index.ts'
12+
import { getRegistry } from '../../lib/registry.ts'
1213
import logger from '../../lib/utils/logger.ts'
1314

1415
const folder = {} as Folder
@@ -43,6 +44,24 @@ describe('FileActions init', () => {
4344
expect(getFileActions()[0]).toStrictEqual(action)
4445
})
4546

47+
test('register FileAction emits registry event', () => {
48+
const callback = vi.fn()
49+
const action: IFileAction = {
50+
id: 'test',
51+
displayName: () => 'Test',
52+
iconSvgInline: () => '<svg></svg>',
53+
exec: async () => true,
54+
}
55+
56+
getRegistry().addEventListener('register:action', callback)
57+
registerFileAction(action)
58+
59+
expect(callback).toHaveBeenCalled()
60+
expect(callback.mock.calls[0][0]).toBeInstanceOf(CustomEvent)
61+
expect(callback.mock.calls[0][0].type).toBe('register:action')
62+
expect(callback.mock.calls[0][0].detail).toBe(action)
63+
})
64+
4665
test('getFileActions() returned array is reactive', () => {
4766
logger.debug = vi.fn()
4867

__tests__/actions/fileListAction.spec.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import type { Folder } from '../../lib/node/index.ts'
99

1010
import { beforeEach, describe, expect, test, vi } from 'vitest'
1111
import { getFileListActions, registerFileListAction } from '../../lib/actions/fileListAction.ts'
12+
import { getRegistry } from '../../lib/registry.ts'
1213
import logger from '../../lib/utils/logger.ts'
1314

1415
const folder = {} as Folder
@@ -84,6 +85,19 @@ describe('FileListActions init', () => {
8485
expect(logger.error).toHaveBeenCalledWith('FileListAction with id "test" is already registered', { action: testActionB })
8586
expect(getFileListActions()).toHaveLength(1)
8687
})
88+
89+
test('register FileAction emits registry event', () => {
90+
const callback = vi.fn()
91+
const testAction = mockAction('test')
92+
93+
getRegistry().addEventListener('register:listAction', callback)
94+
registerFileListAction(testAction)
95+
96+
expect(callback).toHaveBeenCalled()
97+
expect(callback.mock.calls[0][0]).toBeInstanceOf(CustomEvent)
98+
expect(callback.mock.calls[0][0].type).toBe('register:listAction')
99+
expect(callback.mock.calls[0][0].detail).toBe(testAction)
100+
})
87101
})
88102

89103
describe('Invalid IFileListAction registraction', () => {
Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,18 @@
33
* SPDX-License-Identifier: AGPL-3.0-or-later
44
*/
55

6-
import type { IFileListFilterChip } from '../lib/fileListFilters.ts'
6+
import type { IFileListFilterChip } from '../../lib/filters/index.ts'
77

8-
import { subscribe } from '@nextcloud/event-bus'
98
import { beforeEach, describe, expect, test, vi } from 'vitest'
10-
import { FileListFilter, getFileListFilters, registerFileListFilter, unregisterFileListFilter } from '../lib/fileListFilters.ts'
9+
import { FileListFilter, getFileListFilters, registerFileListFilter, unregisterFileListFilter } from '../../lib/filters/index.ts'
10+
import { getRegistry } from '../../lib/registry.ts'
1111

1212
class TestFilter extends FileListFilter {
1313
public testUpdated() {
1414
this.filterUpdated()
1515
}
1616

17-
public testUpdateChips(chips) {
17+
public testUpdateChips(chips: IFileListFilterChip[]) {
1818
this.updateChips(chips)
1919
}
2020
}
@@ -99,13 +99,14 @@ describe('File list filter functions', () => {
9999
const filter = new FileListFilter('my:id')
100100
const spy = vi.fn()
101101

102-
subscribe('files:filter:added', spy)
102+
getRegistry().addEventListener('register:listFilter', spy)
103103

104104
expect(window._nc_filelist_filters).toBe(undefined)
105105

106106
registerFileListFilter(filter)
107107
expect(spy).toHaveBeenCalled()
108-
expect(spy).toHaveBeenCalledWith(filter)
108+
expect(spy.mock.calls[0][0]).toBeInstanceOf(CustomEvent)
109+
expect(spy.mock.calls[0][0].detail).toBe(filter)
109110
})
110111

111112
test('cannot register a filter twice', () => {
@@ -144,11 +145,12 @@ describe('File list filter functions', () => {
144145
const spy = vi.fn()
145146

146147
registerFileListFilter(filter)
147-
subscribe('files:filter:removed', spy)
148+
getRegistry().addEventListener('unregister:listFilter', spy)
148149

149150
unregisterFileListFilter(filter.id)
150151
expect(spy).toHaveBeenCalled()
151-
expect(spy).toHaveBeenCalledWith(filter.id)
152+
expect(spy.mock.calls[0][0]).toBeInstanceOf(CustomEvent)
153+
expect(spy.mock.calls[0][0].detail).toBe(filter)
152154
})
153155

154156
test('can get registered filters', () => {
Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@
33
* SPDX-License-Identifier: AGPL-3.0-or-later
44
*/
55

6-
import type { Folder } from '../lib/node/index.ts'
6+
import type { IFolder, IView } from '../../lib/index.ts'
77

88
import { beforeEach, describe, expect, test, vi } from 'vitest'
9-
import { getFileListHeaders, Header, registerFileListHeaders } from '../lib/fileListHeaders.ts'
10-
import logger from '../lib/utils/logger.ts'
9+
import { getFileListHeaders, Header, registerFileListHeaders } from '../../lib/headers/index.ts'
10+
import { getRegistry } from '../../lib/registry.ts'
11+
import logger from '../../lib/utils/logger.ts'
1112

1213
describe('FileListHeader init', () => {
1314
beforeEach(() => {
@@ -22,7 +23,7 @@ describe('FileListHeader init', () => {
2223
expect(logger.debug).toHaveBeenCalledTimes(1)
2324
})
2425

25-
test('Initializing FileListHeader', () => {
26+
test('register FileListHeader', () => {
2627
logger.debug = vi.fn()
2728
const header = new Header({
2829
id: 'test',
@@ -34,7 +35,7 @@ describe('FileListHeader init', () => {
3435

3536
expect(header.id).toBe('test')
3637
expect(header.order).toBe(1)
37-
expect(header.enabled!({} as Folder, {})).toBe(true)
38+
expect(header.enabled!({} as IFolder, {} as IView)).toBe(true)
3839

3940
registerFileListHeaders(header)
4041

@@ -44,6 +45,25 @@ describe('FileListHeader init', () => {
4445
expect(logger.debug).toHaveBeenCalled()
4546
})
4647

48+
test('register FileListHeader emits registry event', () => {
49+
logger.debug = vi.fn()
50+
const callback = vi.fn()
51+
const header = new Header({
52+
id: 'test',
53+
order: 1,
54+
enabled: () => true,
55+
render: () => {},
56+
updated: () => {},
57+
})
58+
59+
getRegistry().addEventListener('register:listHeader', callback)
60+
registerFileListHeaders(header)
61+
expect(callback).toHaveBeenCalled()
62+
expect(callback.mock.calls[0][0]).toBeInstanceOf(CustomEvent)
63+
expect(callback.mock.calls[0][0].type).toBe('register:listHeader')
64+
expect(callback.mock.calls[0][0].detail).toBe(header)
65+
})
66+
4767
test('getFileListHeaders() returned array is reactive', () => {
4868
logger.debug = vi.fn()
4969

@@ -178,9 +198,9 @@ describe('FileListHeader exec', () => {
178198
expect(header.render).toBe(render)
179199
expect(header.updated).toBe(updated)
180200

181-
header.enabled!({} as Folder, {})
182-
header.render(null as any as HTMLElement, {} as Folder, {})
183-
header.updated({} as Folder, {})
201+
header.enabled!({} as IFolder, {} as IView)
202+
header.render(null as any as HTMLElement, {} as IFolder, {} as IView)
203+
header.updated({} as IFolder, {} as IView)
184204

185205
expect(enabled).toHaveBeenCalled()
186206
expect(render).toHaveBeenCalled()
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33
* SPDX-License-Identifier: AGPL-3.0-or-later
44
*/
55

6-
import type { NewMenuEntry } from '../lib/newMenu/index.ts'
6+
import type { NewMenuEntry } from '../../lib/newMenu/index.ts'
77

88
import { afterEach, describe, expect, test, vi } from 'vitest'
9-
import { addNewFileMenuEntry, getNewFileMenu, getNewFileMenuEntries } from '../lib/newMenu/index.ts'
10-
import { NewMenu, NewMenuEntryCategory } from '../lib/newMenu/NewMenu.ts'
11-
import { Folder } from '../lib/node/index.ts'
12-
import { Permission } from '../lib/permissions.ts'
13-
import logger from '../lib/utils/logger.ts'
9+
import { addNewFileMenuEntry, getNewFileMenu, getNewFileMenuEntries } from '../../lib/newMenu/index.ts'
10+
import { NewMenu, NewMenuEntryCategory } from '../../lib/newMenu/NewMenu.ts'
11+
import { Folder } from '../../lib/node/index.ts'
12+
import { Permission } from '../../lib/permissions.ts'
13+
import logger from '../../lib/utils/logger.ts'
1414

1515
describe('NewFileMenu init', () => {
1616
test('Initializing NewFileMenu', () => {

lib/actions/fileAction.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import type { ActionContext, ActionContextSingle } from '../types.ts'
77

8+
import { getRegistry } from '../registry.ts'
89
import logger from '../utils/logger.ts'
910

1011
export const DefaultType = Object.freeze({
@@ -134,6 +135,8 @@ export function registerFileAction(action: IFileAction): void {
134135
}
135136

136137
window._nc_fileactions.push(action)
138+
getRegistry()
139+
.dispatchTypedEvent('register:action', new CustomEvent('register:action', { detail: action }))
137140
}
138141

139142
/**

lib/actions/fileListAction.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import type { ViewActionContext } from '../types.ts'
77

8+
import { getRegistry } from '../registry.ts'
89
import logger from '../utils/logger.ts'
910

1011
export interface IFileListAction {
@@ -50,6 +51,8 @@ export function registerFileListAction(action: IFileListAction) {
5051
}
5152

5253
window._nc_filelistactions.push(action)
54+
getRegistry()
55+
.dispatchTypedEvent('register:listAction', new CustomEvent('register:listAction', { detail: action }))
5356
}
5457

5558
/**

lib/dav/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
/* eslint-disable jsdoc/check-tag-names */
21
/*!
32
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
43
* SPDX-License-Identifier: AGPL-3.0-or-later

lib/filters/functions.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*!
2+
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
6+
import type { IFileListFilter } from './listFilters.ts'
7+
8+
import { getRegistry } from '../registry.ts'
9+
10+
/**
11+
* Register a new filter on the file list
12+
*
13+
* This only must be called once to register the filter,
14+
* when the filter state changes you need to call `filterUpdated` on the filter instead.
15+
*
16+
* @param filter The filter to register on the file list
17+
*/
18+
export function registerFileListFilter(filter: IFileListFilter): void {
19+
window._nc_filelist_filters ??= new Map<string, IFileListFilter>()
20+
if (window._nc_filelist_filters.has(filter.id)) {
21+
throw new Error(`File list filter "${filter.id}" already registered`)
22+
}
23+
24+
window._nc_filelist_filters.set(filter.id, filter)
25+
getRegistry()
26+
.dispatchTypedEvent('register:listFilter', new CustomEvent('register:listFilter', { detail: filter }))
27+
}
28+
29+
/**
30+
* Remove a registered filter from the file list
31+
*
32+
* @param filterId The unique ID of the filter to remove
33+
*/
34+
export function unregisterFileListFilter(filterId: string): void {
35+
if (window._nc_filelist_filters && window._nc_filelist_filters.has(filterId)) {
36+
const filter = window._nc_filelist_filters.get(filterId)!
37+
window._nc_filelist_filters.delete(filterId)
38+
getRegistry()
39+
.dispatchTypedEvent('unregister:listFilter', new CustomEvent('unregister:listFilter', { detail: filter }))
40+
}
41+
}
42+
43+
/**
44+
* Get all registered file list filters
45+
*/
46+
export function getFileListFilters(): IFileListFilter[] {
47+
if (!window._nc_filelist_filters) {
48+
return []
49+
}
50+
return [...window._nc_filelist_filters.values()]
51+
}

lib/filters/index.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/*!
2+
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
6+
export type * from './listFilters.ts'
7+
8+
export {
9+
getFileListFilters,
10+
registerFileListFilter,
11+
unregisterFileListFilter,
12+
} from './functions.ts'
13+
export { FileListFilter } from './listFilters.ts'

0 commit comments

Comments
 (0)