diff --git a/web/apps/photos/src/components/PhotoList/index.tsx b/web/apps/photos/src/components/PhotoList/index.tsx index 783c26cbe8..72b15ac75a 100644 --- a/web/apps/photos/src/components/PhotoList/index.tsx +++ b/web/apps/photos/src/components/PhotoList/index.tsx @@ -20,7 +20,7 @@ import { ListChildComponentProps, areEqual, } from "react-window"; -import { handleSelectCreator } from "utils/photoFrame"; +import { handleSelectCreatorMulti } from "utils/photoFrame"; import { PublicCollectionGalleryContext } from "utils/publicCollectionGallery"; export const DATE_CONTAINER_HEIGHT = 48; @@ -793,7 +793,7 @@ export function PhotoList({ console.timeEnd("t6"); }, [galleryContext.selectedFile]); - const handleSelect = handleSelectCreator( + const handleSelect = handleSelectCreatorMulti( galleryContext.setSelectedFiles, mode, galleryContext?.user?.id, diff --git a/web/apps/photos/src/utils/photoFrame/index.ts b/web/apps/photos/src/utils/photoFrame/index.ts index 8fdc03622a..06d8d41207 100644 --- a/web/apps/photos/src/utils/photoFrame/index.ts +++ b/web/apps/photos/src/utils/photoFrame/index.ts @@ -131,3 +131,128 @@ export const handleSelectCreator = }; }); }; + +// TODO: This is a copy of handleSelectCreator, forked to +// handle multiple selections efficiently ("Select all in a single day"). If +// the code doesn't diverge, we'll have verbatim duplication. +export const handleSelectCreatorMulti = + ( + setSelected: SetSelectedState, + mode: GalleryBarMode | undefined, + userID: number | undefined, + activeCollectionID: number, + activePersonID: string | undefined, + ) => + ({ id, ownerID }: { id: number; ownerID: number }) => + (checked: boolean) => { + setSelected((selected) => { + if (!mode) { + // Retain older behavior for non-gallery call sites. + if (selected.collectionID !== activeCollectionID) { + selected = { + ownCount: 0, + count: 0, + collectionID: 0, + context: undefined, + }; + } + } else if (!selected.context) { + // Gallery will specify a mode, but a fresh selection starts off + // without a context, so fill it in with the current context. + selected = { + ...selected, + context: + mode == "people" + ? { mode, personID: activePersonID! } + : { + mode, + collectionID: activeCollectionID!, + }, + }; + } else { + // Both mode and context are defined. + if (selected.context.mode != mode) { + // Clear selection if mode has changed. + selected = { + ownCount: 0, + count: 0, + collectionID: 0, + context: + mode == "people" + ? { mode, personID: activePersonID! } + : { + mode, + collectionID: activeCollectionID!, + }, + }; + } else { + if (selected.context?.mode == "people") { + if (selected.context.personID != activePersonID) { + // Clear selection if person has changed. + selected = { + ownCount: 0, + count: 0, + collectionID: 0, + context: { + mode: selected.context?.mode, + personID: activePersonID!, + }, + }; + } + } else { + if ( + selected.context.collectionID != activeCollectionID + ) { + // Clear selection if collection has changed. + selected = { + ownCount: 0, + count: 0, + collectionID: 0, + context: { + mode: selected.context?.mode, + collectionID: activeCollectionID!, + }, + }; + } + } + } + } + + const newContext: SelectionContext | undefined = !mode + ? undefined + : mode == "people" + ? { mode, personID: activePersonID! } + : { mode, collectionID: activeCollectionID! }; + + const handleCounterChange = (count: number) => { + if (selected[id] === checked) { + return count; + } + if (checked) { + return count + 1; + } else { + return count - 1; + } + }; + + const handleAllCounterChange = () => { + if (ownerID === userID) { + return { + ownCount: handleCounterChange(selected.ownCount), + count: handleCounterChange(selected.count), + }; + } else { + return { + count: handleCounterChange(selected.count), + }; + } + }; + return { + ...selected, + [id]: checked, + collectionID: activeCollectionID, + context: newContext, + ...handleAllCounterChange(), + }; + }); + };