diff --git a/web/apps/photos/src/components/Collections/GalleryBarAndListHeader.tsx b/web/apps/photos/src/components/Collections/GalleryBarAndListHeader.tsx index 96b36a5a02..48e32abce6 100644 --- a/web/apps/photos/src/components/Collections/GalleryBarAndListHeader.tsx +++ b/web/apps/photos/src/components/Collections/GalleryBarAndListHeader.tsx @@ -3,7 +3,7 @@ import { CollectionShare, type CollectionShareProps, } from "components/Collections/CollectionShare"; -import type { TimeStampListItem } from "components/FileList"; +import type { FileListHeaderOrFooter } from "components/FileList"; import { useModalVisibility } from "ente-base/components/utils/modal"; import { isSaveCancelled, @@ -49,7 +49,7 @@ type GalleryBarAndListHeaderProps = Omit< barCollectionSummaries: CollectionSummaries; activeCollection: Collection; setActiveCollectionID: (collectionID: number) => void; - setPhotoListHeader: (value: TimeStampListItem) => void; + setFileListHeader: (header: FileListHeaderOrFooter) => void; saveGroups: SaveGroup[]; } & Pick & Pick< @@ -66,7 +66,7 @@ type GalleryBarAndListHeaderProps = Omit< * of the actual list of items. * * These are disparate views - indeed, the list header is not even a child of - * this component but is instead proxied via {@link setPhotoListHeader}. Still, + * this component but is instead proxied via {@link setFileListHeader}. Still, * having this intermediate wrapper component allows us to move some of the * common concerns shared by both the gallery bar and list header (e.g. some * dialogs that can be invoked from both places) into this file instead of @@ -95,7 +95,7 @@ export const GalleryBarAndListHeader: React.FC< onRemotePull, onAddSaveGroup, onSelectPerson, - setPhotoListHeader, + setFileListHeader, }) => { const { show: showAllAlbums, props: allAlbumsVisibilityProps } = useModalVisibility(); @@ -134,7 +134,7 @@ export const GalleryBarAndListHeader: React.FC< useEffect(() => { if (shouldHide) return; - setPhotoListHeader({ + setFileListHeader({ item: mode != "people" ? ( ), - tag: "header", height: 68, }); }, [ diff --git a/web/apps/photos/src/components/FileList.tsx b/web/apps/photos/src/components/FileList.tsx index cf2314ef3f..7d0737d79c 100644 --- a/web/apps/photos/src/components/FileList.tsx +++ b/web/apps/photos/src/components/FileList.tsx @@ -53,14 +53,27 @@ const FOOTER_HEIGHT = 90; const ALBUM_FOOTER_HEIGHT = 75; const ALBUM_FOOTER_HEIGHT_WITH_REFERRAL = 113; -export type FileListItemTag = "header" | "publicAlbumsFooter" | "date" | "file"; +/** + * A component with an explicit height suitable for being plugged in as the + * {@link header} or {@link footer} of the {@link FileList}. + */ +export interface FileListHeaderOrFooter { + /** + * The component itself. + */ + item: React.ReactNode; + /** + * The height of the component (in px). + */ + height: number; +} -export interface TimeStampListItem { +interface TimeStampListItem { /** * An optional {@link FileListItemTag} that can be used to identify item * types for conditional behaviour. */ - tag?: FileListItemTag; + tag?: "date" | "file"; items?: FileListAnnotatedFile[]; itemStartIndex?: number; date?: string; @@ -122,6 +135,19 @@ export interface FileListProps { * another mode in which the gallery operates. */ modePlus?: GalleryBarMode | "search"; + /** + * An optional component shown before all the items in the list. + * + * It is not sticky, and scrolls along with the content of the list. + */ + header?: FileListHeaderOrFooter; + /** + * An optional component shown after all the items in the list. + * + * It is not sticky, and scrolls along with the content of the list. + */ + footer?: FileListHeaderOrFooter; + showAppDownloadBanner?: boolean; /** * The logged in user, if any. * @@ -130,7 +156,6 @@ export interface FileListProps { * omit this prop. */ user?: LocalUser; - showAppDownloadBanner?: boolean; /** * If `true`, then the current listing is showing magic search results. */ @@ -157,16 +182,6 @@ export interface FileListProps { * omitted when running in the public albums app. */ emailByUserID?: Map; - /** - * An optional {@link TimeStampListItem} shown before all the items in the - * list. It is not sticky, and scrolls along with the content of the list. - */ - header?: TimeStampListItem; - /** - * An optional {@link TimeStampListItem} shown after all the items in the - * list. It is not sticky, and scrolls along with the content of the list. - */ - footer?: TimeStampListItem; /** * Called when the user activates the thumbnail at the given {@link index}. * @@ -185,6 +200,7 @@ export const FileList: React.FC = ({ mode, modePlus, header, + footer, user, annotatedFiles, showAppDownloadBanner, @@ -196,7 +212,6 @@ export const FileList: React.FC = ({ activePersonID, favoriteFileIDs, emailByUserID, - footer, onItemClick, }) => { const publicCollectionGalleryContext = useContext( @@ -385,7 +400,6 @@ export const FileList: React.FC = ({ }; const getAppDownloadFooter = (): TimeStampListItem => ({ - tag: "publicAlbumsFooter", height: FOOTER_HEIGHT, item: ( @@ -415,7 +429,6 @@ export const FileList: React.FC = ({ }); const getAlbumsFooter = (): TimeStampListItem => ({ - tag: "publicAlbumsFooter", height: publicCollectionGalleryContext.referralCode ? ALBUM_FOOTER_HEIGHT_WITH_REFERRAL : ALBUM_FOOTER_HEIGHT, diff --git a/web/apps/photos/src/pages/gallery.tsx b/web/apps/photos/src/pages/gallery.tsx index 949b6e43a4..7e3d14bc72 100644 --- a/web/apps/photos/src/pages/gallery.tsx +++ b/web/apps/photos/src/pages/gallery.tsx @@ -5,7 +5,7 @@ import { IconButton, Stack, Typography } from "@mui/material"; import { AuthenticateUser } from "components/AuthenticateUser"; import { GalleryBarAndListHeader } from "components/Collections/GalleryBarAndListHeader"; import { DownloadStatusNotifications } from "components/DownloadStatusNotifications"; -import { type TimeStampListItem } from "components/FileList"; +import type { FileListHeaderOrFooter } from "components/FileList"; import { FileListWithViewer } from "components/FileListWithViewer"; import { FixCreationTime } from "components/FixCreationTime"; import { Sidebar } from "components/Sidebar"; @@ -180,9 +180,11 @@ const Page: React.FC = () => { const [fixCreationTimeFiles, setFixCreationTimeFiles] = useState< EnteFile[] >([]); - // The (non-sticky) header shown at the top of the gallery items. + /** + * The (non-sticky) header shown at the top of the gallery items. + */ const [fileListHeader, setFileListHeader] = useState< - TimeStampListItem | undefined + FileListHeaderOrFooter | undefined >(undefined); const [openCollectionSelector, setOpenCollectionSelector] = useState(false); @@ -409,14 +411,13 @@ const Page: React.FC = () => { useEffect(() => { if (isInSearchMode && state.searchSuggestion) { setFileListHeader({ - height: 104, item: ( ), - tag: "header", + height: 104, }); } }, [isInSearchMode, state.searchSuggestion, state.searchResults]); @@ -1072,7 +1073,7 @@ const Page: React.FC = () => { activeCollection, activeCollectionID, activePerson, - setPhotoListHeader: setFileListHeader, + setFileListHeader, saveGroups, onAddSaveGroup, }} diff --git a/web/apps/photos/src/pages/shared-albums.tsx b/web/apps/photos/src/pages/shared-albums.tsx index ac6ffd7759..52a2aabc8e 100644 --- a/web/apps/photos/src/pages/shared-albums.tsx +++ b/web/apps/photos/src/pages/shared-albums.tsx @@ -398,7 +398,6 @@ export default function PublicCollectionGallery() { }} /> ), - tag: "header" as const, height: 68, } : undefined,