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

feat!(FilePicker): Modernize FilePickerBuilder to directly make use of the Vue based file picker #904

Merged
merged 1 commit into from
Aug 17, 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions l10n/messages.pot
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@ msgstr ""
msgid "All files"
msgstr ""

#: lib/filepicker.ts:183
#: lib/legacy.ts:135
msgid "Choose"
msgstr ""

#: lib/filepicker.ts:171
#: lib/legacy.ts:141
msgid "Copy"
msgstr ""
Expand All @@ -43,6 +45,8 @@ msgstr ""
msgid "Modified"
msgstr ""

#: lib/filepicker.ts:177
#: lib/filepicker.ts:191
#: lib/legacy.ts:147
msgid "Move"
msgstr ""
Expand Down
217 changes: 149 additions & 68 deletions lib/filepicker.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,40 @@
/// <reference types="@nextcloud/typings" />
/**
* @copyright Copyright (c) 2023 Ferdinand Thiessen <[email protected]>
*
* @author Ferdinand Thiessen <[email protected]>
*
* @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 type { IFilePickerButton } from './components/types'
import type { Node } from '@nextcloud/files'

import { spawnDialog } from './utils/dialogs'
import { FilePickerVue } from './components/FilePicker/index'
import { t } from './utils/l10n'

/**
* Type of filter functions to filter the FilePicker's file list
*/
export type FilePickerFilter = (node: Node) => boolean

