diff --git a/web/apps/photos/src/components/PhotoFrame.tsx b/web/apps/photos/src/components/PhotoFrame.tsx index d15a2c42b4..a18242a46c 100644 --- a/web/apps/photos/src/components/PhotoFrame.tsx +++ b/web/apps/photos/src/components/PhotoFrame.tsx @@ -101,7 +101,6 @@ export type PhotoFrameProps = Pick< */ modePlus?: GalleryBarMode | "search"; files: EnteFile[]; - syncWithRemote: () => Promise; setSelected: ( selected: SelectedState | ((selected: SelectedState) => SelectedState), ) => void; @@ -145,6 +144,7 @@ export type PhotoFrameProps = Pick< isInHiddenSection?: boolean; setFilesDownloadProgressAttributesCreator?: SetFilesDownloadProgressAttributesCreator; selectable?: boolean; + onSyncWithRemote: () => Promise; }; /** @@ -154,7 +154,6 @@ const PhotoFrame = ({ mode, modePlus, files, - syncWithRemote, setSelected, selected, favoriteFileIDs, @@ -170,6 +169,7 @@ const PhotoFrame = ({ isInHiddenSection, setFilesDownloadProgressAttributesCreator, selectable, + onSyncWithRemote, onSelectCollection, onSelectPerson, }: PhotoFrameProps) => { @@ -262,8 +262,8 @@ const PhotoFrame = ({ }, [selected]); const handleTriggerSyncWithRemote = useCallback( - () => void syncWithRemote(), - [syncWithRemote], + () => void onSyncWithRemote(), + [onSyncWithRemote], ); const handleSaveEditedImageCopy = useCallback( @@ -303,7 +303,7 @@ const PhotoFrame = ({ throw new Error("Not implemented"); } else { setOpen(false); - needUpdate && syncWithRemote(); + needUpdate && onSyncWithRemote(); setIsPhotoSwipeOpen?.(false); } }; diff --git a/web/apps/photos/src/pages/gallery.tsx b/web/apps/photos/src/pages/gallery.tsx index 5b88e5e9e3..cdacf81631 100644 --- a/web/apps/photos/src/pages/gallery.tsx +++ b/web/apps/photos/src/pages/gallery.tsx @@ -515,88 +515,96 @@ const Page: React.FC = () => { }; }, [selectAll, clearSelection]); - const showSessionExpiredDialog = () => - showMiniDialog(sessionExpiredDialogAttributes(logout)); + const showSessionExpiredDialog = useCallback( + () => showMiniDialog(sessionExpiredDialogAttributes(logout)), + [showMiniDialog, logout], + ); - const syncWithRemote = async (force = false, silent = false) => { - if (!navigator.onLine) return; - if (syncInProgress.current && !force) { - resync.current = { force, silent }; - return; - } - const isForced = syncInProgress.current && force; - syncInProgress.current = true; - try { - const token = getToken(); - if (!token) { + const handleSyncWithRemote = useCallback( + async (force = false, silent = false) => { + if (!navigator.onLine) return; + if (syncInProgress.current && !force) { + resync.current = { force, silent }; return; } - const tokenValid = await isTokenValid(token); - if (!tokenValid) { - throw new Error(CustomError.SESSION_EXPIRED); + const isForced = syncInProgress.current && force; + syncInProgress.current = true; + try { + const token = getToken(); + if (!token) { + return; + } + const tokenValid = await isTokenValid(token); + if (!tokenValid) { + throw new Error(CustomError.SESSION_EXPIRED); + } + !silent && showLoadingBar(); + await preCollectionsAndFilesSync(); + const allCollections = await getAllLatestCollections(); + const [hiddenCollections, collections] = splitByPredicate( + allCollections, + isHiddenCollection, + ); + dispatch({ + type: "setAllCollections", + collections, + hiddenCollections, + }); + const didUpdateNormalFiles = await syncFiles( + "normal", + collections, + (files) => dispatch({ type: "setFiles", files }), + (files) => dispatch({ type: "fetchFiles", files }), + ); + const didUpdateHiddenFiles = await syncFiles( + "hidden", + hiddenCollections, + (hiddenFiles) => + dispatch({ type: "setHiddenFiles", hiddenFiles }), + (hiddenFiles) => + dispatch({ type: "fetchHiddenFiles", hiddenFiles }), + ); + if (didUpdateNormalFiles || didUpdateHiddenFiles) + exportService.onLocalFilesUpdated(); + await syncTrash(allCollections, (trashedFiles: EnteFile[]) => + dispatch({ type: "setTrashedFiles", trashedFiles }), + ); + // syncWithRemote is called with the force flag set to true before + // doing an upload. So it is possible, say when resuming a pending + // upload, that we get two syncWithRemotes happening in parallel. + // + // Do the non-file-related sync only for one of these parallel ones. + if (!isForced) { + await sync(); + } + } catch (e) { + switch (e.message) { + case CustomError.SESSION_EXPIRED: + showSessionExpiredDialog(); + break; + case CustomError.KEY_MISSING: + clearKeys(); + router.push(PAGES.CREDENTIALS); + break; + default: + log.error("syncWithRemote failed", e); + } + } finally { + dispatch({ type: "clearUnsyncedState" }); + !silent && hideLoadingBar(); } - !silent && showLoadingBar(); - await preCollectionsAndFilesSync(); - const allCollections = await getAllLatestCollections(); - const [hiddenCollections, collections] = splitByPredicate( - allCollections, - isHiddenCollection, - ); - dispatch({ - type: "setAllCollections", - collections, - hiddenCollections, - }); - const didUpdateNormalFiles = await syncFiles( - "normal", - collections, - (files) => dispatch({ type: "setFiles", files }), - (files) => dispatch({ type: "fetchFiles", files }), - ); - const didUpdateHiddenFiles = await syncFiles( - "hidden", - hiddenCollections, - (hiddenFiles) => - dispatch({ type: "setHiddenFiles", hiddenFiles }), - (hiddenFiles) => - dispatch({ type: "fetchHiddenFiles", hiddenFiles }), - ); - if (didUpdateNormalFiles || didUpdateHiddenFiles) - exportService.onLocalFilesUpdated(); - await syncTrash(allCollections, (trashedFiles: EnteFile[]) => - dispatch({ type: "setTrashedFiles", trashedFiles }), - ); - // syncWithRemote is called with the force flag set to true before - // doing an upload. So it is possible, say when resuming a pending - // upload, that we get two syncWithRemotes happening in parallel. - // - // Do the non-file-related sync only for one of these parallel ones. - if (!isForced) { - await sync(); + syncInProgress.current = false; + if (resync.current) { + const { force, silent } = resync.current; + setTimeout(() => handleSyncWithRemote(force, silent), 0); + resync.current = undefined; } - } catch (e) { - switch (e.message) { - case CustomError.SESSION_EXPIRED: - showSessionExpiredDialog(); - break; - case CustomError.KEY_MISSING: - clearKeys(); - router.push(PAGES.CREDENTIALS); - break; - default: - log.error("syncWithRemote failed", e); - } - } finally { - dispatch({ type: "clearUnsyncedState" }); - !silent && hideLoadingBar(); - } - syncInProgress.current = false; - if (resync.current) { - const { force, silent } = resync.current; - setTimeout(() => syncWithRemote(force, silent), 0); - resync.current = undefined; - } - }; + }, + [showLoadingBar, hideLoadingBar, router, showSessionExpiredDialog], + ); + + // Alias for existing code. + const syncWithRemote = handleSyncWithRemote; const setupSelectAllKeyBoardShortcutHandler = () => { const handleKeyUp = (e: KeyboardEvent) => { @@ -1047,8 +1055,6 @@ const Page: React.FC = () => { mode={barMode} modePlus={isInSearchMode ? "search" : barMode} files={filteredFiles} - // TODO: Warning: Doesn't have stable identity. - syncWithRemote={syncWithRemote} setSelected={setSelected} selected={selected} favoriteFileIDs={state.favoriteFileIDs} @@ -1070,6 +1076,7 @@ const Page: React.FC = () => { handleMarkUnsyncedFavoriteUpdate } onMarkTempDeleted={handleMarkTempDeleted} + onSyncWithRemote={handleSyncWithRemote} onSelectCollection={handleSelectCollection} onSelectPerson={handleSelectPerson} />