Skip to content

feat:(PB-6297) download and preview individual items in public shared folder - #2054

Open
victor-ferro wants to merge 12 commits into
PB-6297-thumbnailsfrom
PB-6297-preview-download
Open

feat:(PB-6297) download and preview individual items in public shared folder#2054
victor-ferro wants to merge 12 commits into
PB-6297-thumbnailsfrom
PB-6297-preview-download

Conversation

@victor-ferro

Copy link
Copy Markdown

Description

Third and final part of PB-6297, stacked on #2053. The public shared folder preview becomes actionable: items can now be downloaded individually (or in multi-selection) and files can be previewed in the viewer, all without authentication.

Per-item download

  • downloadPublicSharedItems (new in share.service.ts): generalizes the existing root-folder download to any selection — a single file downloads directly (stream → blob → save), a single folder zips through the public iterators, and a mixed selection packs everything into one zip. downloadPublicSharedFolder is now a thin wrapper over it (same behavior as before for the main Download button).
  • Context menu (right-click / three-dots) with Download on any selection, and a beforeunload warning while a download is in flight.

File preview

  • Double-click (or Preview in the menu) opens the plain FileViewer with the decrypted blob, download progress, and the same gates as ShareFileView: previewable type and size limit — otherwise the viewer opens with its fallback without fetching the blob.
  • TopBarActions gets a dedicated share-view variant: Download + Report (reusing handleReportShare, now exported from ReportButton). This also removes a dead branch: the old copy-link button was unreachable in share views. Phosphor deprecated icon names updated (DotsThreeIcon, WarningCircleIcon).

Cleanups (code created earlier in this stack)

  • usePublicSharedFolderContent: fetchFolders/fetchFiles merged into a single fetchLevelItems(type) — same phase machine (folders per level, then files), ~20 duplicated lines removed. Test assertions updated accordingly.
  • useWarnBeforeUnload (new hook): replaces the three identical handleLeavePage + beforeunload effect copies in ShareFolderView, ShareFileView and the new preview; also fixes the effect depending only on progress instead of the full active condition.
  • isTypeAllowed now reuses getIsTypeAllowedAndFileExtensionGroupValues from fileViewerUtils (also fixes uppercase extensions being rejected).
  • ShareLayout: user menu dropdown gets z-50 so it stays above the new preview content.

Related Issues

Relates to PB-6297.

Related Pull Requests

Checklist

  • Changes have been tested locally.
  • Unit tests have been written or updated as necessary.
  • The code adheres to the repository's coding standards.
  • Relevant documentation has been added or updated.
  • No new warnings or errors have been introduced.
  • SonarCloud issues have been reviewed and addressed.
  • QA Passed

Testing Process

  • Unit tests: yarn vitest run src/app/share/services/share.service.test.ts — three new cases for downloadPublicSharedItems (single file with share credentials/key and plain name, single folder zipped through the public iterators, mixed selection packed into one zip closed at the end). Hook tests updated for the unified fetch.
  • yarn typecheck and yarn lint clean; full unit suite run by the pre-commit hook.
  • Manual: on a public shared folder link — downloaded a single file, a folder, and a mixed multi-selection (zip); previewed images/PDFs via double-click and the context menu; verified non-previewable and oversized files open the viewer fallback without downloading; downloaded from inside the viewer; checked the leave-page warning appears only while downloading; Report button in the viewer opens the report email.

@victor-ferro
victor-ferro force-pushed the PB-6297-preview-download branch from 9c0ef9b to 87247c8 Compare July 17, 2026 16:02
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 17, 2026

Copy link
Copy Markdown

Deploying drive-web with  Cloudflare Pages  Cloudflare Pages

Latest commit: 6f83655
Status: ✅  Deploy successful!
Preview URL: https://2427487e.drive-web.pages.dev
Branch Preview URL: https://pb-6297-preview-download.drive-web.pages.dev

View logs

…terators

