UNPKG

@nextcloud/upload

Version:
594 lines (573 loc) 18.4 kB
import { AxiosResponse } from 'axios'; import { ComponentOptionsMixin } from 'vue'; import { default as default_2 } from 'p-cancelable'; import { DefineComponent } from 'vue'; import { Entry } from '@nextcloud/files'; import { ExtractPropTypes } from 'vue'; import { Folder } from '@nextcloud/files'; import { Node as Node_2 } from '@nextcloud/files'; import { PropType } from 'vue'; import { TypedEventTarget } from 'typescript-event-target'; export declare interface ConflictPickerOptions { /** * When this is set to true a hint is shown that conflicts in directories are handles recursively * You still need to call this function for each directory separately. */ recursive?: boolean; } export declare type ConflictResolutionResult<T extends File | FileSystemEntry | Node_2> = { selected: T[]; renamed: T[]; }; /** * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors * SPDX-License-Identifier: AGPL-3.0-or-later */ /** * Helpers to generate a file tree when the File and Directory API is used (e.g. Drag and Drop or <input type="file" webkitdirectory>) */ /** * This is a helper class to allow building a file tree for uploading * It allows to create virtual directories */ export declare class Directory extends File { private _originalName; private _path; private _children; constructor(path: string); get size(): number; get lastModified(): number; get originalName(): string; get children(): Array<File | Directory>; get webkitRelativePath(): string; getChild(name: string): File | Directory | null; /** * Add multiple children at once * @param files The files to add */ addChildren(files: Array<File | FileSystemEntry>): Promise<void>; /** * Add a child to the directory. * If it is a nested child the parents will be created if not already exist. * @param file The child to add */ addChild(file: File | FileSystemEntry): Promise<void>; } export declare class Eta extends TypedEventTarget<EtaEventsMap> { /** Bytes done */ private _done; /** Total bytes to do */ private _total; /** Current progress (cached) as interval [0,1] */ private _progress; /** Status of the ETA */ private _status; /** Time of the last update */ private _startTime; /** Total elapsed time for current ETA */ private _elapsedTime; /** Current speed in bytes per second */ private _speed; /** Expected duration to finish in seconds */ private _eta; /** * Cutoff time for the low pass filter of the ETA. * A higher value will consider more history information for calculation, * and thus suppress spikes of the speed, * but will make the overall resposiveness slower. */ private _cutoffTime; constructor(options?: EtaOptions); /** * Add more transferred bytes. * @param done Additional bytes done. */ add(done: number): void; /** * Update the transmission state. * * @param done The new value of transferred bytes. * @param total Optionally also update the total bytes we expect. */ update(done: number, total?: number): void; reset(): void; /** * Pause the ETA calculation. */ pause(): void; /** * Resume the ETA calculation. */ resume(): void; /** * Status of the Eta (paused, active, idle). */ get status(): EtaStatus; /** * Progress (percent done) */ get progress(): number; /** * Estimated time in seconds. */ get time(): number; /** * Human readable version of the estimated time. */ get timeReadable(): string; /** * Transfer speed in bytes per second. * Returns `-1` if not yet estimated. */ get speed(): number; /** * Get the speed in human readable format using file sizes like 10KB/s. * Returns the empty string if not yet estimated. */ get speedReadable(): string; } export declare interface EtaEventsMap { pause: CustomEvent; reset: CustomEvent; resume: CustomEvent; update: CustomEvent; } declare interface EtaOptions { /** Low pass filter cutoff time for smoothing the speed */ cutoffTime?: number; /** Total number of bytes to be expected */ total?: number; /** Start the estimation directly */ start?: boolean; } export declare enum EtaStatus { Idle = 0, Paused = 1, Running = 2 } /** * Get the conflicts between two sets of files * @param {Array<File|FileSystemEntry|Node>} files the incoming files * @param {Node[]} content all the existing files in the directory * @return {boolean} true if there is a conflict */ export declare function getConflicts<T extends File | FileSystemEntry | Node_2>(files: T[], content: Node_2[]): T[]; /** * Get the global Uploader instance. * * Note: If you need a local uploader you can just create a new instance, * this global instance will be shared with other apps. * * @param isPublic Set to true to use public upload endpoint (by default it is auto detected) * @param forceRecreate Force a new uploader instance - main purpose is for testing */ export declare function getUploader(isPublic?: boolean, forceRecreate?: boolean): Uploader; /** * Check if there is a conflict between two sets of files * @param {Array<File|FileSystemEntry|Node>} files the incoming files * @param {Node[]} content all the existing files in the directory * @return {boolean} true if there is a conflict */ export declare function hasConflict(files: (File | FileSystemEntry | Node_2)[], content: Node_2[]): boolean; /** * Interface of the internal Directory class */ export declare type IDirectory = Pick<Directory, keyof Directory>; /** * Open the conflict resolver * @param {string} dirname the directory name * @param {(File|Node)[]} conflicts the incoming files * @param {Node[]} content all the existing files in the directory * @param {ConflictPickerOptions} options Optional settings for the conflict picker * @return {Promise<ConflictResolutionResult>} the selected and renamed files */ export declare function openConflictPicker<T extends File | FileSystemEntry | Node_2>(dirname: string | undefined, conflicts: T[], content: Node_2[], options?: ConflictPickerOptions): Promise<ConflictResolutionResult<T>>; export declare class Upload { private _source; private _file; private _isChunked; private _chunks; private _size; private _uploaded; private _startTime; private _status; private _controller; private _response; constructor(source: string, chunked: boolean | undefined, size: number, file: File); get source(): string; get file(): File; get isChunked(): boolean; get chunks(): number; get size(): number; get startTime(): number; set response(response: AxiosResponse | null); get response(): AxiosResponse | null; get uploaded(): number; /** * Update the uploaded bytes of this upload */ set uploaded(length: number); get status(): number; /** * Update this upload status */ set status(status: UploadStatus); /** * Returns the axios cancel token source */ get signal(): AbortSignal; /** * Cancel any ongoing requests linked to this upload */ cancel(): void; } /** * Upload a file * This will init an Uploader instance if none exists. * You will be able to retrieve it with `getUploader` * * @param {string} destinationPath the destination path * @param {File} file the file to upload * @return {Uploader} the uploader instance */ export declare function upload(destinationPath: string, file: File): Uploader; /** * Helper function to create a conflict resolution callback for the `Uploader.batchUpload` method. * * This creates a callback that will open the conflict picker to resolve the conflicts. * In case of a rename the new name is validated and the invalid filename dialog is shown an error happens there. * * @param contentsCallback Callback to retrieve contents of a given path */ export declare function uploadConflictHandler(contentsCallback: (path: string) => Promise<Node_2[]>): (nodes: Array<File | IDirectory>, path: string) => Promise<Array<File | IDirectory> | false>; export declare class Uploader { private _destinationFolder; private _isPublic; private _customHeaders; private _uploadQueue; private _jobQueue; private _queueSize; private _queueProgress; private _queueStatus; private _eta; private _notifiers; /** * Initialize uploader * * @param {boolean} isPublic are we in public mode ? * @param {Folder} destinationFolder the context folder to operate, relative to the root folder */ constructor(isPublic?: boolean, destinationFolder?: Folder); /** * Get the upload destination path relative to the root folder */ get destination(): Folder; /** * Set the upload destination path relative to the root folder */ set destination(folder: Folder); /** * Get the root folder */ get root(): string; /** * Get registered custom headers for uploads */ get customHeaders(): Record<string, string>; /** * Set a custom header * @param name The header to set * @param value The string value */ setCustomHeader(name: string, value?: string): void; /** * Unset a custom header * @param name The header to unset */ deleteCustomerHeader(name: string): void; /** * Get the upload queue */ get queue(): Upload[]; private reset; /** * Pause any ongoing upload(s) */ pause(): void; /** * Resume any pending upload(s) */ start(): void; /** * Get the estimation for the uploading time. */ get eta(): Eta; /** * Get the upload queue stats */ get info(): { size: number; progress: number; status: UploaderStatus; }; private updateStats; addNotifier(notifier: (upload: Upload) => void): void; /** * Notify listeners of the upload completion * @param upload The upload that finished */ private _notifyAll; /** * Uploads multiple files or folders while preserving the relative path (if available) * @param {string} destination The destination path relative to the root folder. e.g. /foo/bar (a file "a.txt" will be uploaded then to "/foo/bar/a.txt") * @param {Array<File|FileSystemEntry>} files The files and/or folders to upload * @param {Function} callback Callback that receives the nodes in the current folder and the current path to allow resolving conflicts, all nodes that are returned will be uploaded (if a folder does not exist it will be created) * @return Cancelable promise that resolves to an array of uploads * * @example * ```ts * // For example this is from handling the onchange event of an input[type=file] * async handleFiles(files: File[]) { * this.uploads = await this.uploader.batchUpload('uploads', files, this.handleConflicts) * } * * async handleConflicts(nodes: File[], currentPath: string) { * const conflicts = getConflicts(nodes, this.fetchContent(currentPath)) * if (conflicts.length === 0) { * // No conflicts so upload all * return nodes * } else { * // Open the conflict picker to resolve conflicts * try { * const { selected, renamed } = await openConflictPicker(currentPath, conflicts, this.fetchContent(currentPath), { recursive: true }) * return [...selected, ...renamed] * } catch (e) { * return false * } * } * } * ``` */ batchUpload(destination: string, files: (File | FileSystemEntry)[], callback?: (nodes: Array<File | IDirectory>, currentPath: string) => Promise<Array<File | IDirectory> | false>): default_2<Upload[]>; /** * Helper to create a directory wrapped inside an Upload class * @param destination Destination where to create the directory * @param directory The directory to create * @param client The cached WebDAV client */ private createDirectory; private uploadDirectory; /** * Upload a file to the given path * @param {string} destination the destination path relative to the root folder. e.g. /foo/bar.txt * @param {File|FileSystemFileEntry} fileHandle the file to upload * @param {string} root the root folder to upload to * @param retries number of retries */ upload(destination: string, fileHandle: File | FileSystemFileEntry, root?: string, retries?: number): default_2<Upload>; /** * Create modification time headers if valid value is available. * It can be invalid on Android devices if SD cards with NTFS / FAT are used, * as those files might use the NT epoch for time so the value will be negative. * * @param file The file to upload */ private _mtimeHeader; } export declare enum UploaderStatus { IDLE = 0, UPLOADING = 1, PAUSED = 2 } export declare const UploadPicker: DefineComponent< { accept: { type: PropType<string[]>; default: null; }; disabled: { type: BooleanConstructor; default: boolean; }; multiple: { type: BooleanConstructor; default: boolean; }; /** * Allow to disable the "new"-menu for this UploadPicker instance */ noMenu: { type: BooleanConstructor; default: boolean; }; /** * Allow to disable the button label */ noLabel: { type: BooleanConstructor; default: boolean; }; /** * Make the "New"-button primary color. */ primary: { type: BooleanConstructor; default: boolean; }; destination: { type: typeof Folder; default: undefined; }; allowFolders: { type: BooleanConstructor; default: boolean; }; /** * List of file present in the destination folder * It is also possible to provide a function that takes a relative path to the current directory and returns the content of it * Note: If a function is passed it should return the current base directory when no path or an empty is passed */ content: { type: PropType<Node_2[] | ((relativePath?: string) => Node_2[] | PromiseLike<Node_2[]>)>; default: () => never[]; }; /** * Overwrite forbidden characters (by default the capabilities of the server are used) * @deprecated Deprecated and will be removed in the next major version */ forbiddenCharacters: { type: PropType<string[]>; default: () => never[]; }; }, { t: (original: string, placeholders?: Record<string, string | number>) => string; progressTimeId: string; }, { newFileMenuEntries: Entry[]; openedMenu: boolean; uploadManager: Uploader; }, { menuEntriesUpload(): Entry[]; menuEntriesNew(): Entry[]; menuEntriesOther(): Entry[]; /** * Check whether the current browser supports uploading directories * Hint: This does not check if the current connection supports this, as some browsers require a secure context! */ canUploadFolders(): boolean; queue(): Upload[]; hasFailure(): boolean; isAssembling(): boolean; isUploading(): boolean; isOnlyAssembling(): boolean; isPaused(): boolean; buttonLabel(): string; haveMenu(): boolean; }, { etaTimeAndSpeed(): string; /** * Handle clicking a new-menu entry * @param entry The entry that was clicked */ onClick(entry: Entry): Promise<void>; /** * Trigger file picker * @param uploadFolders Upload folders */ onTriggerPick(uploadFolders?: boolean): void; /** * Helper for backwards compatibility that queries the content of the current directory * @param path The current path */ getContent(path?: string): Promise<Node_2[]>; /** * Start uploading */ onPick(): Promise<void>; resetForm(): void; /** * Cancel ongoing queue */ onCancel(): void; setDestination(destination: Folder): void; onUploadCompletion(upload: Upload): void; onKeyDown(event: KeyboardEvent): void; }, ComponentOptionsMixin, ComponentOptionsMixin, {}, string, Readonly<ExtractPropTypes< { accept: { type: PropType<string[]>; default: null; }; disabled: { type: BooleanConstructor; default: boolean; }; multiple: { type: BooleanConstructor; default: boolean; }; /** * Allow to disable the "new"-menu for this UploadPicker instance */ noMenu: { type: BooleanConstructor; default: boolean; }; /** * Allow to disable the button label */ noLabel: { type: BooleanConstructor; default: boolean; }; /** * Make the "New"-button primary color. */ primary: { type: BooleanConstructor; default: boolean; }; destination: { type: typeof Folder; default: undefined; }; allowFolders: { type: BooleanConstructor; default: boolean; }; /** * List of file present in the destination folder * It is also possible to provide a function that takes a relative path to the current directory and returns the content of it * Note: If a function is passed it should return the current base directory when no path or an empty is passed */ content: { type: PropType<Node_2[] | ((relativePath?: string) => Node_2[] | PromiseLike<Node_2[]>)>; default: () => never[]; }; /** * Overwrite forbidden characters (by default the capabilities of the server are used) * @deprecated Deprecated and will be removed in the next major version */ forbiddenCharacters: { type: PropType<string[]>; default: () => never[]; }; }>>, { destination: Folder; content: Node_2[] | ((relativePath?: string) => Node_2[] | PromiseLike<Node_2[]>); primary: boolean; accept: string[]; allowFolders: boolean; disabled: boolean; multiple: boolean; noMenu: boolean; noLabel: boolean; forbiddenCharacters: string[]; }>; export declare enum UploadStatus { INITIALIZED = 0, UPLOADING = 1, ASSEMBLING = 2, FINISHED = 3, CANCELLED = 4, FAILED = 5 } export { }