Skip to content

Commit ebc4815

Browse files
authored
Merge pull request #1509 from nextcloud-libraries/feat/merge-uploader
feat(upload): merge upload related API from `@nextcloud/upload`
2 parents 06e2199 + 2c4eb92 commit ebc4815

21 files changed

Lines changed: 2395 additions & 107 deletions

lib/globalScope.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import type {
1313
import type { IFileAction, IFileListAction } from './ui/actions/index.ts'
1414
import type { FilesRegistry } from './ui/registry.ts'
1515
import type { ISidebarAction, ISidebarTab } from './ui/sidebar/index.ts'
16+
import type { Uploader } from './upload/index.ts'
1617

1718
interface InternalGlobalScope {
1819
davNamespaces?: DavProperty
@@ -22,6 +23,8 @@ interface InternalGlobalScope {
2223
navigation?: Navigation
2324
registry?: FilesRegistry
2425

26+
uploader?: Uploader
27+
2528
fileActions?: Map<string, IFileAction>
2629
fileListActions?: Map<string, IFileListAction>
2730
fileListFilters?: Map<string, IFileListFilter>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*!
2+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
6+
import { expect, test } from 'vitest'
7+
import { UploadCancelledError } from './UploadCancelledError.ts'
8+
9+
test('UploadCancelledError', () => {
10+
const cause = new Error('Network error')
11+
const error = new UploadCancelledError(cause)
12+
expect(error).toBeInstanceOf(Error)
13+
expect(error).toBeInstanceOf(UploadCancelledError)
14+
expect(error.message).toBe('Upload has been cancelled')
15+
expect(error.cause).toBe(cause)
16+
expect(error).toHaveProperty('__UPLOAD_CANCELLED__')
17+
})
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/*!
2+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
6+
export class UploadCancelledError extends Error {
7+
__UPLOAD_CANCELLED__ = true
8+
9+
public constructor(cause?: unknown) {
10+
super('Upload has been cancelled', { cause })
11+
}
12+
13+
public static isCancelledError(error: unknown): error is UploadCancelledError {
14+
return typeof error === 'object' && error !== null && (error as UploadCancelledError).__UPLOAD_CANCELLED__ === true
15+
}
16+
}

lib/upload/getUploader.spec.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*!
2+
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
6+
import { join } from '@nextcloud/paths'
7+
import { expect, test } from 'vitest'
8+
import { defaultRemoteURL, defaultRootPath } from '../dav/dav.ts'
9+
import { scopedGlobals } from '../globalScope.ts'
10+
import { Folder } from '../node/folder.ts'
11+
import { getUploader } from './getUploader.ts'
12+
import { Uploader } from './uploader/Uploader.ts'
13+
14+
test('getUploader - should return the uploader instance from the global scope', async () => {
15+
const uploader = new Uploader(false, new Folder({ owner: 'test', root: defaultRootPath, source: join(defaultRemoteURL, defaultRootPath) }))
16+
scopedGlobals.uploader = uploader
17+
const returnedUploader = getUploader()
18+
expect(returnedUploader).toBe(uploader)
19+
})
20+
21+
test('getUploader - should return the same instance on multiple calls', async () => {
22+
const uploader1 = getUploader()
23+
const uploader2 = getUploader()
24+
expect(uploader1).toBe(uploader2)
25+
})
26+
27+
test('getUploader - should not return the same instance on multiple calls with forceRecreate', async () => {
28+
const uploader1 = getUploader(true)
29+
const uploader2 = getUploader(true, true)
30+
expect(uploader1).not.toBe(uploader2)
31+
})

lib/upload/getUploader.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*!
2+
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
6+
import { isPublicShare } from '@nextcloud/sharing/public'
7+
import { scopedGlobals } from '../globalScope.ts'
8+
import { Uploader } from './uploader/Uploader.ts'
9+
10+
/**
11+
* Get the global Uploader instance.
12+
*
13+
* Note: If you need a local uploader you can just create a new instance,
14+
* this global instance will be shared with other apps and is mostly useful
15+
* for the Files app web UI to keep track of all uploads and their progress.
16+
*
17+
* @param isPublic Set to true to use public upload endpoint (by default it is auto detected)
18+
* @param forceRecreate Force a new uploader instance - main purpose is for testing
19+
*/
20+
export function getUploader(isPublic: boolean = isPublicShare(), forceRecreate = false): Uploader {
21+
if (forceRecreate || scopedGlobals.uploader === undefined) {
22+
scopedGlobals.uploader = new Uploader(isPublic)
23+
}
24+
25+
return scopedGlobals.uploader
26+
}

lib/upload/index.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/*!
2+
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
3+
* SPDX-License-Identifier: AGPL-3.0-or-later
4+
*/
5+
6+
export type { Eta, EtaEventsMap } from './uploader/index.ts'
7+
export type { Directory, IDirectory } from './utils/fileTree.ts'
8+
9+
export { getUploader } from './getUploader.ts'
10+
export { Upload, UploadStatus } from './uploader/Upload.ts'
11+
export { EtaStatus, Uploader, UploaderStatus } from './uploader/index.ts'
12+
export { getConflicts, hasConflict } from './utils/conflicts.ts'

0 commit comments

Comments
 (0)