diff --git a/web/apps/photos/src/components/PhotoFrame.tsx b/web/apps/photos/src/components/PhotoFrame.tsx index a74cde8d02..5b906194fc 100644 --- a/web/apps/photos/src/components/PhotoFrame.tsx +++ b/web/apps/photos/src/components/PhotoFrame.tsx @@ -2,7 +2,7 @@ import log from "@/base/log"; import type { LivePhotoSourceURL, SourceURLs } from "@/media/file"; import { EnteFile } from "@/media/file"; import { FileType } from "@/media/file-type"; -import type { GalleryBarMode } from "@/new/photos/components/gallery/BarImpl"; +import type { GalleryBarMode } from "@/new/photos/components/gallery/reducer"; import { TRASH_SECTION } from "@/new/photos/services/collection"; import DownloadManager from "@/new/photos/services/download"; import { PHOTOS_PAGES } from "@ente/shared/constants/pages"; diff --git a/web/apps/photos/src/components/pages/gallery/SelectedFileOptions.tsx b/web/apps/photos/src/components/pages/gallery/SelectedFileOptions.tsx index 453c6d8684..e454ccabb9 100644 --- a/web/apps/photos/src/components/pages/gallery/SelectedFileOptions.tsx +++ b/web/apps/photos/src/components/pages/gallery/SelectedFileOptions.tsx @@ -1,7 +1,7 @@ import { SelectionBar } from "@/base/components/Navbar"; import type { Collection } from "@/media/collection"; import type { CollectionSelectorAttributes } from "@/new/photos/components/CollectionSelector"; -import type { GalleryBarMode } from "@/new/photos/components/gallery/BarImpl"; +import type { GalleryBarMode } from "@/new/photos/components/gallery/reducer"; import { ALL_SECTION, ARCHIVE_SECTION, diff --git a/web/apps/photos/src/pages/gallery.tsx b/web/apps/photos/src/pages/gallery.tsx index 039bc56ade..0f65f3c406 100644 --- a/web/apps/photos/src/pages/gallery.tsx +++ b/web/apps/photos/src/pages/gallery.tsx @@ -19,10 +19,10 @@ import { PeopleEmptyState, SearchResultsHeader, } from "@/new/photos/components/gallery"; -import type { GalleryBarMode } from "@/new/photos/components/gallery/BarImpl"; import { uniqueFilesByID, useGalleryReducer, + type GalleryBarMode, } from "@/new/photos/components/gallery/reducer"; import { usePeopleStateSnapshot } from "@/new/photos/components/utils/ml"; import { shouldShowWhatsNew } from "@/new/photos/services/changelog"; @@ -246,8 +246,6 @@ export default function Gallery() { const [userIDToEmailMap, setUserIDToEmailMap] = useState>(null); const [emailList, setEmailList] = useState(null); - const [activeCollectionID, setActiveCollectionID] = - useState(undefined); const [fixCreationTimeView, setFixCreationTimeView] = useState(false); const [fixCreationTimeAttributes, setFixCreationTimeAttributes] = useState(null); @@ -282,12 +280,6 @@ export default function Gallery() { SearchOption | undefined >(); - // If visible, what should the (sticky) gallery bar show. - const [barMode, setBarMode] = useState("albums"); - - // The ID of the currently selected person in the gallery bar (if any). - const [activePersonID, setActivePersonID] = useState(); - const peopleState = usePeopleStateSnapshot(); const [isClipSearchResult, setIsClipSearchResult] = @@ -334,6 +326,9 @@ export default function Gallery() { const fileToCollectionsMap = state.fileCollectionIDs; const collectionSummaries = state.collectionSummaries; const hiddenCollectionSummaries = state.hiddenCollectionSummaries; + const barMode = state.barMode ?? "albums"; + const activeCollectionID = state.activeCollectionID; + const activePersonID = state.activePersonID; const isInSearchMode = state.isInSearchMode; if (process.env.NEXT_PUBLIC_ENTE_WIP_CL) { @@ -373,7 +368,7 @@ export default function Gallery() { } await downloadManager.init(token); setupSelectAllKeyBoardShortcutHandler(); - setActiveCollectionID(ALL_SECTION); + dispatch({ type: "showAll" }); setIsFirstLoad(isFirstLogin()); if (justSignedUp()) { setPlanModalView(true); @@ -979,15 +974,18 @@ export default function Gallery() { ) => { const type = searchOption?.suggestion.type; if (type == "collection" || type == "person") { - dispatch({ type: "exitSearch" }); - setSelectedSearchOption(undefined); if (type == "collection") { - setBarMode("albums"); - setActiveCollectionID(searchOption.suggestion.collectionID); + dispatch({ + type: "showNormalOrHiddenCollectionSummary", + collectionSummaryID: searchOption.suggestion.collectionID, + }); } else { - setBarMode("people"); - setActivePersonID(searchOption.suggestion.person.id); + dispatch({ + type: "showPerson", + personID: searchOption.suggestion.person.id, + }); } + setSelectedSearchOption(undefined); } else if (searchOption) { dispatch({ type: "enterSearchMode" }); setSelectedSearchOption(searchOption); @@ -1014,40 +1012,34 @@ export default function Gallery() { setExportModalView(false); }; - const handleShowCollection = (collectionID: number) => { - setBarMode("albums"); - setActiveCollectionID(collectionID); - // TODO: type: "showAlbum" - // setIsInSearchMode(false); - dispatch({ type: "exitSearch" }); - }; + const handleSetActiveCollectionID = ( + collectionSummaryID: number | undefined, + ) => + dispatch({ + type: "showNormalOrHiddenCollectionSummary", + collectionSummaryID, + }); - const handleShowSearchInput = () => dispatch({ type: "enterSearchMode" }); + const handleChangeBarMode = (mode: GalleryBarMode) => + mode == "people" + ? dispatch({ type: "showPeople" }) + : dispatch({ + type: "showAll", + }); const openHiddenSection: GalleryContextType["openHiddenSection"] = ( callback, ) => { authenticateUser(() => { - setBarMode("hidden-albums"); - setActiveCollectionID(HIDDEN_ITEMS_SECTION); + dispatch({ type: "showHidden" }); callback?.(); }); }; - const exitHiddenSection = () => { - setBarMode("albums"); - setActiveCollectionID(ALL_SECTION); - }; - - const handleSelectPerson = (person: Person | undefined) => { - setActivePersonID(person?.id); - setBarMode("people"); - }; - - const handleSelectFileInfoPerson = (personID: string) => { - setActivePersonID(personID); - setBarMode("people"); - }; + const handleSelectPerson = (person: Person | undefined) => + person + ? dispatch({ type: "showPerson", personID: person.id }) + : dispatch({ type: "showPeople" }); const handleOpenCollectionSelector = useCallback( (attributes: CollectionSelectorAttributes) => { @@ -1076,8 +1068,12 @@ export default function Gallery() { value={{ ...defaultGalleryContext, showPlanSelectorModal, - setActiveCollectionID, - onShowCollection: handleShowCollection, + setActiveCollectionID: handleSetActiveCollectionID, + onShowCollection: (id) => + dispatch({ + type: "showNormalOrHiddenCollectionSummary", + collectionSummaryID: id, + }), syncWithRemote, setBlockingLoad, photoListHeader, @@ -1155,7 +1151,7 @@ export default function Gallery() { > {barMode == "hidden-albums" ? ( dispatch({ type: "showAll" })} /> ) : ( + dispatch({ type: "enterSearchMode" }), onSelectSearchOption: handleSelectSearchOption, onSelectPerson: handleSelectPerson, }} @@ -1175,11 +1172,11 @@ export default function Gallery() { {...{ shouldHide: isInSearchMode, mode: barMode, - onChangeMode: setBarMode, + onChangeMode: handleChangeBarMode, collectionSummaries, activeCollection, activeCollectionID, - setActiveCollectionID, + setActiveCollectionID: handleSetActiveCollectionID, hiddenCollectionSummaries, showPeopleSectionButton, people: galleryPeopleState?.people ?? [], @@ -1269,7 +1266,9 @@ export default function Gallery() { setFilesDownloadProgressAttributesCreator } selectable={true} - onSelectPerson={handleSelectFileInfoPerson} + onSelectPerson={(personID) => { + dispatch({ type: "showPerson", personID }); + }} /> )} {selected.count > 0 && diff --git a/web/apps/photos/src/utils/photoFrame/index.ts b/web/apps/photos/src/utils/photoFrame/index.ts index 1c52664b45..651848f1d9 100644 --- a/web/apps/photos/src/utils/photoFrame/index.ts +++ b/web/apps/photos/src/utils/photoFrame/index.ts @@ -3,7 +3,7 @@ import type { LivePhotoSourceURL, SourceURLs } from "@/media/file"; import { EnteFile } from "@/media/file"; import { FileType } from "@/media/file-type"; import type { SelectionContext } from "@/new/photos/components/gallery"; -import type { GalleryBarMode } from "@/new/photos/components/gallery/BarImpl"; +import type { GalleryBarMode } from "@/new/photos/components/gallery/reducer"; import { ensure } from "@/utils/ensure"; import { SetSelectedState } from "types/gallery"; diff --git a/web/packages/new/photos/components/gallery/BarImpl.tsx b/web/packages/new/photos/components/gallery/BarImpl.tsx index 1b2b2d4dc1..7cd754c240 100644 --- a/web/packages/new/photos/components/gallery/BarImpl.tsx +++ b/web/packages/new/photos/components/gallery/BarImpl.tsx @@ -42,11 +42,7 @@ import { type ListChildComponentProps, areEqual, } from "react-window"; - -/** - * Specifies what the bar is displaying currently. - */ -export type GalleryBarMode = "albums" | "hidden-albums" | "people"; +import type { GalleryBarMode } from "./reducer"; export interface GalleryBarImplProps { /** diff --git a/web/packages/new/photos/components/gallery/reducer.ts b/web/packages/new/photos/components/gallery/reducer.ts index cf423eb23b..a48d4634c7 100644 --- a/web/packages/new/photos/components/gallery/reducer.ts +++ b/web/packages/new/photos/components/gallery/reducer.ts @@ -42,6 +42,25 @@ import { import type { Person } from "../../services/ml/people"; import type { FamilyData } from "../../services/user"; +/** + * Specifies what the bar at the top of the gallery is displaying currently. + */ +export type GalleryBarMode = "albums" | "hidden-albums" | "people"; + +/** + * Specifies what the gallery is currently displaying. + * + * TODO: An experiment at consolidating state. + */ +export type GalleryFocus = + | { + type: "albums" | "hidden-albums"; + activeCollectionID: number; + activeCollection: Collection | undefined; + activeCollectionSummary: CollectionSummary; + } + | { type: "people"; activePersonID: string; activePerson: Person }; + /** * Derived UI state backing the gallery. * @@ -139,6 +158,18 @@ export interface GalleryState { /*--< Transient UI state >--*/ + /** + * If visible, what should the (sticky) gallery bar show. + */ + barMode: GalleryBarMode | undefined; + /** + * The section / area, and the item within it, that the gallery is currently + * showing. + */ + focus: GalleryFocus | undefined; + activeCollectionID: number | undefined; + activePersonID: string | undefined; + filteredData: EnteFile[]; /** * The currently selected person, if any. @@ -207,6 +238,14 @@ export type GalleryAction = | { type: "resetHiddenFiles"; hiddenFiles: EnteFile[] } | { type: "fetchHiddenFiles"; hiddenFiles: EnteFile[] } | { type: "setTrashedFiles"; trashedFiles: EnteFile[] } + | { type: "showAll" } + | { type: "showHidden" } + | { + type: "showNormalOrHiddenCollectionSummary"; + collectionSummaryID: number | undefined; + } + | { type: "showPeople" } + | { type: "showPerson"; personID: string } | { type: "searchResults"; searchResults: EnteFile[] } | { type: "enterSearchMode" } | { type: "exitSearch" }; @@ -227,6 +266,10 @@ const initialGalleryState: GalleryState = { fileCollectionIDs: new Map(), collectionSummaries: new Map(), hiddenCollectionSummaries: new Map(), + barMode: undefined, + focus: undefined, + activeCollectionID: undefined, + activePersonID: undefined, filteredData: [], activePerson: undefined, people: [], @@ -456,6 +499,52 @@ const galleryReducer: React.Reducer = ( state.archivedCollectionIDs, ), }; + case "showAll": + return { + ...state, + barMode: "albums", + activeCollectionID: ALL_SECTION, + isInSearchMode: false, + searchResults: undefined, + }; + case "showHidden": + return { + ...state, + barMode: "hidden-albums", + activeCollectionID: HIDDEN_ITEMS_SECTION, + isInSearchMode: false, + searchResults: undefined, + }; + case "showNormalOrHiddenCollectionSummary": + return { + ...state, + barMode: + action.collectionSummaryID !== undefined && + state.hiddenCollectionSummaries.has( + action.collectionSummaryID, + ) + ? "hidden-albums" + : "albums", + activeCollectionID: action.collectionSummaryID ?? ALL_SECTION, + isInSearchMode: false, + searchResults: undefined, + }; + case "showPeople": + return { + ...state, + barMode: "people", + activePersonID: undefined, + isInSearchMode: false, + searchResults: undefined, + }; + case "showPerson": + return { + ...state, + barMode: "people", + activePersonID: action.personID, + isInSearchMode: false, + searchResults: undefined, + }; case "enterSearchMode": return { ...state, isInSearchMode: true }; case "searchResults":