From 4e474d4f2984e4a263e843145cdf8d01eb183234 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Fri, 4 Jul 2025 16:52:26 +0530 Subject: [PATCH] Sketch --- .../DownloadStatusNotifications.tsx | 64 +++++++++---------- web/apps/photos/src/pages/gallery.tsx | 7 +- web/apps/photos/src/pages/shared-albums.tsx | 10 ++- web/packages/gallery/services/save.ts | 21 +++++- 4 files changed, 65 insertions(+), 37 deletions(-) diff --git a/web/apps/photos/src/components/DownloadStatusNotifications.tsx b/web/apps/photos/src/components/DownloadStatusNotifications.tsx index 7c0c188438..f83035bdcd 100644 --- a/web/apps/photos/src/components/DownloadStatusNotifications.tsx +++ b/web/apps/photos/src/components/DownloadStatusNotifications.tsx @@ -17,7 +17,11 @@ interface DownloadStatusNotificationsProps { * {@link Notification} component that was showing the save group's status. */ saveGroups: SaveGroup[]; - setAttributesList: (value: SaveGroup[]) => void; + /** + * Called when the user closes the download status associated with the given + * {@link saveGroup}. + */ + onCloseSaveGroup: (saveGroup: SaveGroup) => void; /** * Called when the hidden section should be shown. * @@ -47,17 +51,13 @@ export const DownloadStatusNotifications: React.FC< DownloadStatusNotificationsProps > = ({ saveGroups, - setAttributesList, + onCloseSaveGroup, onShowHiddenSection, onShowCollection, }) => { const { showMiniDialog } = useBaseContext(); - const onClose = (id: number) => { - setAttributesList(saveGroups.filter((attr) => attr.id !== id)); - }; - - const confirmCancelDownload = (attributes: SaveGroup) => { + const confirmCancelDownload = (group: SaveGroup) => showMiniDialog({ title: t("stop_downloads_title"), message: t("stop_downloads_message"), @@ -65,39 +65,37 @@ export const DownloadStatusNotifications: React.FC< text: t("yes_stop_downloads"), color: "critical", action: () => { - attributes?.canceller.abort(); - onClose(attributes.id); + group?.canceller.abort(); + onCloseSaveGroup(group); }, }, cancel: t("no"), }); - }; - const handleClose = (attributes: SaveGroup) => () => { - if (isSaveComplete(attributes)) { - onClose(attributes.id); + const createOnClose = (group: SaveGroup) => () => { + if (isSaveComplete(group)) { + onCloseSaveGroup(group); } else { - confirmCancelDownload(attributes); + confirmCancelDownload(group); } }; - const createHandleOnClick = - (id: number, onShowCollection: (collectionID: number) => void) => - () => { - const attributes = saveGroups.find((attr) => attr.id === id); - const electron = globalThis.electron; - if (electron) { - electron.openDirectory(attributes.downloadDirPath); - } else if (onShowCollection) { - if (attributes.isHidden) { - void onShowHiddenSection().then(() => { - onShowCollection(attributes.collectionID); - }); - } else { - onShowCollection(attributes.collectionID); - } + const createOnClick = (group: SaveGroup) => () => { + const electron = globalThis.electron; + if (electron) { + electron.openDirectory(group.downloadDirPath); + } else if (onShowCollection) { + if (group.isHidden) { + void onShowHiddenSection().then(() => { + onShowCollection(group.collectionID); + }); + } else { + onShowCollection(group.collectionID); } - }; + } else { + return undefined; + } + }; if (!saveGroups) { return <>; @@ -117,7 +115,7 @@ export const DownloadStatusNotifications: React.FC< horizontal="left" sx={{ "&&": { bottom: `${index * 80 + 20}px` } }} open={isSaveStarted(group)} - onClose={handleClose(group)} + onClose={createOnClose(group)} keepOpenOnClick attributes={{ color: isSaveCompleteWithErrors(group) @@ -134,9 +132,7 @@ export const DownloadStatusNotifications: React.FC< count: group.success + group.failed, total: group.total, }), - onClick: onShowCollection - ? createHandleOnClick(group.id, onShowCollection) - : undefined, + onClick: createOnClick(group), }} />, ); diff --git a/web/apps/photos/src/pages/gallery.tsx b/web/apps/photos/src/pages/gallery.tsx index e0e95c0dc7..738508e5bc 100644 --- a/web/apps/photos/src/pages/gallery.tsx +++ b/web/apps/photos/src/pages/gallery.tsx @@ -42,6 +42,7 @@ import { savedAuthToken } from "ente-base/token"; import { FullScreenDropZone } from "ente-gallery/components/FullScreenDropZone"; import { type UploadTypeSelectorIntent } from "ente-gallery/components/Upload"; import type { + SaveGroup, SetFilesDownloadProgressAttributes, SetFilesDownloadProgressAttributesCreator, } from "ente-gallery/services/save"; @@ -631,6 +632,10 @@ const Page: React.FC = () => { }; }; + const handleCloseSaveGroup = useCallback(({id}) => setFilesDownloadProgressAttributesList((groups) => groups.filter((g) => g.id != id)), []); + + + const setFilesDownloadProgressAttributesCreator: SetFilesDownloadProgressAttributesCreator = useCallback((folderName, collectionID, isHidden) => { const id = Math.random(); @@ -997,7 +1002,7 @@ const Page: React.FC = () => { /> diff --git a/web/apps/photos/src/pages/shared-albums.tsx b/web/apps/photos/src/pages/shared-albums.tsx index 030562840f..535060e4bb 100644 --- a/web/apps/photos/src/pages/shared-albums.tsx +++ b/web/apps/photos/src/pages/shared-albums.tsx @@ -135,6 +135,14 @@ export default function PublicCollectionGallery() { setFilesDownloadProgressAttributesList, ] = useState([]); + const handleCloseSaveGroup = useCallback( + ({ id }) => + setFilesDownloadProgressAttributesList((groups) => + groups.filter((g) => g.id != id), + ), + [], + ); + const setFilesDownloadProgressAttributesCreator: SetFilesDownloadProgressAttributesCreator = useCallback((folderName, collectionID, isHidden) => { const id = Math.random(); @@ -574,7 +582,7 @@ export default function PublicCollectionGallery() { /> diff --git a/web/packages/gallery/services/save.ts b/web/packages/gallery/services/save.ts index 1d35e4a7a5..75ea519560 100644 --- a/web/packages/gallery/services/save.ts +++ b/web/packages/gallery/services/save.ts @@ -61,7 +61,16 @@ export interface SaveGroup { folderName: string; collectionID: number; isHidden: boolean; - downloadDirPath: string; + /** + * The path to a directory on the user's file system that was selected by + * the user to save the files in when they initiated the download on the + * desktop app. + * + * This property is only set when running in the context of the desktop app. + * The web app downloads to the user's default downloads folder, and when + * running in the web app this property will not be set. + */ + downloadDirPath?: string; /** * An {@link AbortController} that can be used to cancel the save. */ @@ -70,12 +79,22 @@ export interface SaveGroup { export const isSaveStarted = (group: SaveGroup) => group.total > 0; +/** + * Return `true` if there are no files in this save group that are pending. + */ export const isSaveComplete = ({ total, success, failed }: SaveGroup) => total == success + failed; +/** + * Return `true` if there are no files in this save group that are pending, but + * one or more files had failed to download. + */ export const isSaveCompleteWithErrors = (group: SaveGroup) => group.failed > 0 && isSaveComplete(group); +/** + * Return `true` if this save was cancelled on a user request. + */ export const isSaveCancelled = (group: SaveGroup) => group.canceller.signal.aborted;