feat(13-07): implement sequential cloud upload queue with typed pause/resume
- StorageBrowser: add conflict dialog (D-03) and error dialog (D-04) for paused queue states - StorageBrowser: add upload-queue-resolve emit for Keep both / Replace / Skip / Retry / Cancel all actions - StorageBrowser: add upload-queue-list with upload-queue-item rows for remaining queued items - StorageBrowser: suppress UploadProgress in cloud mode (replaced by queue dialogs) - CloudFolderView: replace placeholder onFilesSelected with sequential queue runner - CloudFolderView: handle upload-queue-resolve events for all conflict/error resolution actions - api/cloud.js: add conflictAction param to uploadCloudFile for keep_both/replace paths - api/cloud.js: add downloadCloudFile authorized fallback helper - CloudFolderView.test.js: fix CapturingStub missing name, add uploadCloudFile mock - All 34 queue and thin-view tests pass (StorageBrowser.cloud-queue + CloudFolderView)
This commit is contained in:
@@ -131,8 +131,12 @@ export function testCloudConnection(connectionId) {
|
||||
* D-02: Provider credentials and raw provider URLs are never exposed.
|
||||
* Returns {kind: 'open', url: '<authorized-docuvault-url>'}.
|
||||
* The caller must navigate to the returned URL — never window.open to a provider URL.
|
||||
*
|
||||
* @param {string} connectionId - Connection UUID
|
||||
* @param {string} itemId - Provider item ID (opaque reference)
|
||||
* @param {object} [fileContext] - Optional file metadata context (name, contentType) for display
|
||||
*/
|
||||
export function openCloudFile(connectionId, itemId) {
|
||||
export function openCloudFile(connectionId, itemId, fileContext) {
|
||||
return request(`/api/cloud/connections/${connectionId}/items/${encodeURIComponent(itemId)}/open`)
|
||||
}
|
||||
|
||||
@@ -222,14 +226,32 @@ export function deleteCloudItem(connectionId, itemId) {
|
||||
* @param {string} parentRef - Parent folder provider_item_id
|
||||
* @param {string} filename - Target filename
|
||||
* @param {File|Blob} file - File bytes
|
||||
* @param {'keep_both'|'replace'|undefined} conflictAction - Optional conflict resolution action
|
||||
*/
|
||||
export function uploadCloudFile(connectionId, parentRef, filename, file) {
|
||||
export function uploadCloudFile(connectionId, parentRef, filename, file, conflictAction) {
|
||||
const formData = new FormData()
|
||||
formData.append('file', file, filename)
|
||||
formData.append('parent_ref', parentRef ?? '')
|
||||
formData.append('filename', filename)
|
||||
if (conflictAction) formData.append('conflict_action', conflictAction)
|
||||
return request(`/api/cloud/connections/${connectionId}/items/upload`, {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Download a cloud file through the DocuVault authorized download endpoint.
|
||||
*
|
||||
* D-02: No raw provider URL or credential is exposed.
|
||||
* D-18: Used as a fallback for unsupported preview formats (Office, Workspace).
|
||||
* The authorized endpoint serves bytes through DocuVault's own proxy.
|
||||
*
|
||||
* Returns {kind: 'download', url: '<docuvault-relative-url>'}.
|
||||
*/
|
||||
export function downloadCloudFile(connectionId, itemId) {
|
||||
return request(
|
||||
`/api/cloud/connections/${connectionId}/items/${encodeURIComponent(itemId)}/download`,
|
||||
{ method: 'GET' },
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user