From b4e71ad0fb282fbc9981924b9783ae6659a9e4fe Mon Sep 17 00:00:00 2001 From: John Molakvoæ Date: Fri, 18 Aug 2023 10:59:14 +0200 Subject: chore: use Navigation from `@nextcloud/files` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: John Molakvoæ --- apps/files/src/actions/deleteAction.spec.ts | 7 +- apps/files/src/actions/deleteAction.ts | 7 +- apps/files/src/actions/downloadAction.spec.ts | 8 +- apps/files/src/actions/downloadAction.ts | 9 +- apps/files/src/actions/editLocallyAction.spec.ts | 7 +- apps/files/src/actions/favoriteAction.spec.ts | 9 +- apps/files/src/actions/favoriteAction.ts | 9 +- apps/files/src/actions/openFolderAction.spec.ts | 6 +- apps/files/src/actions/openFolderAction.ts | 5 +- apps/files/src/actions/openInFilesAction.spec.ts | 7 +- apps/files/src/actions/renameAction.spec.ts | 5 +- apps/files/src/actions/sidebarAction.spec.ts | 6 +- apps/files/src/actions/sidebarAction.ts | 6 +- apps/files/src/actions/viewInFolderAction.spec.ts | 5 +- apps/files/src/actions/viewInFolderAction.ts | 6 +- apps/files/src/components/FilesListHeader.vue | 1 - apps/files/src/main.ts | 6 +- apps/files/src/mixins/filesSorting.ts | 6 +- apps/files/src/services/Navigation.ts | 241 --------------------- apps/files/src/views/FilesList.vue | 7 +- apps/files/src/views/Navigation.cy.ts | 37 ++-- apps/files/src/views/Navigation.vue | 20 +- apps/files/src/views/favorites.spec.ts | 9 +- apps/files/src/views/favorites.ts | 17 +- apps/files/src/views/files.ts | 9 +- apps/files/src/views/recent.ts | 9 +- .../src/actions/enterCredentialsAction.spec.ts | 7 +- .../src/actions/openInFilesAction.spec.ts | 7 +- apps/files_external/src/main.ts | 17 +- .../files_external/src/services/externalStorage.ts | 3 +- .../src/actions/acceptShareAction.spec.ts | 7 +- .../files_sharing/src/actions/acceptShareAction.ts | 5 +- .../src/actions/openInFilesAction.spec.ts | 10 +- .../src/actions/rejectShareAction.spec.ts | 7 +- .../files_sharing/src/actions/rejectShareAction.ts | 5 +- .../src/actions/restoreShareAction.spec.ts | 7 +- .../src/actions/restoreShareAction.ts | 5 +- apps/files_sharing/src/services/SharingService.ts | 3 +- apps/files_sharing/src/views/shares.spec.ts | 7 +- apps/files_sharing/src/views/shares.ts | 29 ++- apps/files_trashbin/src/actions/restoreAction.ts | 5 +- apps/files_trashbin/src/main.ts | 13 +- apps/files_trashbin/src/services/trashbin.ts | 3 +- 43 files changed, 158 insertions(+), 446 deletions(-) delete mode 100644 apps/files/src/services/Navigation.ts diff --git a/apps/files/src/actions/deleteAction.spec.ts b/apps/files/src/actions/deleteAction.spec.ts index 8d99b195c3d..d7b7cd5307d 100644 --- a/apps/files/src/actions/deleteAction.spec.ts +++ b/apps/files/src/actions/deleteAction.spec.ts @@ -21,22 +21,21 @@ */ import { action } from './deleteAction' import { expect } from '@jest/globals' -import { File, Folder, Permission } from '@nextcloud/files' +import { File, Folder, Permission, View } from '@nextcloud/files' import { FileAction } from '../services/FileAction' import * as eventBus from '@nextcloud/event-bus' import axios from '@nextcloud/axios' import logger from '../logger' -import type { Navigation } from '../services/Navigation' const view = { id: 'files', name: 'Files', -} as Navigation +} as View const trashbinView = { id: 'trashbin', name: 'Trashbin', -} as Navigation +} as View describe('Delete action conditions tests', () => { test('Default values', () => { diff --git a/apps/files/src/actions/deleteAction.ts b/apps/files/src/actions/deleteAction.ts index 52dd2f53491..6fd8fc467e3 100644 --- a/apps/files/src/actions/deleteAction.ts +++ b/apps/files/src/actions/deleteAction.ts @@ -20,18 +20,17 @@ * */ import { emit } from '@nextcloud/event-bus' -import { Permission, Node } from '@nextcloud/files' +import { Permission, Node, View } from '@nextcloud/files' import { translate as t } from '@nextcloud/l10n' import axios from '@nextcloud/axios' import TrashCanSvg from '@mdi/svg/svg/trash-can.svg?raw' import { registerFileAction, FileAction } from '../services/FileAction' import logger from '../logger.js' -import type { Navigation } from '../services/Navigation' export const action = new FileAction({ id: 'delete', - displayName(nodes: Node[], view: Navigation) { + displayName(nodes: Node[], view: View) { return view.id === 'trashbin' ? t('files_trashbin', 'Delete permanently') : t('files', 'Delete') @@ -58,7 +57,7 @@ export const action = new FileAction({ return false } }, - async execBatch(nodes: Node[], view: Navigation, dir: string) { + async execBatch(nodes: Node[], view: View, dir: string) { return Promise.all(nodes.map(node => this.exec(node, view, dir))) }, diff --git a/apps/files/src/actions/downloadAction.spec.ts b/apps/files/src/actions/downloadAction.spec.ts index abe099af3f8..35a4c0a277a 100644 --- a/apps/files/src/actions/downloadAction.spec.ts +++ b/apps/files/src/actions/downloadAction.spec.ts @@ -21,17 +21,13 @@ */ import { action } from './downloadAction' import { expect } from '@jest/globals' -import { File, Folder, Permission } from '@nextcloud/files' +import { File, Folder, Permission, View } from '@nextcloud/files' import { FileAction } from '../services/FileAction' -import * as eventBus from '@nextcloud/event-bus' -import axios from '@nextcloud/axios' -import type { Navigation } from '../services/Navigation' -import logger from '../logger' const view = { id: 'files', name: 'Files', -} as Navigation +} as View describe('Download action conditions tests', () => { test('Default values', () => { diff --git a/apps/files/src/actions/downloadAction.ts b/apps/files/src/actions/downloadAction.ts index 13fcde61063..bf4e05ec8b0 100644 --- a/apps/files/src/actions/downloadAction.ts +++ b/apps/files/src/actions/downloadAction.ts @@ -19,13 +19,12 @@ * along with this program. If not, see . * */ -import { Permission, Node, FileType } from '@nextcloud/files' +import { Permission, Node, FileType, View } from '@nextcloud/files' import { translate as t } from '@nextcloud/l10n' import ArrowDownSvg from '@mdi/svg/svg/arrow-down.svg?raw' -import { registerFileAction, FileAction, DefaultType } from '../services/FileAction' +import { registerFileAction, FileAction } from '../services/FileAction' import { generateUrl } from '@nextcloud/router' -import type { Navigation } from '../services/Navigation' const triggerDownload = function(url: string) { const hiddenElement = document.createElement('a') @@ -55,7 +54,7 @@ export const action = new FileAction({ .every(permission => (permission & Permission.READ) !== 0) }, - async exec(node: Node, view: Navigation, dir: string) { + async exec(node: Node, view: View, dir: string) { if (node.type === FileType.Folder) { downloadNodes(dir, [node]) return null @@ -65,7 +64,7 @@ export const action = new FileAction({ return null }, - async execBatch(nodes: Node[], view: Navigation, dir: string) { + async execBatch(nodes: Node[], view: View, dir: string) { if (nodes.length === 1) { this.exec(nodes[0], view, dir) return [null] diff --git a/apps/files/src/actions/editLocallyAction.spec.ts b/apps/files/src/actions/editLocallyAction.spec.ts index f40b3b558db..3d2c31c468e 100644 --- a/apps/files/src/actions/editLocallyAction.spec.ts +++ b/apps/files/src/actions/editLocallyAction.spec.ts @@ -21,16 +21,15 @@ */ import { action } from './editLocallyAction' import { expect } from '@jest/globals' -import { File, Permission } from '@nextcloud/files' -import { DefaultType, FileAction } from '../services/FileAction' +import { File, Permission, View } from '@nextcloud/files' +import { FileAction } from '../services/FileAction' import * as ncDialogs from '@nextcloud/dialogs' import axios from '@nextcloud/axios' -import type { Navigation } from '../services/Navigation' const view = { id: 'files', name: 'Files', -} as Navigation +} as View describe('Edit locally action conditions tests', () => { test('Default values', () => { diff --git a/apps/files/src/actions/favoriteAction.spec.ts b/apps/files/src/actions/favoriteAction.spec.ts index 57957e67a33..b24984dfdc2 100644 --- a/apps/files/src/actions/favoriteAction.spec.ts +++ b/apps/files/src/actions/favoriteAction.spec.ts @@ -19,25 +19,24 @@ * along with this program. If not, see . * */ -import * as favoriteAction from './favoriteAction' import { action } from './favoriteAction' import { expect } from '@jest/globals' -import { File, Permission } from '@nextcloud/files' +import { File, Permission, View } from '@nextcloud/files' import { FileAction } from '../services/FileAction' import * as eventBus from '@nextcloud/event-bus' +import * as favoriteAction from './favoriteAction' import axios from '@nextcloud/axios' -import type { Navigation } from '../services/Navigation' import logger from '../logger' const view = { id: 'files', name: 'Files', -} as Navigation +} as View const favoriteView = { id: 'favorites', name: 'Favorites', -} as Navigation +} as View global.window.OC = { TAG_FAVORITE: '_$!!$_', diff --git a/apps/files/src/actions/favoriteAction.ts b/apps/files/src/actions/favoriteAction.ts index a33aacf4947..7a067bb9407 100644 --- a/apps/files/src/actions/favoriteAction.ts +++ b/apps/files/src/actions/favoriteAction.ts @@ -21,7 +21,7 @@ */ import { emit } from '@nextcloud/event-bus' import { generateUrl } from '@nextcloud/router' -import { Permission, type Node } from '@nextcloud/files' +import { Permission, type Node, View } from '@nextcloud/files' import { translate as t } from '@nextcloud/l10n' import axios from '@nextcloud/axios' import Vue from 'vue' @@ -31,14 +31,13 @@ import StarSvg from '@mdi/svg/svg/star.svg?raw' import { registerFileAction, FileAction } from '../services/FileAction' import logger from '../logger.js' -import type { Navigation } from '../services/Navigation' // If any of the nodes is not favorited, we display the favorite action. const shouldFavorite = (nodes: Node[]): boolean => { return nodes.some(node => node.attributes.favorite !== 1) } -export const favoriteNode = async (node: Node, view: Navigation, willFavorite: boolean): Promise => { +export const favoriteNode = async (node: Node, view: View, willFavorite: boolean): Promise => { try { // TODO: migrate to webdav tags plugin const url = generateUrl('/apps/files/api/v1/files') + node.path @@ -92,11 +91,11 @@ export const action = new FileAction({ && nodes.every(node => node.permissions !== Permission.NONE) }, - async exec(node: Node, view: Navigation) { + async exec(node: Node, view: View) { const willFavorite = shouldFavorite([node]) return await favoriteNode(node, view, willFavorite) }, - async execBatch(nodes: Node[], view: Navigation) { + async execBatch(nodes: Node[], view: View) { const willFavorite = shouldFavorite(nodes) return Promise.all(nodes.map(async node => await favoriteNode(node, view, willFavorite))) }, diff --git a/apps/files/src/actions/openFolderAction.spec.ts b/apps/files/src/actions/openFolderAction.spec.ts index 49fc9a9a63a..ff72f10b4d7 100644 --- a/apps/files/src/actions/openFolderAction.spec.ts +++ b/apps/files/src/actions/openFolderAction.spec.ts @@ -19,10 +19,8 @@ * along with this program. If not, see . * */ -import type { Navigation } from '../services/Navigation' - import { expect } from '@jest/globals' -import { File, Folder, Node, Permission } from '@nextcloud/files' +import { File, Folder, Node, Permission, View } from '@nextcloud/files' import { action } from './openFolderAction' import { DefaultType, FileAction } from '../services/FileAction' @@ -30,7 +28,7 @@ import { DefaultType, FileAction } from '../services/FileAction' const view = { id: 'files', name: 'Files', -} as Navigation +} as View describe('Open folder action conditions tests', () => { test('Default values', () => { diff --git a/apps/files/src/actions/openFolderAction.ts b/apps/files/src/actions/openFolderAction.ts index c0e03b20af0..0d28792f15e 100644 --- a/apps/files/src/actions/openFolderAction.ts +++ b/apps/files/src/actions/openFolderAction.ts @@ -19,11 +19,10 @@ * along with this program. If not, see . * */ -import { Permission, Node, FileType } from '@nextcloud/files' +import { Permission, Node, FileType, View } from '@nextcloud/files' import { translate as t } from '@nextcloud/l10n' import FolderSvg from '@mdi/svg/svg/folder.svg?raw' -import type { Navigation } from '../services/Navigation' import { join } from 'path' import { registerFileAction, FileAction, DefaultType } from '../services/FileAction' @@ -52,7 +51,7 @@ export const action = new FileAction({ && (node.permissions & Permission.READ) !== 0 }, - async exec(node: Node, view: Navigation, dir: string) { + async exec(node: Node, view: View, dir: string) { if (!node || node.type !== FileType.Folder) { return false } diff --git a/apps/files/src/actions/openInFilesAction.spec.ts b/apps/files/src/actions/openInFilesAction.spec.ts index 4fc402c73a8..2097095c9ef 100644 --- a/apps/files/src/actions/openInFilesAction.spec.ts +++ b/apps/files/src/actions/openInFilesAction.spec.ts @@ -21,19 +21,18 @@ */ import { action } from './openInFilesAction' import { expect } from '@jest/globals' -import { File, Folder, Permission } from '@nextcloud/files' +import { File, Folder, Permission, View } from '@nextcloud/files' import { DefaultType, FileAction } from '../../../files/src/services/FileAction' -import type { Navigation } from '../../../files/src/services/Navigation' const view = { id: 'files', name: 'Files', -} as Navigation +} as View const recentView = { id: 'recent', name: 'Recent', -} as Navigation +} as View describe('Open in files action conditions tests', () => { test('Default values', () => { diff --git a/apps/files/src/actions/renameAction.spec.ts b/apps/files/src/actions/renameAction.spec.ts index c4d5d45cde9..8e76bea4b84 100644 --- a/apps/files/src/actions/renameAction.spec.ts +++ b/apps/files/src/actions/renameAction.spec.ts @@ -21,15 +21,14 @@ */ import { action } from './renameAction' import { expect } from '@jest/globals' -import { File, Permission } from '@nextcloud/files' +import { File, Permission, View } from '@nextcloud/files' import { FileAction } from '../services/FileAction' import * as eventBus from '@nextcloud/event-bus' -import type { Navigation } from '../services/Navigation' const view = { id: 'files', name: 'Files', -} as Navigation +} as View describe('Rename action conditions tests', () => { test('Default values', () => { diff --git a/apps/files/src/actions/sidebarAction.spec.ts b/apps/files/src/actions/sidebarAction.spec.ts index 6b33667d1dd..1fb31959944 100644 --- a/apps/files/src/actions/sidebarAction.spec.ts +++ b/apps/files/src/actions/sidebarAction.spec.ts @@ -19,10 +19,8 @@ * along with this program. If not, see . * */ -import type { Navigation } from '../services/Navigation' - import { expect } from '@jest/globals' -import { File, Permission } from '@nextcloud/files' +import { File, Permission, View } from '@nextcloud/files' import { action } from './sidebarAction' import { FileAction } from '../services/FileAction' @@ -31,7 +29,7 @@ import logger from '../logger' const view = { id: 'files', name: 'Files', -} as Navigation +} as View describe('Open sidebar action conditions tests', () => { test('Default values', () => { diff --git a/apps/files/src/actions/sidebarAction.ts b/apps/files/src/actions/sidebarAction.ts index 849cf78368d..52244d9912a 100644 --- a/apps/files/src/actions/sidebarAction.ts +++ b/apps/files/src/actions/sidebarAction.ts @@ -19,9 +19,7 @@ * along with this program. If not, see . * */ -import type { Navigation } from '../services/Navigation' - -import { Permission, type Node } from '@nextcloud/files' +import { Permission, type Node, View } from '@nextcloud/files' import { translate as t } from '@nextcloud/l10n' import InformationSvg from '@mdi/svg/svg/information-variant.svg?raw' @@ -54,7 +52,7 @@ export const action = new FileAction({ return (nodes[0].root?.startsWith('/files/') && nodes[0].permissions !== Permission.NONE) ?? false }, - async exec(node: Node, view: Navigation) { + async exec(node: Node, view: View) { try { // TODO: migrate Sidebar to use a Node instead await window.OCA.Files.Sidebar.open(node.path) diff --git a/apps/files/src/actions/viewInFolderAction.spec.ts b/apps/files/src/actions/viewInFolderAction.spec.ts index 7d61fa4298d..c26eb52e400 100644 --- a/apps/files/src/actions/viewInFolderAction.spec.ts +++ b/apps/files/src/actions/viewInFolderAction.spec.ts @@ -21,14 +21,13 @@ */ import { action } from './viewInFolderAction' import { expect } from '@jest/globals' -import { File, Folder, Node, Permission } from '@nextcloud/files' +import { File, Folder, Node, Permission, View } from '@nextcloud/files' import { FileAction } from '../services/FileAction' -import type { Navigation } from '../services/Navigation' const view = { id: 'files', name: 'Files', -} as Navigation +} as View describe('View in folder action conditions tests', () => { test('Default values', () => { diff --git a/apps/files/src/actions/viewInFolderAction.ts b/apps/files/src/actions/viewInFolderAction.ts index f0c5d2485a3..c8abcbb72f1 100644 --- a/apps/files/src/actions/viewInFolderAction.ts +++ b/apps/files/src/actions/viewInFolderAction.ts @@ -19,12 +19,10 @@ * along with this program. If not, see . * */ -import { Node, FileType, Permission } from '@nextcloud/files' +import { Node, FileType, Permission, View } from '@nextcloud/files' import { translate as t } from '@nextcloud/l10n' import FolderMoveSvg from '@mdi/svg/svg/folder-move.svg?raw' -import type { Navigation } from '../services/Navigation' -import { join } from 'path' import { registerFileAction, FileAction } from '../services/FileAction' export const action = new FileAction({ @@ -53,7 +51,7 @@ export const action = new FileAction({ return node.type === FileType.File }, - async exec(node: Node, view: Navigation, dir: string) { + async exec(node: Node, view: View, dir: string) { if (!node || node.type !== FileType.File) { return false } diff --git a/apps/files/src/components/FilesListHeader.vue b/apps/files/src/components/FilesListHeader.vue index 74dc224a39b..a1ad790da98 100644 --- a/apps/files/src/components/FilesListHeader.vue +++ b/apps/files/src/components/FilesListHeader.vue @@ -50,7 +50,6 @@ export default { }, computed: { enabled() { - console.debug('Enabled', this.header.id) return this.header.enabled(this.currentFolder, this.currentView) }, }, diff --git a/apps/files/src/main.ts b/apps/files/src/main.ts index d0fb3922229..3094656ecc0 100644 --- a/apps/files/src/main.ts +++ b/apps/files/src/main.ts @@ -10,12 +10,13 @@ import './actions/openInFilesAction.js' import './actions/renameAction' import './actions/sidebarAction' import './actions/viewInFolderAction' +import './newMenu/newFolder' import Vue from 'vue' import { createPinia, PiniaVuePlugin } from 'pinia' +import { getNavigation } from '@nextcloud/files' import FilesListView from './views/FilesList.vue' -import { NavigationService } from './services/Navigation' import NavigationView from './views/Navigation.vue' import registerFavoritesView from './views/favorites' import registerRecentView from './views/recent' @@ -47,8 +48,7 @@ Vue.use(PiniaVuePlugin) const pinia = createPinia() // Init Navigation Service -const Navigation = new NavigationService() -Object.assign(window.OCP.Files, { Navigation }) +const Navigation = getNavigation() Vue.prototype.$navigation = Navigation // Init Files App Settings Service diff --git a/apps/files/src/mixins/filesSorting.ts b/apps/files/src/mixins/filesSorting.ts index e766ea631f3..2ec3d94d67e 100644 --- a/apps/files/src/mixins/filesSorting.ts +++ b/apps/files/src/mixins/filesSorting.ts @@ -23,14 +23,14 @@ import Vue from 'vue' import { mapState } from 'pinia' import { useViewConfigStore } from '../store/viewConfig' -import type { NavigationService, Navigation } from '../services/Navigation' +import { Navigation, View } from '@nextcloud/files' export default Vue.extend({ computed: { ...mapState(useViewConfigStore, ['getConfig', 'setSortingBy', 'toggleSortingDirection']), - currentView(): Navigation { - return (this.$navigation as NavigationService).active as Navigation + currentView(): View { + return (this.$navigation as Navigation).active as View }, /** diff --git a/apps/files/src/services/Navigation.ts b/apps/files/src/services/Navigation.ts deleted file mode 100644 index 8f8212783ca..00000000000 --- a/apps/files/src/services/Navigation.ts +++ /dev/null @@ -1,241 +0,0 @@ -/** - * @copyright Copyright (c) 2022 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 . - * - */ -/* eslint-disable no-use-before-define */ -import type { Folder, Node } from '@nextcloud/files' -import isSvg from 'is-svg' - -import logger from '../logger.js' - -export type ContentsWithRoot = { - folder: Folder, - contents: Node[] -} - -export interface Column { - /** Unique column ID */ - id: string - /** Translated column title */ - title: string - /** The content of the cell. The element will be appended within */ - render: (node: Node, view: Navigation) => HTMLElement - /** Function used to sort Nodes between them */ - sort?: (nodeA: Node, nodeB: Node) => number - /** - * Custom summary of the column to display at the end of the list. - * Will not be displayed if nothing is provided - */ - summary?: (node: Node[], view: Navigation) => string -} - -export interface Navigation { - /** Unique view ID */ - id: string - /** Translated view name */ - name: string - /** Translated accessible description of the view */ - caption?: string - - /** Translated title of the empty view */ - emptyTitle?: string - /** Translated description of the empty view */ - emptyCaption?: string - - /** - * Method return the content of the provided path - * This ideally should be a cancellable promise. - * promise.cancel(reason) will be called when the directory - * change and the promise is not resolved yet. - * You _must_ also return the current directory - * information alongside with its content. - */ - getContents: (path: string) => Promise - /** The view icon as an inline svg */ - icon: string - /** The view order */ - order: number - - /** - * This view column(s). Name and actions are - * by default always included - */ - columns?: Column[] - /** The empty view element to render your empty content into */ - emptyView?: (div: HTMLDivElement) => void - /** The parent unique ID */ - parent?: string - /** This view is sticky (sent at the bottom) */ - sticky?: boolean - - /** - * This view has children and is expanded or not, - * will be overridden by user config. - */ - expanded?: boolean - - /** - * Will be used as default if the user - * haven't customized their sorting column - */ - defaultSortKey?: string -} - -export class NavigationService { - - private _views: Navigation[] = [] - private _currentView: Navigation | null = null - - constructor() { - logger.debug('Navigation service initialized') - } - - register(view: Navigation) { - try { - isValidNavigation(view) - isUniqueNavigation(view, this._views) - } catch (e) { - if (e instanceof Error) { - logger.error(e.message, { view }) - } - throw e - } - - this._views.push(view) - } - - remove(id: string) { - const index = this._views.findIndex(view => view.id === id) - if (index !== -1) { - this._views.splice(index, 1) - } - } - - get views(): Navigation[] { - return this._views - } - - setActive(view: Navigation | null) { - this._currentView = view - } - - get active(): Navigation | null { - return this._currentView - } - -} - -/** - * Make sure the given view is unique - * and not already registered. - */ -const isUniqueNavigation = function(view: Navigation, views: Navigation[]): boolean { - if (views.find(search => search.id === view.id)) { - throw new Error(`Navigation id ${view.id} is already registered`) - } - return true -} - -/** - * Typescript cannot validate an interface. - * Please keep in sync with the Navigation interface requirements. - */ -const isValidNavigation = function(view: Navigation): boolean { - if (!view.id || typeof view.id !== 'string') { - throw new Error('Navigation id is required and must be a string') - } - - if (!view.name || typeof view.name !== 'string') { - throw new Error('Navigation name is required and must be a string') - } - - if (view.columns && view.columns.length > 0 - && (!view.caption || typeof view.caption !== 'string')) { - throw new Error('Navigation caption is required for top-level views and must be a string') - } - - if (!view.getContents || typeof view.getContents !== 'function') { - throw new Error('Navigation getContents is required and must be a function') - } - - if (!view.icon || typeof view.icon !== 'string' || !isSvg(view.icon)) { - throw new Error('Navigation icon is required and must be a valid svg string') - } - - if (!('order' in view) || typeof view.order !== 'number') { - throw new Error('Navigation order is required and must be a number') - } - - // Optional properties - if (view.columns) { - view.columns.forEach(isValidColumn) - } - - if (view.emptyView && typeof view.emptyView !== 'function') { - throw new Error('Navigation emptyView must be a function') - } - - if (view.parent && typeof view.parent !== 'string') { - throw new Error('Navigation parent must be a string') - } - - if ('sticky' in view && typeof view.sticky !== 'boolean') { - throw new Error('Navigation sticky must be a boolean') - } - - if ('expanded' in view && typeof view.expanded !== 'boolean') { - throw new Error('Navigation expanded must be a boolean') - } - - if (view.defaultSortKey && typeof view.defaultSortKey !== 'string') { - throw new Error('Navigation defaultSortKey must be a string') - } - - return true -} - -/** - * Typescript cannot validate an interface. - * Please keep in sync with the Column interface requirements. - */ -const isValidColumn = function(column: Column): boolean { - if (!column.id || typeof column.id !== 'string') { - throw new Error('A column id is required') - } - - if (!column.title || typeof column.title !== 'string') { - throw new Error('A column title is required') - } - - if (!column.render || typeof column.render !== 'function') { - throw new Error('A render function is required') - } - - // Optional properties - if (column.sort && typeof column.sort !== 'function') { - throw new Error('Column sortFunction must be a function') - } - - if (column.summary && typeof column.summary !== 'function') { - throw new Error('Column summary must be a function') - } - - return true -} diff --git a/apps/files/src/views/FilesList.vue b/apps/files/src/views/FilesList.vue index eb38d09e290..8aa93dd41a5 100644 --- a/apps/files/src/views/FilesList.vue +++ b/apps/files/src/views/FilesList.vue @@ -64,10 +64,9 @@