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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add FileListHeader #717

Merged
merged 1 commit into from Aug 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
67 changes: 67 additions & 0 deletions __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)

Check warning on line 32 in __tests__/fileListHeaders.spec.ts

View workflow job for this annotation

GitHub Actions / eslint

Forbidden non-null assertion

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 })
})
})
109 changes: 109 additions & 0 deletions lib/fileListHeaders.ts
@@ -0,0 +1,109 @@
/**
* @copyright Copyright (c) 2023 John Molakvo忙 <skjnldsv@protonmail.com>
*
* @author John Molakvo忙 <skjnldsv@protonmail.com>
*
* @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 <http://www.gnu.org/licenses/>.
*
*/

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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the view be typed somehow?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree we should have the view types (if possible)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we haven't migrated it from server yet! Was maing sure no changes were needed (I had to adjust quite a lot at the beginning)

/** 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 || []
}
3 changes: 2 additions & 1 deletion lib/index.ts
Expand Up @@ -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'

Expand Down Expand Up @@ -62,7 +63,7 @@
/**
* Get the list of registered entries from the upload menu
*
* @param {FileInfo} context the creation context. Usually the current folder FileInfo

Check warning on line 66 in lib/index.ts

View workflow job for this annotation

GitHub Actions / eslint

The type 'FileInfo' is undefined
*/
export const getNewFileMenuEntries = function(context?: object) {
const newFileMenu = getNewFileMenu()
Expand Down
10 changes: 6 additions & 4 deletions window.d.ts
@@ -1,17 +1,19 @@
/// <reference types="@nextcloud/typings" />

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
}
}