New-code coverage was failing the SonarCloud quality gate (75% vs 80%
required). Adds tests for the untested useWarnBeforeUnload hook and for
the previously uncovered lines in share.service.ts: the folders/files
iterator factories passed to downloadFolderAsZip, and the happy/error
paths of downloadPublicSharedFolder.
@victor-ferro victor-ferro self-assigned this Jul 24, 2026
# Conflicts:
#	src/views/PublicShared/hooks/usePublicSharedFolderContent.ts
# Conflicts:
#	src/views/PublicShared/components/PublicSharedFolderContent.tsx
#	src/views/PublicShared/components/PublicSharedItemList.tsx
#	src/views/PublicShared/hooks/usePublicSharedFolderContent.ts
Comment on lines +141 to +155
downloadFile({
bucketId: (shareItem as unknown as DriveFileData).bucket,
fileId: (shareItem as unknown as DriveFileData).fileId,
creds: shareCredentials,
key: publicShareKey,
options: {
notifyProgress: (totalBytes, downloadedBytes) => {
setPreviewProgress(downloadedBytes / totalBytes);
},
},
})
.then((fileStream) => binaryStreamToBlob(fileStream, shareItem.type || ''))
.then((fileBlob) => {
setPreviewBlob(fileBlob);
})

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need to verify that the downloaded item is the same as actually opened, this can cause that user press one item, close instantalnealy, open another, and flash the preview of first item until the second is downloaded

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

if (!isTypeAllowed(shareItem) || !isFileSizePreviewable(Number(shareItem.size))) return;

downloadFile({
bucketId: (shareItem as unknown as DriveFileData).bucket,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

avoid as unknown cast outside of tests

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done! It's been detected that the AdvancedSharedItem type doesn't have fileId because the SharedFiles imported from the SDK has a field called 'fileiId' but doesn't have 'fileId'

Comment on lines +102 to +121
const downloadItems = (itemsToDownload: AdvancedSharedItem[]) => {
if (isDownloading || itemsToDownload.length === 0 || !shareCredentials) return;

setIsDownloading(true);

downloadPublicSharedItems({
items: itemsToDownload,
credentials: shareCredentials,
key: publicShareKey,
code,
resourcesToken: nextLevelToken,
})
.catch((err) => {
errorService.reportError(err);
notificationsService.show({ text: errorService.castError(err).message, type: ToastType.Error });
})
.finally(() => {
setIsDownloading(false);
});
};

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would extract all the download logic added for previewing into a hook

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

return confirmationMessage; // WebKit, Chrome <34
};

const useWarnBeforeUnload = (isActive: boolean): void => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this hook already exists: useBeforeUnload.ts

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hadn't seen it. The functionality I put into the hook was also being used in other places, and I thought there wasn't already an existing hook for this. My bad!!
Done!

return `${item.plainName ?? item.name}${extension}`;
};

export async function downloadPublicSharedItems({

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't the DownloadManager cover this?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, it can be done, but it touches several things in the main download flow. Because for public downloads we'd run into some blockers:

  1. generateTasksForItem calls localStorageService.getUser() and throws 'User not found' before even looking at downloadCredentials, so anonymous visitors crash right away.
  2. Progress is reported through tasksService, and TaskLogger is only mounted in HeaderAndSidenavLayout — an anonymous user would get a download with zero feedback, and the preview UI here doesn't read from tasks.
  3. Single files go through the download worker, and postMessage turns the { bucketKey: Buffer } key (new sharing version) into a plain Uint8Array, which the network crypto won't accept without re-hydrating it in the worker.

If you want me to get started on it, I will. But maybe it might be work for a separate ticket

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, so we’ll have to create a task to decouple DownloadManager from everything that isn’t handle ‘download', because, as we have seen, it does not allow us to reuse it 😞

Moves download and preview state out of PublicSharedFolderContent into
usePublicSharedDownload, discarding stale preview downloads when the
preview is closed or replaced, and removes 'as unknown as' casts by
typing fileId on AdvancedSharedItem and narrowing helper signatures.
@victor-ferro
victor-ferro requested a review from CandelR July 29, 2026 10:11
Comment thread src/hooks/useBeforeUnload.test.ts Outdated
@@ -0,0 +1,102 @@
/**
* @jest-environment jsdom
*/

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we don't use jest, this tag is not necessary

@@ -0,0 +1,267 @@
/**
* @jest-environment jsdom
*/

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here, we don't use jest

@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants