[web] File list code cleanup (#6491)

This commit is contained in:
Manav Rathi
2025-07-08 10:11:25 +05:30
committed by GitHub
9 changed files with 230 additions and 232 deletions

View File

@@ -9,7 +9,7 @@ import { imageURLGenerator } from "services/render";
const Page: React.FC = () => {
const [isEmpty, setIsEmpty] = useState(false);
const [imageURL, setImageURL] = useState<string | undefined>();
const [imageURL, setImageURL] = useState("");
const router = useRouter();

View File

@@ -1347,9 +1347,7 @@ const ManagePublicShareOptions: React.FC<ManagePublicShareOptionsProps> = ({
setBlockingLoad,
onRemotePull,
}) => {
const [errorMessage, setErrorMessage] = useState<string | undefined>(
undefined,
);
const [errorMessage, setErrorMessage] = useState("");
const [copied, handleCopyLink] = useClipboardCopy(resolvedURL);
@@ -1362,7 +1360,7 @@ const ManagePublicShareOptions: React.FC<ManagePublicShareOptionsProps> = ({
updates: UpdatePublicURLAttributes,
) => {
setBlockingLoad(true);
setErrorMessage(undefined);
setErrorMessage("");
try {
setPublicURL(await updatePublicURL(collection.id, updates));
void onRemotePull({ silent: true });
@@ -1375,7 +1373,7 @@ const ManagePublicShareOptions: React.FC<ManagePublicShareOptionsProps> = ({
};
const handleRemovePublicLink = async () => {
setBlockingLoad(true);
setErrorMessage(undefined);
setErrorMessage("");
try {
await deleteShareURL(collection.id);
setPublicURL(undefined);

View File

@@ -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<CollectionHeaderProps, "onRemotePull" | "onAddSaveGroup"> &
Pick<
@@ -62,11 +62,11 @@ type GalleryBarAndListHeaderProps = Omit<
* dialogs that might be triggered by actions on either the bar or the header..
*
* This component manages the sticky horizontally scrollable bar shown at the
* top of the gallery, AND the non-sticky header shown below the bar, at the top
* of the actual list of items.
* top of the gallery, AND the (non-sticky) header shown below the bar, at the
* top 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" ? (
<CollectionHeader
@@ -159,7 +159,6 @@ export const GalleryBarAndListHeader: React.FC<
) : (
<></>
),
tag: "header",
height: 68,
});
}, [

View File

@@ -3,7 +3,7 @@
import AlbumOutlinedIcon from "@mui/icons-material/AlbumOutlined";
import FavoriteRoundedIcon from "@mui/icons-material/FavoriteRounded";
import PlayCircleOutlineOutlinedIcon from "@mui/icons-material/PlayCircleOutlineOutlined";
import { Box, Checkbox, Link, Typography, styled } from "@mui/material";
import { Box, Checkbox, Typography, styled } from "@mui/material";
import Avatar from "components/Avatar";
import type { LocalUser } from "ente-accounts/services/user";
import { assertionFailed } from "ente-base/assert";
@@ -31,7 +31,6 @@ import { PseudoCollectionID } from "ente-new/photos/services/collection-summary"
import { t } from "i18next";
import memoize from "memoize-one";
import React, { useContext, useEffect, useMemo, useRef, useState } from "react";
import { Trans } from "react-i18next";
import {
VariableSizeList as List,
type ListChildComponentProps,
@@ -49,18 +48,27 @@ export const SPACE_BTW_DATES = 44;
const SPACE_BTW_DATES_TO_IMAGE_CONTAINER_WIDTH_RATIO = 0.244;
const FOOTER_HEIGHT = 90;
const ALBUM_FOOTER_HEIGHT = 75;
const ALBUM_FOOTER_HEIGHT_WITH_REFERRAL = 113;
/**
* 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 type FileListItemTag = "header" | "publicAlbumsFooter" | "date" | "file";
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 +130,18 @@ 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;
/**
* The logged in user, if any.
*
@@ -130,11 +150,13 @@ export interface FileListProps {
* omit this prop.
*/
user?: LocalUser;
showAppDownloadBanner?: boolean;
/**
* If `true`, then the current listing is showing magic search results.
* If `true`, then the default behaviour of grouping files by their date is
* suppressed.
*
* This behaviour is used when showing magic search results.
*/
isMagicSearchResult?: boolean;
disableGrouping?: boolean;
selectable?: boolean;
setSelected: (
selected: SelectedState | ((selected: SelectedState) => SelectedState),
@@ -157,16 +179,6 @@ export interface FileListProps {
* omitted when running in the public albums app.
*/
emailByUserID?: Map<number, string>;
/**
* 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,10 +197,10 @@ export const FileList: React.FC<FileListProps> = ({
mode,
modePlus,
header,
footer,
user,
annotatedFiles,
showAppDownloadBanner,
isMagicSearchResult,
disableGrouping,
selectable,
selected,
setSelected,
@@ -196,7 +208,6 @@ export const FileList: React.FC<FileListProps> = ({
activePersonID,
favoriteFileIDs,
emailByUserID,
footer,
onItemClick,
}) => {
const publicCollectionGalleryContext = useContext(
@@ -246,7 +257,7 @@ export const FileList: React.FC<FileListProps> = ({
if (header) {
timeStampList.push(asFullSpanListItem(header));
}
if (isMagicSearchResult) {
if (disableGrouping) {
noGrouping(timeStampList);
} else {
groupByTime(timeStampList);
@@ -258,15 +269,10 @@ export const FileList: React.FC<FileListProps> = ({
if (timeStampList.length === 1) {
timeStampList.push(getEmptyListItem());
}
timeStampList.push(getVacuumItem(timeStampList));
const footerHeight = footer?.height ?? 0;
timeStampList.push(getVacuumItem(timeStampList, footerHeight));
if (footer) {
timeStampList.push(asFullSpanListItem(footer));
} else if (showAppDownloadBanner) {
timeStampList.push(getAppDownloadFooter());
}
if (publicCollectionGalleryContext.credentials) {
timeStampList.push(getAlbumsFooter());
}
setTimeStampList(timeStampList);
@@ -283,7 +289,7 @@ export const FileList: React.FC<FileListProps> = ({
annotatedFiles,
header,
footer,
isMagicSearchResult,
disableGrouping,
publicCollectionGalleryContext.credentials,
]);
@@ -358,15 +364,7 @@ export const FileList: React.FC<FileListProps> = ({
};
};
const getVacuumItem = (timeStampList) => {
let footerHeight;
if (publicCollectionGalleryContext.credentials) {
footerHeight = publicCollectionGalleryContext.referralCode
? ALBUM_FOOTER_HEIGHT_WITH_REFERRAL
: ALBUM_FOOTER_HEIGHT;
} else {
footerHeight = FOOTER_HEIGHT;
}
const getVacuumItem = (timeStampList, footerHeight: number) => {
const fileListHeight = (() => {
let sum = 0;
const getCurrentItemSize = getItemSize(timeStampList);
@@ -384,95 +382,6 @@ export const FileList: React.FC<FileListProps> = ({
};
};
const getAppDownloadFooter = (): TimeStampListItem => ({
tag: "publicAlbumsFooter",
height: FOOTER_HEIGHT,
item: (
<FooterContainer span={columns}>
<Typography variant="small" sx={{ color: "text.faint" }}>
<Trans
i18nKey={"install_mobile_app"}
components={{
a: (
<Link
href="https://play.google.com/store/apps/details?id=io.ente.photos"
target="_blank"
rel="noopener"
/>
),
b: (
<Link
href="https://apps.apple.com/in/app/ente-photos/id1542026904"
target="_blank"
rel="noopener"
/>
),
}}
/>
</Typography>
</FooterContainer>
),
});
const getAlbumsFooter = (): TimeStampListItem => ({
tag: "publicAlbumsFooter",
height: publicCollectionGalleryContext.referralCode
? ALBUM_FOOTER_HEIGHT_WITH_REFERRAL
: ALBUM_FOOTER_HEIGHT,
item: (
<AlbumFooterContainer
span={columns}
hasReferral={!!publicCollectionGalleryContext.referralCode}
>
{/* Make the entire area tappable, otherwise it is hard to
get at on mobile devices. */}
<Box sx={{ width: "100%" }}>
<Link
color="text.base"
sx={{ "&:hover": { color: "inherit" } }}
target="_blank"
href={"https://ente.io"}
>
<Typography variant="small">
<Trans
i18nKey="shared_using"
components={{
a: (
<Typography
variant="small"
component="span"
sx={{ color: "accent.main" }}
/>
),
}}
values={{ url: "ente.io" }}
/>
</Typography>
</Link>
{publicCollectionGalleryContext.referralCode ? (
<FullStretchContainer>
<Typography
sx={{
marginTop: "12px",
padding: "8px",
color: "accent.contrastText",
}}
>
<Trans
i18nKey={"sharing_referral_code"}
values={{
referralCode:
publicCollectionGalleryContext.referralCode,
}}
/>
</Typography>
</FullStretchContainer>
) : null}
</Box>
</AlbumFooterContainer>
),
});
/**
* Checks and merge multiple dates into a single row.
*/
@@ -953,41 +862,6 @@ const DateContainer = styled(ListItemContainer)(
`,
);
const FooterContainer = styled(ListItemContainer)`
margin-bottom: 0.75rem;
@media (max-width: 540px) {
font-size: 12px;
margin-bottom: 0.5rem;
}
text-align: center;
justify-content: center;
align-items: flex-end;
margin-top: calc(2rem + 20px);
`;
const AlbumFooterContainer = styled(ListItemContainer, {
shouldForwardProp: (propName) => propName != "hasReferral",
})<{ hasReferral: boolean }>`
margin-top: 48px;
margin-bottom: ${({ hasReferral }) => (!hasReferral ? `10px` : "0px")};
text-align: center;
justify-content: center;
`;
const FullStretchContainer = styled("div")(
({ theme }) => `
margin: 0 -24px;
width: calc(100% + 46px);
left: -24px;
@media (max-width: ${IMAGE_CONTAINER_MAX_WIDTH * MIN_COLUMNS}px) {
margin: 0 -4px;
width: calc(100% + 6px);
left: -4px;
}
background-color: ${theme.vars.palette.accent.main};
`,
);
const NothingContainer = styled(ListItemContainer)`
text-align: center;
justify-content: center;

View File

@@ -58,8 +58,7 @@ export type FileListWithViewerProps = {
| "modePlus"
| "header"
| "footer"
| "showAppDownloadBanner"
| "isMagicSearchResult"
| "disableGrouping"
| "selectable"
| "selected"
| "setSelected"
@@ -98,8 +97,7 @@ export const FileListWithViewer: React.FC<FileListWithViewerProps> = ({
user,
files,
enableDownload,
showAppDownloadBanner,
isMagicSearchResult,
disableGrouping,
selectable,
selected,
setSelected,
@@ -186,8 +184,7 @@ export const FileListWithViewer: React.FC<FileListWithViewerProps> = ({
header,
footer,
user,
showAppDownloadBanner,
isMagicSearchResult,
disableGrouping,
selectable,
selected,
setSelected,

View File

@@ -1,11 +1,11 @@
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
import FileUploadOutlinedIcon from "@mui/icons-material/FileUploadOutlined";
import MenuIcon from "@mui/icons-material/Menu";
import { IconButton, Stack, Typography } from "@mui/material";
import { IconButton, Link, 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,8 @@ const Page: React.FC = () => {
const [fixCreationTimeFiles, setFixCreationTimeFiles] = useState<
EnteFile[]
>([]);
// 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 +408,13 @@ const Page: React.FC = () => {
useEffect(() => {
if (isInSearchMode && state.searchSuggestion) {
setFileListHeader({
height: 104,
item: (
<SearchResultsHeader
searchSuggestion={state.searchSuggestion}
fileCount={state.searchResults?.length ?? 0}
/>
),
tag: "header",
height: 104,
});
}
}, [isInSearchMode, state.searchSuggestion, state.searchResults]);
@@ -966,6 +964,14 @@ const Page: React.FC = () => {
[],
);
const showAppDownloadFooter =
state.collectionFiles.length < 30 && !isInSearchMode;
const fileListFooter = useMemo(
() => (showAppDownloadFooter ? createAppDownloadFooter() : undefined),
[showAppDownloadFooter],
);
const showSelectionBar =
selected.count > 0 && selected.collectionID === activeCollectionID;
@@ -1072,7 +1078,7 @@ const Page: React.FC = () => {
activeCollection,
activeCollectionID,
activePerson,
setPhotoListHeader: setFileListHeader,
setFileListHeader,
saveGroups,
onAddSaveGroup,
}}
@@ -1148,13 +1154,11 @@ const Page: React.FC = () => {
mode={barMode}
modePlus={isInSearchMode ? "search" : barMode}
header={fileListHeader}
footer={fileListFooter}
user={user}
files={filteredFiles}
enableDownload={true}
showAppDownloadBanner={
state.collectionFiles.length < 30 && !isInSearchMode
}
isMagicSearchResult={state.searchSuggestion?.type == "clip"}
disableGrouping={state.searchSuggestion?.type == "clip"}
selectable={true}
selected={selected}
setSelected={setSelected}
@@ -1382,3 +1386,39 @@ const handleSubscriptionCompletionRedirectIfNeeded = async (
}
}
};
const createAppDownloadFooter = (): FileListHeaderOrFooter => ({
item: (
<Typography
variant="small"
sx={{
alignSelf: "flex-end",
marginInline: "auto",
marginBlock: 0.75,
textAlign: "center",
color: "text.faint",
}}
>
<Trans
i18nKey={"install_mobile_app"}
components={{
a: (
<Link
href="https://play.google.com/store/apps/details?id=io.ente.photos"
target="_blank"
rel="noopener"
/>
),
b: (
<Link
href="https://apps.apple.com/in/app/ente-photos/id1542026904"
target="_blank"
rel="noopener"
/>
),
}}
/>
</Typography>
),
height: 90,
});

View File

@@ -3,7 +3,15 @@ import AddPhotoAlternateOutlinedIcon from "@mui/icons-material/AddPhotoAlternate
import CloseIcon from "@mui/icons-material/Close";
import DownloadIcon from "@mui/icons-material/Download";
import FileDownloadOutlinedIcon from "@mui/icons-material/FileDownloadOutlined";
import { Box, Button, IconButton, Stack, styled, Tooltip } from "@mui/material";
import {
Box,
Button,
IconButton,
Link,
Stack,
styled,
Tooltip,
} from "@mui/material";
import Typography from "@mui/material/Typography";
import { DownloadStatusNotifications } from "components/DownloadStatusNotifications";
import { FileListWithViewer } from "components/FileListWithViewer";
@@ -84,6 +92,7 @@ import { t } from "i18next";
import { useRouter } from "next/router";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { type FileWithPath } from "react-dropzone";
import { Trans } from "react-i18next";
import { uploadManager } from "services/upload-manager";
import { getSelectedFiles, type SelectedState } from "utils/file";
import { PublicCollectionGalleryContext } from "utils/publicCollectionGallery";
@@ -98,7 +107,8 @@ export default function PublicCollectionGallery() {
const [publicFiles, setPublicFiles] = useState<EnteFile[] | undefined>(
undefined,
);
const [errorMessage, setErrorMessage] = useState<string>(null);
const [referralCode, setReferralCode] = useState<string>("");
const [errorMessage, setErrorMessage] = useState<string>("");
const [loading, setLoading] = useState(true);
const [isPasswordProtected, setIsPasswordProtected] = useState(false);
const [uploadTypeSelectorView, setUploadTypeSelectorView] = useState(false);
@@ -117,7 +127,6 @@ export default function PublicCollectionGallery() {
const credentials = useRef<PublicAlbumsCredentials | undefined>(undefined);
const collectionKey = useRef<string>(null);
const url = useRef<string>(null);
const referralCode = useRef<string>("");
const { saveGroups, onAddSaveGroup, onRemoveSaveGroup } = useSaveGroups();
@@ -180,8 +189,9 @@ export default function PublicCollectionGallery() {
const accessToken = t;
let accessTokenJWT: string | undefined;
if (collection) {
referralCode.current =
await savedLastPublicCollectionReferralCode();
setReferralCode(
(await savedLastPublicCollectionReferralCode()) ?? "",
);
setPublicCollection(collection);
setIsPasswordProtected(
!!collection.publicURLs[0]?.passwordEnabled,
@@ -223,13 +233,13 @@ export default function PublicCollectionGallery() {
try {
const { collection, referralCode: userReferralCode } =
await pullCollection(accessToken, collectionKey.current);
referralCode.current = userReferralCode;
setReferralCode(userReferralCode);
setPublicCollection(collection);
const isPasswordProtected =
!!collection.publicURLs[0]?.passwordEnabled;
setIsPasswordProtected(isPasswordProtected);
setErrorMessage(null);
setErrorMessage("");
// Remove the locally cached accessTokenJWT if the sharer has
// disabled password protection on the link.
@@ -390,7 +400,7 @@ export default function PublicCollectionGallery() {
publicCollection && publicFiles
? {
item: (
<ListHeader
<FileListHeader
{...{
publicCollection,
publicFiles,
@@ -398,27 +408,19 @@ export default function PublicCollectionGallery() {
}}
/>
),
tag: "header" as const,
height: 68,
height: fileListHeaderHeight,
}
: undefined,
[onAddSaveGroup, publicCollection, publicFiles],
);
const fileListFooter = useMemo(
() =>
onAddPhotos
? {
item: (
<CenteredFill sx={{ marginTop: "56px" }}>
<AddMorePhotosButton onClick={onAddPhotos} />
</CenteredFill>
),
height: 104,
}
: undefined,
[onAddPhotos],
);
const fileListFooter = useMemo(() => {
const props = { referralCode, onAddPhotos };
return {
item: <FileListFooter {...props} />,
height: fileListFooterHeightForProps(props),
};
}, [referralCode, onAddPhotos]);
if (loading && (!publicFiles || !credentials.current)) {
return <LoadingIndicator />;
@@ -460,10 +462,7 @@ export default function PublicCollectionGallery() {
}
// TODO: memo this (after the dependencies are traceable).
const context = {
credentials: credentials.current,
referralCode: referralCode.current,
};
const context = { credentials: credentials.current };
return (
<PublicCollectionGalleryContext.Provider value={context}>
@@ -627,13 +626,24 @@ const SelectedFileOptions: React.FC<SelectedFileOptionsProps> = ({
</Stack>
);
interface ListHeaderProps {
interface FileListHeaderProps {
publicCollection: Collection;
publicFiles: EnteFile[];
onAddSaveGroup: AddSaveGroup;
}
const ListHeader: React.FC<ListHeaderProps> = ({
/**
* The fixed height (in px) of {@link FileListHeader}.
*/
const fileListHeaderHeight = 68;
/**
* A header shown before the listing of files.
*
* It scrolls along with the content. It has a fixed height,
* {@link fileListHeaderHeight}.
*/
const FileListHeader: React.FC<FileListHeaderProps> = ({
publicCollection,
publicFiles,
onAddSaveGroup,
@@ -671,3 +681,85 @@ const ListHeader: React.FC<ListHeaderProps> = ({
</GalleryItemsHeaderAdapter>
);
};
interface FileListFooterProps {
referralCode?: string;
onAddPhotos?: () => void;
}
/**
* The dynamic (prop-depedent) height of {@link FileListFooter}.
*/
const fileListFooterHeightForProps = ({
referralCode,
onAddPhotos,
}: FileListFooterProps) => (onAddPhotos ? 104 : 0) + (referralCode ? 113 : 75);
/**
* A footer shown after the listing of files.
*
* It scrolls along with the content. It has a dynamic height, dependent on the
* props, calculated using {@link fileListFooterHeightForProps}.
*/
const FileListFooter: React.FC<FileListFooterProps> = ({
referralCode,
onAddPhotos,
}) => (
<Stack sx={{ flex: 1, alignSelf: "flex-end" }}>
{onAddPhotos && (
<CenteredFill>
<AddMorePhotosButton onClick={onAddPhotos} />
</CenteredFill>
)}
{/* Make the entire area tappable, otherwise it is hard to
get at on mobile devices. */}
<Link
color="text.muted"
sx={{
mt: "48px",
mb: "6px",
textAlign: "center",
"&:hover": { color: "inherit" },
}}
target="_blank"
href="https://ente.io"
>
<Typography variant="small">
<Trans
i18nKey="shared_using"
components={{
a: (
<Typography
variant="small"
component="span"
sx={{ color: "accent.main" }}
/>
),
}}
values={{ url: "ente.io" }}
/>
</Typography>
</Link>
{referralCode && (
<Typography
sx={{
mt: "6px",
mb: 0,
/* Negative margin to extend to edges by counteracting the
maximum margin that can be added by FileViewer. */
mx: "-24px",
padding: "8px",
bgcolor: "accent.main",
color: "accent.contrastText",
textAlign: "center",
}}
>
<Trans
i18nKey={"sharing_referral_code"}
values={{ referralCode }}
/>
</Typography>
)}
</Stack>
);

View File

@@ -8,11 +8,9 @@ export interface PublicCollectionGalleryContextType {
* undefined when we're in the default photos app context.
*/
credentials: PublicAlbumsCredentials | undefined;
referralCode: string | null;
}
export const PublicCollectionGalleryContext =
createContext<PublicCollectionGalleryContextType>({
credentials: undefined,
referralCode: null,
});

View File

@@ -38,8 +38,8 @@ interface GalleryItemsSummaryProps {
}
/**
* A component suitable for being used as a (non-sticky) summary displayed on
* top of the of a list of photos (or other items) shown in the gallery.
* A component suitable for being used as a summary displayed on top of the of a
* list of photos (or other items) shown in the gallery.
*/
export const GalleryItemsSummary: React.FC<GalleryItemsSummaryProps> = ({
name,