/**
* @deprecated
*/
export enum FilePickerType {
Choose = 1,
Move = 2,
Expand All @@ -8,70 +43,56 @@ export enum FilePickerType {
Custom = 5,
}

export interface FilePickerButton {
text: string
type?: 'primary' | 'secondary'
/** Passed on the callback as second argument */
id: number
}

export class FilePicker {

private title: string
private multiSelect: boolean
private mimeTypeFiler: string[]
private modal: boolean
private type: FilePickerType
private mimeTypeFilter: string[]
private directoriesAllowed: boolean
private buttons?: FilePickerButton[]
private buttons: IFilePickerButton[]
private path?: string
private filter?: Nextcloud.v24.FilePickerFilter
private filter?: FilePickerFilter

public constructor(title: string,
multiSelect: boolean,
mimeTypeFilter: string[],
modal: boolean,
type: FilePickerType,
directoriesAllowed: boolean,
buttons: IFilePickerButton[],
path?: string,
filter?: Nextcloud.v24.FilePickerFilter,
buttons?: FilePickerButton[]) {
filter?: FilePickerFilter) {
this.title = title
this.multiSelect = multiSelect
this.mimeTypeFiler = mimeTypeFilter
this.modal = modal
this.type = type
this.mimeTypeFilter = mimeTypeFilter
this.directoriesAllowed = directoriesAllowed
this.path = path
this.filter = filter
this.buttons = buttons
}

public async pick(): Promise<[string|string[], FilePickerType]> {
// Async import for module splitting (treeshaking)
const filepicker = (await import('./legacy')).filepicker

return new Promise((resolve) => {
const buttons = this.buttons?.map(button => ({
defaultButton: button.type === 'primary',
label: button.text,
type: button.id,
/**
* Pick files using the FilePicker
*
* @return Promise with array of picked files or rejected promise on close without picking
*/
public async pick(): Promise<string[]> {
return new Promise((resolve, reject) => {
const buttons = this.buttons.map((button) => ({
...button,
callback: (nodes: Node[]) => {
button.callback(nodes)
resolve(nodes.map((node) => node.path))
},
}))

filepicker(
this.title,
(path: string | string[], type: FilePickerType) => resolve([path, type]),
this.multiSelect,
this.mimeTypeFiler,
this.modal,
this.type,
this.path,
{
allowDirectoryChooser: this.directoriesAllowed,
filter: this.filter,
buttons,
},
)
spawnDialog(FilePickerVue, {
allowPickDirectory: this.directoriesAllowed,
buttons,
name: this.title,
path: this.path,
mimetypeFilter: this.mimeTypeFilter,
multiselect: this.multiSelect,
filterFn: this.filter,
}, reject)
})
}

Expand All @@ -81,82 +102,142 @@ export class FilePickerBuilder {

private title: string
private multiSelect = false
private mimeTypeFiler: string[] = []
private modal = true
private type: FilePickerType = FilePickerType.Choose
private mimeTypeFilter: string[] = []
private directoriesAllowed = false
private path?: string
private filter?: Nextcloud.v24.FilePickerFilter
private buttons: FilePickerButton[] = []
private filter?: FilePickerFilter
private buttons: IFilePickerButton[] = []

/**
* Construct a new FilePicker
*
* @param title Title of the FilePicker
*/
public constructor(title: string) {
this.title = title
}

/**
* Enable or disable picking multiple files
*
* @param ms True to enable picking multiple files, false otherwise
*/
public setMultiSelect(ms: boolean): FilePickerBuilder {
this.multiSelect = ms
return this
}

/**
* Add allowed MIME type
*
* @param filter MIME type to allow
*/
public addMimeTypeFilter(filter: string): FilePickerBuilder {
this.mimeTypeFiler.push(filter)
this.mimeTypeFilter.push(filter)
return this
}

/**
* Set allowed MIME types
*
* @param filter Array of allowed MIME types
*/
public setMimeTypeFilter(filter: string[]): FilePickerBuilder {
this.mimeTypeFiler = filter
this.mimeTypeFilter = filter
return this
}

public addButton(button: FilePickerButton): FilePickerBuilder {
/**
* Add a button to the FilePicker
*
* @param button The button
*/
public addButton(button: IFilePickerButton): FilePickerBuilder {
this.buttons.push(button)
return this
}

/**
* @param modal no function
* @deprecated Does not have any effect as the dialog is always modal
* Set FilePicker type based on legacy file picker types
* @param type The legacy filepicker type to emulate
* @deprecated Use `addButton` instead as with setType you do not know which button was pressed
*/
public setModal(modal: boolean): FilePickerBuilder {
this.modal = modal
return this
}

public setType(type: FilePickerType): FilePickerBuilder {
this.type = type
this.buttons = []

if (type === FilePickerType.CopyMove || type === FilePickerType.Copy) {
this.buttons.push({
callback: () => {},
label: t('Copy'),
type: 'primary',
})
} else if (type === FilePickerType.Move) {
this.buttons.push({
callback: () => {},
label: t('Move'),
type: 'primary',
})
} else if (type === FilePickerType.Choose) {
this.buttons.push({
callback: () => {},
label: t('Choose'),
type: 'primary',
})
}

if (type === FilePickerType.CopyMove) {
this.buttons.push({
callback: () => {},
label: t('Move'),
type: 'secondary',
})
}

return this
}

/**
* Allow to pick directories besides files
*
* @param allow True to allow picking directories
*/
public allowDirectories(allow = true): FilePickerBuilder {
this.directoriesAllowed = allow
return this
}

/**
* Set starting path of the FilePicker
*
* @param path Path to start from picking
*/
public startAt(path: string): FilePickerBuilder {
this.path = path
return this
}

public setFilter(filter: Nextcloud.v24.FilePickerFilter): FilePickerBuilder {
/**
* Add filter function to filter file list of FilePicker
*
* @param filter Filter function to apply
*/
public setFilter(filter: FilePickerFilter): FilePickerBuilder {
this.filter = filter
return this
}

/**
* Construct the configured FilePicker
*/
public build(): FilePicker {
if (this.buttons && this.type !== FilePickerType.Custom) {
console.error('FilePickerBuilder: When adding custom buttons the `type` must be set to `FilePickerType.Custom`.')
}

return new FilePicker(
this.title,
this.multiSelect,
this.mimeTypeFiler,
this.modal,
this.type,
this.mimeTypeFilter,
this.directoriesAllowed,
this.buttons,
this.path,
this.filter,
this.buttons,
)
}

Expand Down