diff --git a/web/apps/photos/src/pages/gallery.tsx b/web/apps/photos/src/pages/gallery.tsx index e569f6b285..6822011a8d 100644 --- a/web/apps/photos/src/pages/gallery.tsx +++ b/web/apps/photos/src/pages/gallery.tsx @@ -15,7 +15,10 @@ import { FullScreenDropZone } from "@/gallery/components/FullScreenDropZone"; import { resetFileViewerDataSourceOnClose } from "@/gallery/components/viewer/data-source"; import { type Collection } from "@/media/collection"; import { mergeMetadata, type EnteFile } from "@/media/file"; -import type { ItemVisibility } from "@/media/file-metadata"; +import { + updateRemotePrivateMagicMetadata, + type ItemVisibility, +} from "@/media/file-metadata"; import { CollectionSelector, type CollectionSelectorAttributes, @@ -132,12 +135,7 @@ import { getSelectedCollection, handleCollectionOps, } from "utils/collection"; -import { - FILE_OPS_TYPE, - changeFilesVisibility, - getSelectedFiles, - handleFileOps, -} from "utils/file"; +import { FILE_OPS_TYPE, getSelectedFiles, handleFileOps } from "utils/file"; const defaultGalleryContext: GalleryContextType = { setActiveCollectionID: () => null, @@ -817,7 +815,8 @@ const Page: React.FC = () => { }; const handleFileViewerFileVisibilityUpdate = useCallback( - async (fileID: number, visibility: ItemVisibility) => { + async (file: EnteFile, visibility: ItemVisibility) => { + const fileID = file.id; dispatch({ type: "markPendingVisibilityUpdate", fileID, @@ -825,7 +824,10 @@ const Page: React.FC = () => { }); try { - await changeFilesVisibility(files, visibility); + updateRemotePrivateMagicMetadata(file, { visibility }); + // TODO(AR): Need to trigger a "lite" sync (or update that + // particular file in the reducer state in some other way). + // See: [Note: File viewer update and dispatch] dispatch({ type: "markUnsyncedVisibilityUpdate", diff --git a/web/packages/gallery/components/viewer/FileViewer.tsx b/web/packages/gallery/components/viewer/FileViewer.tsx index 9e150f989c..4bffb8a2a2 100644 --- a/web/packages/gallery/components/viewer/FileViewer.tsx +++ b/web/packages/gallery/components/viewer/FileViewer.tsx @@ -144,10 +144,10 @@ export interface FileViewerToggleArchiveButtonProps { */ unsyncedVisibilityUpdates?: Map; /** - * Update the {@link visibility} of the file with the given {@link fileID}. + * Update the {@link visibility} of the given {@link file}. */ onFileVisibilityUpdate?: ( - fileID: number, + file: EnteFile, visibility: ItemVisibility, ) => Promise; } @@ -464,36 +464,57 @@ export const FileViewer: React.FC = ({ // `onFileVisibilityUpdate` will asynchronously result in updates to // the `files` prop. // - // Currently that indeed is what happens as the last call in the - // `onDelete` and `onFileVisibilityUpdate` implementations are calls - // to a (useReducer) dispatcher, but we need to be careful about - // preserving this assumption if changing their implementation in - // the future. + // Currently that is indeed what happens because the last call in + // the `onDelete` and `onFileVisibilityUpdate` implementations are + // calls to a (useReducer) dispatcher, but we need to be careful + // about preserving this assumption when changing their + // implementation in the future. awaitNextFilesOrFavoritesUpdate((files: EnteFile[]) => { handleNeedsRemoteSync(); + // We might've been provided an expectedFileID. If so, only do + // the reload if it is no longer present in the files array. + // + // There are 3 cases: + // + // 1. On file deletion: expectedFileID is not provided. + // + // 2. On file archive when we're in the all section: + // expectedFileID is provided, and will not be present in the + // updated files array after update. In such cases, we want + // to behave like delete (move to the next slide). + // + // 3. On other types of file archive: expectedFileID is + // provided, but it'll still be present in the updated files + // array, and in such cases we don't want to reload the + // current slide. Instead, we only modify the file attribute + // of the currentAnnotatedFile (if appropriate). if (files.length) { - // We might've been provided an expectedFileID. If so, only - // do the reload if it is no longer present in the files - // array. - // - // There are 3 cases: - // - // - On file deletion: expectedFileID is not provided. - // - // - On file archive when we're in the all section: - // expectedFileID is provided, and will not be present in - // the updated files array after update. In such cases, we - // want to behave like delete (move to the next slide). - // - // - On other types of file archive: expectedFileID is - // provided, but it'll still be present in the updated - // files array, and in such cases we don't want to reload - // the current slide. - if ( - expectedFileID && - files.find(({ id }) => id == expectedFileID) - ) { - // Do nothing. + const updatedFile = expectedFileID + ? files.find(({ id }) => id == expectedFileID) + : undefined; + if (updatedFile) { + setActiveAnnotatedFile((activeAnnotatedFile) => { + // Modify the file attribute of activeAnnotatedFile + // if we're still showing a slide with a file that + // has the same ID as the one we expected to modify. + // + // In the case of delete, this code will not run, + // and in the case of toggling archive, none of the + // other attributes of activeAnnotatedFile currently + // depend on the archive status change, so this is + // safe. But it is still on the kludgy side, and + // might need care with future changes. + // + // (We don't do a full refresh since that would + // cause the user to lose their pan / zoom etc) + if ( + activeAnnotatedFile && + activeAnnotatedFile.file.id == expectedFileID + ) { + activeAnnotatedFile.file = updatedFile; + } + return activeAnnotatedFile; + }); } else { // Refreshing the current slide after the current file // has gone will show the subsequent slide (since that @@ -771,7 +792,7 @@ export const FileViewer: React.FC = ({ toggleArchived = () => { handleMoreMenuCloseIfNeeded(); void onFileVisibilityUpdate( - file.id, + file, isArchived ? ItemVisibility.visible : ItemVisibility.archived,