diff --git a/__tests__/fileListHeaders.spec.ts b/__tests__/fileListHeaders.spec.ts new file mode 100644 index 00000000..29483db3 --- /dev/null +++ b/__tests__/fileListHeaders.spec.ts @@ -0,0 +1,67 @@ +import { describe, expect, test, beforeEach, vi } from 'vitest' +import { Header, getFileListHeaders, registerFileListHeaders } from '../lib/fileListHeaders' +import logger from '../lib/utils/logger' +import { Folder } from '../lib/files/folder' + +describe('FileListHeader init', () => { + + beforeEach(() => { + delete window._nc_filelistheader + }) + + test('Getting empty uninitialized FileListHeader', () => { + logger.debug = vi.fn() + const headers = getFileListHeaders() + expect(window._nc_filelistheader).toBeUndefined() + expect(headers).toHaveLength(0) + expect(logger.debug).toHaveBeenCalledTimes(0) + }) + + test('Initializing FileListHeader', () => { + logger.debug = vi.fn() + const header = new Header({ + id: 'test', + order: 1, + enabled: () => true, + render: () => {}, + updated: () => {}, + }) + + expect(header.id).toBe('test') + expect(header.order).toBe(1) + expect(header.enabled!({} as Folder, {})).toBe(true) + + registerFileListHeaders(header) + + expect(window._nc_filelistheader).toHaveLength(1) + expect(getFileListHeaders()).toHaveLength(1) + expect(getFileListHeaders()[0]).toStrictEqual(header) + expect(logger.debug).toHaveBeenCalled() + }) + + test('Duplicate Header gets rejected', () => { + logger.error = vi.fn() + const header = new Header({ + id: 'test', + order: 1, + render: () => {}, + updated: () => {}, + }) + + registerFileListHeaders(header) + expect(getFileListHeaders()).toHaveLength(1) + expect(getFileListHeaders()[0]).toStrictEqual(header) + + const header2 = new Header({ + id: 'test', + order: 2, + render: () => {}, + updated: () => {}, + }) + + registerFileListHeaders(header2) + expect(getFileListHeaders()).toHaveLength(1) + expect(getFileListHeaders()[0]).toStrictEqual(header) + expect(logger.error).toHaveBeenCalledWith('Header test already registered', { header: header2 }) + }) +}) diff --git a/lib/fileListHeaders.ts b/lib/fileListHeaders.ts new file mode 100644 index 00000000..421d0262 --- /dev/null +++ b/lib/fileListHeaders.ts @@ -0,0 +1,109 @@ +/** + * @copyright Copyright (c) 2023 John Molakvoæ + * + * @author John Molakvoæ + * + * @license AGPL-3.0-or-later + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +import { Folder } from './files/folder' +import logger from './utils/logger' + +export interface HeaderData { + /** Unique ID */ + id: string + /** Order */ + order: number + /** Condition wether this header is shown or not */ + enabled?: (folder: Folder, view) => boolean + /** Executed when file list is initialized */ + render: (el: HTMLElement, folder: Folder, view) => void + /** Executed when root folder changed */ + updated(folder: Folder, view) +} + +export class Header { + + private _header: HeaderData + + constructor(header: HeaderData) { + this.validateHeader(header) + this._header = header + } + + get id() { + return this._header.id + } + + get order() { + return this._header.order + } + + get enabled() { + return this._header.enabled + } + + get render() { + return this._header.render + } + + get updated() { + return this._header.updated + } + + private validateHeader(header: HeaderData) { + if (!header.id || !header.render || !header.updated) { + throw new Error('Invalid header: id, render and updated are required') + } + + if (typeof header.id !== 'string') { + throw new Error('Invalid id property') + } + + if (header.enabled !== undefined && typeof header.enabled !== 'function') { + throw new Error('Invalid enabled property') + } + + if (header.render && typeof header.render !== 'function') { + throw new Error('Invalid render property') + } + + if (header.updated && typeof header.updated !== 'function') { + throw new Error('Invalid updated property') + } + } + +} + +export const registerFileListHeaders = function(header: Header): void { + if (typeof window._nc_filelistheader === 'undefined') { + window._nc_filelistheader = [] + logger.debug('FileActions initialized') + } + + // Check duplicates + if (window._nc_filelistheader.find(search => search.id === header.id)) { + logger.error(`Header ${header.id} already registered`, { header }) + return + } + + window._nc_filelistheader.push(header) +} + +export const getFileListHeaders = function(): Header[] { + return window._nc_filelistheader || [] +} diff --git a/lib/index.ts b/lib/index.ts index 58cc5957..faa0c032 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -24,7 +24,8 @@ import { type Entry, getNewFileMenu } from './newFileMenu' export { formatFileSize } from './humanfilesize' -export { FileAction, registerFileAction, getFileActions } from './fileAction' +export { FileAction, getFileActions, registerFileAction } from './fileAction' +export { Header, getFileListHeaders, registerFileListHeaders } from './fileListHeaders' export { type Entry } from './newFileMenu' export { Permission } from './permissions' diff --git a/window.d.ts b/window.d.ts index 626d2850..e574a890 100644 --- a/window.d.ts +++ b/window.d.ts @@ -1,17 +1,19 @@ /// +import type { DavProperty } from './lib/dav/davProperties' import type { FileAction } from './lib/fileAction' +import type { Header } from './lib/fileListHeaders' import type { NewFileMenu } from './lib/newFileMenu' -import type { DavProperty } from './lib/dav/davProperties' export {} declare global { interface Window { OC: Nextcloud.v25.OC | Nextcloud.v26.OC | Nextcloud.v27.OC; - _nc_newfilemenu?: NewFileMenu - _nc_fileactions?: FileAction[] - _nc_dav_properties?: string[] _nc_dav_namespaces?: DavProperty + _nc_dav_properties?: string[] + _nc_fileactions?: FileAction[] + _nc_filelistheader?: Header[] + _nc_newfilemenu?: NewFileMenu } }