From e02b76d2d2147e36d368666771672b69c2e3dbb3 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Fri, 29 Nov 2024 13:32:11 +0530 Subject: [PATCH 01/16] Prune --- web/packages/shared/components/OverflowMenu.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/web/packages/shared/components/OverflowMenu.tsx b/web/packages/shared/components/OverflowMenu.tsx index ab4b71fb95..510277e4ed 100644 --- a/web/packages/shared/components/OverflowMenu.tsx +++ b/web/packages/shared/components/OverflowMenu.tsx @@ -90,7 +90,6 @@ interface OverflowMenuOptionProps { color?: ButtonProps["color"]; startIcon?: React.ReactNode; endIcon?: React.ReactNode; - keepOpenAfterClick?: boolean; children?: any; // To avoid changing old places without an audit, new code should use this // option explicitly to fix/tweak the alignment of the button label and @@ -103,7 +102,6 @@ export const OverflowMenuOption: React.FC = ({ color = "primary", startIcon, endIcon, - keepOpenAfterClick, centerAlign, children, }) => { @@ -111,9 +109,7 @@ export const OverflowMenuOption: React.FC = ({ const handleClick = () => { onClick(); - if (!keepOpenAfterClick) { - menuContext.close(); - } + menuContext.close(); }; return ( Date: Fri, 29 Nov 2024 13:39:51 +0530 Subject: [PATCH 02/16] Tweak --- .../shared/components/OverflowMenu.tsx | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/web/packages/shared/components/OverflowMenu.tsx b/web/packages/shared/components/OverflowMenu.tsx index 510277e4ed..a70a2378f6 100644 --- a/web/packages/shared/components/OverflowMenu.tsx +++ b/web/packages/shared/components/OverflowMenu.tsx @@ -10,7 +10,7 @@ import { type PaperProps, } from "@mui/material"; import Menu, { type MenuProps } from "@mui/material/Menu"; -import React, { createContext, useContext, useState } from "react"; +import React, { createContext, useContext, useMemo, useState } from "react"; const OverflowMenuContext = createContext({ // eslint-disable-next-line @typescript-eslint/no-empty-function @@ -45,26 +45,27 @@ export const OverflowMenu: React.FC = ({ triggerButtonProps, menuPaperProps, }) => { - const [sortByEl, setSortByEl] = useState( - null, + const [anchorEl, setAnchorEl] = useState(); + const context = useMemo( + () => ({ close: () => setAnchorEl(undefined) }), + [], ); - const handleClose = () => setSortByEl(null); return ( - + setSortByEl(event.currentTarget)} - aria-controls={sortByEl ? ariaControls : undefined} + onClick={(event) => setAnchorEl(event.currentTarget)} + aria-controls={anchorEl ? ariaControls : undefined} aria-haspopup="true" - aria-expanded={sortByEl ? "true" : undefined} + aria-expanded={anchorEl ? "true" : undefined} {...triggerButtonProps} > {triggerButtonIcon} setAnchorEl(undefined)} MenuListProps={{ disablePadding: true, "aria-labelledby": ariaControls, @@ -111,6 +112,7 @@ export const OverflowMenuOption: React.FC = ({ onClick(); menuContext.close(); }; + return ( Date: Fri, 29 Nov 2024 13:50:22 +0530 Subject: [PATCH 03/16] Tweak --- web/apps/auth/src/pages/auth.tsx | 2 +- .../Collections/CollectionHeader.tsx | 2 +- web/apps/photos/src/components/Export.tsx | 4 +-- .../photos/src/components/WatchFolder.tsx | 4 +-- web/apps/photos/src/pages/shared-albums.tsx | 2 +- .../components/CollectionsSortOptions.tsx | 2 +- .../components/gallery/PeopleHeader.tsx | 6 ++-- .../shared/components/OverflowMenu.tsx | 34 ++++++++++++++----- 8 files changed, 37 insertions(+), 19 deletions(-) diff --git a/web/apps/auth/src/pages/auth.tsx b/web/apps/auth/src/pages/auth.tsx index 06a4f51175..9297a1c5ea 100644 --- a/web/apps/auth/src/pages/auth.tsx +++ b/web/apps/auth/src/pages/auth.tsx @@ -156,7 +156,7 @@ const AuthNavbar: React.FC = () => { } > = ({ /> } > {collectionSummaryType == "trash" ? ( diff --git a/web/apps/photos/src/components/Export.tsx b/web/apps/photos/src/components/Export.tsx index 43418b73e6..f7283e539c 100644 --- a/web/apps/photos/src/components/Export.tsx +++ b/web/apps/photos/src/components/Export.tsx @@ -270,9 +270,9 @@ const DirectoryPathContainer = styled(LinkButton)( const ChangeDirectoryOption: React.FC = ({ onClick }) => ( } + triggerButtonProps={{ sx: { ml: 1 } }} > }> {t("CHANGE_FOLDER")} diff --git a/web/apps/photos/src/components/WatchFolder.tsx b/web/apps/photos/src/components/WatchFolder.tsx index eec2309a24..5913e04a77 100644 --- a/web/apps/photos/src/components/WatchFolder.tsx +++ b/web/apps/photos/src/components/WatchFolder.tsx @@ -311,14 +311,14 @@ interface EntryOptionsProps { const EntryOptions: React.FC = ({ confirmStopWatching }) => { return ( } menuPaperProps={{ sx: { backgroundColor: (theme) => theme.colors.background.elevated2, }, }} - ariaControls={"watch-mapping-option"} - triggerButtonIcon={} > = ({ /> {downloadEnabled && ( } > = ({ ...optProps }) => ( } menuPaperProps={{ sx: { diff --git a/web/packages/new/photos/components/gallery/PeopleHeader.tsx b/web/packages/new/photos/components/gallery/PeopleHeader.tsx index ca8a1bca2b..7f96b89d3a 100644 --- a/web/packages/new/photos/components/gallery/PeopleHeader.tsx +++ b/web/packages/new/photos/components/gallery/PeopleHeader.tsx @@ -147,7 +147,7 @@ const CGroupPersonHeader: React.FC = ({ person }) => { fileCount={person.fileIDs.length} /> } > = ({ fileCount={person.fileIDs.length} /> } > = ({ } > ; - children?: React.ReactNode; - ariaControls: string; + // backgroundColor; menuPaperProps?: Partial; } +/** + * A custom MUI {@link Menu} with some Ente specific styling applied to it. + */ export const StyledMenu = styled(Menu)` & .MuiPaper-root { margin: 16px auto; @@ -38,12 +50,18 @@ export const StyledMenu = styled(Menu)` } `; -export const OverflowMenu: React.FC = ({ - children, - ariaControls, +/** + * An overflow menu showing {@link OverflowMenuOptions}, alongwith a button to + * trigger the visibility of the menu. + */ +export const OverflowMenu: React.FC< + React.PropsWithChildren +> = ({ + ariaID, triggerButtonIcon, triggerButtonProps, menuPaperProps, + children, }) => { const [anchorEl, setAnchorEl] = useState(); const context = useMemo( @@ -54,7 +72,7 @@ export const OverflowMenu: React.FC = ({ setAnchorEl(event.currentTarget)} - aria-controls={anchorEl ? ariaControls : undefined} + aria-controls={anchorEl ? ariaID : undefined} aria-haspopup="true" aria-expanded={anchorEl ? "true" : undefined} {...triggerButtonProps} @@ -62,13 +80,13 @@ export const OverflowMenu: React.FC = ({ {triggerButtonIcon} setAnchorEl(undefined)} MenuListProps={{ disablePadding: true, - "aria-labelledby": ariaControls, + "aria-labelledby": ariaID, }} PaperProps={menuPaperProps} anchorOrigin={{ From 9fffbdb6facc3c8694005b86671238c2a3f55f22 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Fri, 29 Nov 2024 14:25:14 +0530 Subject: [PATCH 04/16] Don't pass an undefined anchorEl --- web/packages/shared/components/OverflowMenu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/packages/shared/components/OverflowMenu.tsx b/web/packages/shared/components/OverflowMenu.tsx index ffdc30f6e9..65f556b545 100644 --- a/web/packages/shared/components/OverflowMenu.tsx +++ b/web/packages/shared/components/OverflowMenu.tsx @@ -81,7 +81,7 @@ export const OverflowMenu: React.FC< setAnchorEl(undefined)} MenuListProps={{ From d9bd79e5efac32fbedaec12a8334d83897488c36 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Fri, 29 Nov 2024 14:32:47 +0530 Subject: [PATCH 05/16] Default --- web/apps/auth/src/pages/auth.tsx | 6 +----- web/apps/photos/src/components/Export.tsx | 7 +------ web/apps/photos/src/components/WatchFolder.tsx | 2 -- web/apps/photos/src/pages/shared-albums.tsx | 6 +----- .../photos/components/gallery/PeopleHeader.tsx | 16 +++------------- web/packages/shared/components/OverflowMenu.tsx | 7 +++++-- 6 files changed, 11 insertions(+), 33 deletions(-) diff --git a/web/apps/auth/src/pages/auth.tsx b/web/apps/auth/src/pages/auth.tsx index 9297a1c5ea..9c0502d46a 100644 --- a/web/apps/auth/src/pages/auth.tsx +++ b/web/apps/auth/src/pages/auth.tsx @@ -16,7 +16,6 @@ import { } from "@ente/shared/components/OverflowMenu"; import { AUTH_PAGES as PAGES } from "@ente/shared/constants/pages"; import LogoutOutlined from "@mui/icons-material/LogoutOutlined"; -import MoreHoriz from "@mui/icons-material/MoreHoriz"; import { Button, ButtonBase, @@ -155,10 +154,7 @@ const AuthNavbar: React.FC = () => { - } - > + } diff --git a/web/apps/photos/src/components/Export.tsx b/web/apps/photos/src/components/Export.tsx index f7283e539c..2f6f4b71d9 100644 --- a/web/apps/photos/src/components/Export.tsx +++ b/web/apps/photos/src/components/Export.tsx @@ -18,7 +18,6 @@ import { } from "@ente/shared/components/OverflowMenu"; import { CustomError } from "@ente/shared/error"; import FolderIcon from "@mui/icons-material/Folder"; -import MoreHoriz from "@mui/icons-material/MoreHoriz"; import { Box, Button, @@ -269,11 +268,7 @@ const DirectoryPathContainer = styled(LinkButton)( ); const ChangeDirectoryOption: React.FC = ({ onClick }) => ( - } - triggerButtonProps={{ sx: { ml: 1 } }} - > + }> {t("CHANGE_FOLDER")} diff --git a/web/apps/photos/src/components/WatchFolder.tsx b/web/apps/photos/src/components/WatchFolder.tsx index 5913e04a77..c80d336412 100644 --- a/web/apps/photos/src/components/WatchFolder.tsx +++ b/web/apps/photos/src/components/WatchFolder.tsx @@ -23,7 +23,6 @@ import CheckIcon from "@mui/icons-material/Check"; import DoNotDisturbOutlinedIcon from "@mui/icons-material/DoNotDisturbOutlined"; import FolderCopyOutlinedIcon from "@mui/icons-material/FolderCopyOutlined"; import FolderOpenIcon from "@mui/icons-material/FolderOpen"; -import MoreHorizIcon from "@mui/icons-material/MoreHoriz"; import { Box, Button, @@ -312,7 +311,6 @@ const EntryOptions: React.FC = ({ confirmStopWatching }) => { return ( } menuPaperProps={{ sx: { backgroundColor: (theme) => diff --git a/web/apps/photos/src/pages/shared-albums.tsx b/web/apps/photos/src/pages/shared-albums.tsx index 48afe2ee5a..7c1664e765 100644 --- a/web/apps/photos/src/pages/shared-albums.tsx +++ b/web/apps/photos/src/pages/shared-albums.tsx @@ -44,7 +44,6 @@ import AddPhotoAlternateOutlined from "@mui/icons-material/AddPhotoAlternateOutl import CloseIcon from "@mui/icons-material/Close"; import DownloadIcon from "@mui/icons-material/Download"; import FileDownloadOutlinedIcon from "@mui/icons-material/FileDownloadOutlined"; -import MoreHoriz from "@mui/icons-material/MoreHoriz"; import type { ButtonProps, IconButtonProps } from "@mui/material"; import { Box, Button, IconButton, Stack, styled, Tooltip } from "@mui/material"; import Typography from "@mui/material/Typography"; @@ -715,10 +714,7 @@ const ListHeader: React.FC = ({ fileCount={publicFiles.length} /> {downloadEnabled && ( - } - > + } onClick={downloadAllFiles} diff --git a/web/packages/new/photos/components/gallery/PeopleHeader.tsx b/web/packages/new/photos/components/gallery/PeopleHeader.tsx index 7f96b89d3a..73a2e680f6 100644 --- a/web/packages/new/photos/components/gallery/PeopleHeader.tsx +++ b/web/packages/new/photos/components/gallery/PeopleHeader.tsx @@ -40,7 +40,6 @@ import ClearIcon from "@mui/icons-material/Clear"; import EditIcon from "@mui/icons-material/Edit"; import HideImageOutlinedIcon from "@mui/icons-material/HideImageOutlined"; import ListAltOutlinedIcon from "@mui/icons-material/ListAltOutlined"; -import MoreHorizIcon from "@mui/icons-material/MoreHoriz"; import RestoreIcon from "@mui/icons-material/Restore"; import VisibilityOutlinedIcon from "@mui/icons-material/VisibilityOutlined"; import { @@ -146,10 +145,7 @@ const CGroupPersonHeader: React.FC = ({ person }) => { name={name} fileCount={person.fileIDs.length} /> - } - > + } centerAlign @@ -210,10 +206,7 @@ const IgnoredPersonHeader: React.FC = ({ nameProps={{ color: "text.muted" }} fileCount={person.fileIDs.length} /> - } - > + } centerAlign @@ -271,10 +264,7 @@ const ClusterPersonHeader: React.FC = ({ - } - > + } centerAlign diff --git a/web/packages/shared/components/OverflowMenu.tsx b/web/packages/shared/components/OverflowMenu.tsx index 65f556b545..545534318f 100644 --- a/web/packages/shared/components/OverflowMenu.tsx +++ b/web/packages/shared/components/OverflowMenu.tsx @@ -10,6 +10,7 @@ import { type PaperProps, } from "@mui/material"; import Menu, { type MenuProps } from "@mui/material/Menu"; +import MoreHorizIcon from "@mui/icons-material/MoreHoriz"; import React, { createContext, useContext, useMemo, useState } from "react"; const OverflowMenuContext = createContext({ @@ -24,8 +25,10 @@ interface OverflowMenuProps { ariaID: string; /** * The icon for the trigger button. + * + * If not provided, then by default the MoreHoriz icon from MUI is used. */ - triggerButtonIcon: React.ReactNode; + triggerButtonIcon?: React.ReactNode; /** * Optional additional properties for the trigger icon button. */ @@ -77,7 +80,7 @@ export const OverflowMenu: React.FC< aria-expanded={anchorEl ? "true" : undefined} {...triggerButtonProps} > - {triggerButtonIcon} + {triggerButtonIcon ?? } Date: Fri, 29 Nov 2024 14:43:30 +0530 Subject: [PATCH 06/16] Tweak --- .../new/photos/components/CollectionsSortOptions.tsx | 10 +++++----- web/packages/new/photos/components/gallery/BarImpl.tsx | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/web/packages/new/photos/components/CollectionsSortOptions.tsx b/web/packages/new/photos/components/CollectionsSortOptions.tsx index 88d8132dd6..d33bce1ed1 100644 --- a/web/packages/new/photos/components/CollectionsSortOptions.tsx +++ b/web/packages/new/photos/components/CollectionsSortOptions.tsx @@ -24,10 +24,10 @@ interface CollectionsSortOptionsProps { */ nestedInDialog?: boolean; /** - * Set this to true to disable the background in the button that triggers - * the menu. + * Set this to true to disable the background for the icon button that + * triggers the menu. */ - disableTriggerButtonBackground?: boolean; + transparentTriggerButtonBackground?: boolean; } /** @@ -37,7 +37,7 @@ interface CollectionsSortOptionsProps { */ export const CollectionsSortOptions: React.FC = ({ nestedInDialog, - disableTriggerButtonBackground, + transparentTriggerButtonBackground, ...optProps }) => ( = ({ triggerButtonProps={{ sx: { backgroundColor: (theme) => - disableTriggerButtonBackground + transparentTriggerButtonBackground ? undefined : theme.colors.fill.faint, }, diff --git a/web/packages/new/photos/components/gallery/BarImpl.tsx b/web/packages/new/photos/components/gallery/BarImpl.tsx index 6cdaf4b6ea..43ca6853ef 100644 --- a/web/packages/new/photos/components/gallery/BarImpl.tsx +++ b/web/packages/new/photos/components/gallery/BarImpl.tsx @@ -223,7 +223,7 @@ export const GalleryBarImpl: React.FC = ({ From 8e1450c6c66fb1950e5677f4a0a9f0ad6862b1e5 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Fri, 29 Nov 2024 14:46:39 +0530 Subject: [PATCH 07/16] Convert deprecated method --- web/packages/shared/components/OverflowMenu.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/web/packages/shared/components/OverflowMenu.tsx b/web/packages/shared/components/OverflowMenu.tsx index 545534318f..844246c8e4 100644 --- a/web/packages/shared/components/OverflowMenu.tsx +++ b/web/packages/shared/components/OverflowMenu.tsx @@ -1,4 +1,5 @@ import { FluidContainer } from "@ente/shared/components/Container"; +import MoreHorizIcon from "@mui/icons-material/MoreHoriz"; import { Box, IconButton, @@ -10,7 +11,6 @@ import { type PaperProps, } from "@mui/material"; import Menu, { type MenuProps } from "@mui/material/Menu"; -import MoreHorizIcon from "@mui/icons-material/MoreHoriz"; import React, { createContext, useContext, useMemo, useState } from "react"; const OverflowMenuContext = createContext({ @@ -91,7 +91,9 @@ export const OverflowMenu: React.FC< disablePadding: true, "aria-labelledby": ariaID, }} - PaperProps={menuPaperProps} + slotProps={{ + paper: menuPaperProps, + }} anchorOrigin={{ vertical: "bottom", horizontal: "right", From 0dbd8a0f8c9c1e5bbddce803bc3fb37db9f29359 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Fri, 29 Nov 2024 14:50:33 +0530 Subject: [PATCH 08/16] Memo context to avoid unnecessary renders --- web/apps/photos/src/pages/_app.tsx | 37 +++++++++++++++--------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/web/apps/photos/src/pages/_app.tsx b/web/apps/photos/src/pages/_app.tsx index ab1b816649..08354383ca 100644 --- a/web/apps/photos/src/pages/_app.tsx +++ b/web/apps/photos/src/pages/_app.tsx @@ -44,7 +44,7 @@ import { t } from "i18next"; import type { AppProps } from "next/app"; import { useRouter } from "next/router"; import "photoswipe/dist/photoswipe.css"; -import { useCallback, useEffect, useState } from "react"; +import { useCallback, useEffect, useMemo, useState } from "react"; import LoadingBar from "react-top-loading-bar"; import { resumeExportsIfNeeded } from "services/export"; import { photosLogout } from "services/logout"; @@ -156,8 +156,6 @@ export default function App({ Component, pageProps }: AppProps) { setNotificationView(true); }, [notificationAttributes]); - const showNavBar = (show: boolean) => setShowNavBar(show); - const onGenericError = useCallback((e: unknown) => { log.error(e); // The generic error handler is sometimes called in the context of @@ -172,21 +170,24 @@ export default function App({ Component, pageProps }: AppProps) { const logout = useCallback(() => void photosLogout(), []); - const appContext = { - showNavBar, - showLoadingBar, - hideLoadingBar, - watchFolderView, - setWatchFolderView, - watchFolderFiles, - setWatchFolderFiles, - setNotificationAttributes, - themeColor, - setThemeColor, - showMiniDialog, - onGenericError, - logout, - }; + const appContext = useMemo( + () => ({ + showNavBar: (show: boolean) => setShowNavBar(show), + showLoadingBar, + hideLoadingBar, + watchFolderView, + setWatchFolderView, + watchFolderFiles, + setWatchFolderFiles, + setNotificationAttributes, + themeColor, + setThemeColor, + showMiniDialog, + onGenericError, + logout, + }), + [showLoadingBar, hideLoadingBar, onGenericError, logout], + ); const title = isI18nReady ? t("title_photos") : staticAppTitle; From 85673177f58e057a5bbe9dfb628d5b5c97846a5b Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Fri, 29 Nov 2024 14:59:58 +0530 Subject: [PATCH 09/16] Keep JSX together --- web/apps/photos/src/pages/shared-albums.tsx | 66 ++++++++++----------- 1 file changed, 31 insertions(+), 35 deletions(-) diff --git a/web/apps/photos/src/pages/shared-albums.tsx b/web/apps/photos/src/pages/shared-albums.tsx index 7c1664e765..2643cdc8c4 100644 --- a/web/apps/photos/src/pages/shared-albums.tsx +++ b/web/apps/photos/src/pages/shared-albums.tsx @@ -423,41 +423,6 @@ export default function PublicCollectionGallery() { await syncWithRemote(); }; - if (loading) { - if (!publicFiles) { - return ( - - - - ); - } - } else { - if (errorMessage) { - return {errorMessage}; - } - if (isPasswordProtected && !passwordJWTToken.current) { - return ( - - - {t("password")} - - {t("link_password_description")} - - - - - ); - } - if (!publicFiles) { - return {t("NOT_FOUND")}; - } - } - const clearSelection = () => { if (!selected?.count) { return; @@ -487,6 +452,37 @@ export default function PublicCollectionGallery() { } }; + if (loading) { + if (!publicFiles) { + return ( + + + + ); + } + } else if (errorMessage) { + return {errorMessage}; + } else if (isPasswordProtected && !passwordJWTToken.current) { + return ( + + + {t("password")} + + {t("link_password_description")} + + + + + ); + } else if (!publicFiles) { + return {t("NOT_FOUND")}; + } + return ( Date: Fri, 29 Nov 2024 15:03:39 +0530 Subject: [PATCH 10/16] Reorder --- web/apps/photos/src/pages/shared-albums.tsx | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/web/apps/photos/src/pages/shared-albums.tsx b/web/apps/photos/src/pages/shared-albums.tsx index 2643cdc8c4..3c6e7cb1d8 100644 --- a/web/apps/photos/src/pages/shared-albums.tsx +++ b/web/apps/photos/src/pages/shared-albums.tsx @@ -452,14 +452,14 @@ export default function PublicCollectionGallery() { } }; - if (loading) { - if (!publicFiles) { - return ( - - - - ); - } + if (loading && !publicFiles) { + return ( + + + + ); + } else if (!publicFiles) { + return {t("NOT_FOUND")}; } else if (errorMessage) { return {errorMessage}; } else if (isPasswordProtected && !passwordJWTToken.current) { @@ -479,8 +479,6 @@ export default function PublicCollectionGallery() { ); - } else if (!publicFiles) { - return {t("NOT_FOUND")}; } return ( From 70eb18fe3bbc4ff76a1562d4b4a7bc007d1b74ec Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Fri, 29 Nov 2024 16:14:53 +0530 Subject: [PATCH 11/16] Combine --- web/apps/photos/src/pages/shared-albums.tsx | 83 +++++++++++---------- 1 file changed, 43 insertions(+), 40 deletions(-) diff --git a/web/apps/photos/src/pages/shared-albums.tsx b/web/apps/photos/src/pages/shared-albums.tsx index 3c6e7cb1d8..5ed99e9478 100644 --- a/web/apps/photos/src/pages/shared-albums.tsx +++ b/web/apps/photos/src/pages/shared-albums.tsx @@ -8,7 +8,7 @@ import { useIsTouchscreen, } from "@/base/components/utils/hooks"; import { sharedCryptoWorker } from "@/base/crypto"; -import { isHTTP401Error } from "@/base/http"; +import { isHTTP401Error, PublicAlbumsCredentials } from "@/base/http"; import log from "@/base/log"; import { downloadManager } from "@/gallery/services/download"; import { updateShouldDisableCFUploadProxy } from "@/gallery/services/upload"; @@ -85,9 +85,7 @@ import { downloadSelectedFiles, getSelectedFiles } from "utils/file"; import { PublicCollectionGalleryContext } from "utils/publicCollectionGallery"; export default function PublicCollectionGallery() { - const token = useRef(null); - // passwordJWTToken refers to the jwt token which is used for album protected by password. - const passwordJWTToken = useRef(null); + const credentials = useRef(); const collectionKey = useRef(null); const url = useRef(null); const referralCode = useRef(""); @@ -241,16 +239,13 @@ export default function PublicCollectionGallery() { ck.length < 50 ? await cryptoWorker.toB64(bs58.decode(ck)) : await cryptoWorker.fromHex(ck); - token.current = t; - downloadManager.setPublicAlbumsCredentials({ - accessToken: token.current, - }); - await updateShouldDisableCFUploadProxy(); collectionKey.current = dck; url.current = window.location.href; const localCollection = await getLocalPublicCollection( collectionKey.current, ); + const accessToken = t; + let accessTokenJWT: string | undefined; if (localCollection) { referralCode.current = await getReferralCode(); const sortAsc: boolean = @@ -259,20 +254,20 @@ export default function PublicCollectionGallery() { const isPasswordProtected = localCollection?.publicURLs?.[0]?.passwordEnabled; setIsPasswordProtected(isPasswordProtected); - const collectionUID = getPublicCollectionUID(token.current); + const collectionUID = getPublicCollectionUID(accessToken); const localFiles = await getLocalPublicFiles(collectionUID); const localPublicFiles = sortFiles( mergeMetadata(localFiles), sortAsc, ); setPublicFiles(localPublicFiles); - passwordJWTToken.current = + accessTokenJWT = await getLocalPublicCollectionPassword(collectionUID); - downloadManager.setPublicAlbumsCredentials({ - accessToken: token.current, - accessTokenJWT: passwordJWTToken.current, - }); } + credentials.current = { accessToken, accessTokenJWT }; + downloadManager.setPublicAlbumsCredentials(credentials.current); + // Update the CF proxy flag, but we don't need to block on it. + void updateShouldDisableCFUploadProxy(); await syncWithRemote(); } finally { if (!redirectingToWebsite) { @@ -321,12 +316,14 @@ export default function PublicCollectionGallery() { }, [onAddPhotos]); const syncWithRemote = async () => { - const collectionUID = getPublicCollectionUID(token.current); + const collectionUID = getPublicCollectionUID( + credentials.current.accessToken, + ); try { showLoadingBar(); setLoading(true); const [collection, userReferralCode] = await getPublicCollection( - token.current, + credentials.current.accessToken, collectionKey.current, ); referralCode.current = userReferralCode; @@ -337,19 +334,26 @@ export default function PublicCollectionGallery() { setIsPasswordProtected(isPasswordProtected); setErrorMessage(null); - // remove outdated password, sharer has disabled the password - if (!isPasswordProtected && passwordJWTToken.current) { - passwordJWTToken.current = null; + // Remove the locally saved outdated password token if the sharer + // has disabled password protection on the link. + if (!isPasswordProtected && credentials.current.accessTokenJWT) { + credentials.current.accessTokenJWT = undefined; + downloadManager.setPublicAlbumsCredentials(credentials.current); savePublicCollectionPassword(collectionUID, null); } + + if (isPasswordProtected && !credentials.current.accessTokenJWT) { + await removePublicFiles(collectionUID); + } + if ( !isPasswordProtected || - (isPasswordProtected && passwordJWTToken.current) + (isPasswordProtected && credentials.current.accessTokenJWT) ) { try { await syncPublicFiles( - token.current, - passwordJWTToken.current, + credentials.current.accessToken, + credentials.current.accessTokenJWT, collection, setPublicFiles, ); @@ -358,13 +362,13 @@ export default function PublicCollectionGallery() { if (parsedError.message === CustomError.TOKEN_EXPIRED) { // passwordToken has expired, sharer has changed the password, // so,clearing local cache token value to prompt user to re-enter password - passwordJWTToken.current = null; + credentials.current.accessTokenJWT = undefined; + downloadManager.setPublicAlbumsCredentials( + credentials.current, + ); } } } - if (isPasswordProtected && !passwordJWTToken.current) { - await removePublicFiles(collectionUID); - } } catch (e) { const parsedError = parseSharingErrorCodes(e); if ( @@ -398,18 +402,17 @@ export default function PublicCollectionGallery() { setFieldError, ) => { try { - const jwtToken = await verifyPublicAlbumPassword( + const accessTokenJWT = await verifyPublicAlbumPassword( publicCollection.publicURLs[0]!, password, - token.current, + credentials.current.accessToken, ); - passwordJWTToken.current = jwtToken; - downloadManager.setPublicAlbumsCredentials({ - accessToken: token.current, - accessTokenJWT: passwordJWTToken.current, - }); - const collectionUID = getPublicCollectionUID(token.current); - await savePublicCollectionPassword(collectionUID, jwtToken); + credentials.current.accessTokenJWT = accessTokenJWT; + downloadManager.setPublicAlbumsCredentials(credentials.current); + const collectionUID = getPublicCollectionUID( + credentials.current.accessToken, + ); + await savePublicCollectionPassword(collectionUID, accessTokenJWT); } catch (e) { log.error("Failed to verifyLinkPassword", e); if (isHTTP401Error(e)) { @@ -452,7 +455,7 @@ export default function PublicCollectionGallery() { } }; - if (loading && !publicFiles) { + if (loading && (!publicFiles || !credentials.current)) { return ( @@ -462,7 +465,7 @@ export default function PublicCollectionGallery() { return {t("NOT_FOUND")}; } else if (errorMessage) { return {errorMessage}; - } else if (isPasswordProtected && !passwordJWTToken.current) { + } else if (isPasswordProtected && !credentials.current.accessTokenJWT) { return ( @@ -484,9 +487,9 @@ export default function PublicCollectionGallery() { return ( Date: Fri, 29 Nov 2024 16:56:53 +0530 Subject: [PATCH 12/16] Propogate --- .../photos/src/components/PhotoList/index.tsx | 8 ++-- .../components/PhotoViewer/FileInfo/index.tsx | 4 +- .../src/components/PhotoViewer/index.tsx | 2 +- .../components/Upload/UploadTypeSelector.tsx | 2 +- .../photos/src/components/Upload/Uploader.tsx | 14 +++---- web/apps/photos/src/pages/shared-albums.tsx | 21 +++++----- .../src/services/upload/upload-service.ts | 38 ++++++++----------- .../src/services/upload/uploadManager.ts | 21 +++++----- .../utils/publicCollectionGallery/index.ts | 14 ++++--- 9 files changed, 56 insertions(+), 68 deletions(-) diff --git a/web/apps/photos/src/components/PhotoList/index.tsx b/web/apps/photos/src/components/PhotoList/index.tsx index a6dfaa3a5a..f6a3aeee1f 100644 --- a/web/apps/photos/src/components/PhotoList/index.tsx +++ b/web/apps/photos/src/components/PhotoList/index.tsx @@ -325,7 +325,7 @@ export function PhotoList({ timeStampList.push(getEmptyListItem()); } timeStampList.push(getVacuumItem(timeStampList)); - if (publicCollectionGalleryContext.accessedThroughSharedURL) { + if (publicCollectionGalleryContext.credentials) { if (publicCollectionGalleryContext.photoListFooter) { timeStampList.push( getPhotoListFooter( @@ -396,7 +396,7 @@ export function PhotoList({ if (hasFooter) { return timeStampList; } - if (publicCollectionGalleryContext.accessedThroughSharedURL) { + if (publicCollectionGalleryContext.credentials) { if (publicCollectionGalleryContext.photoListFooter) { return [ ...timeStampList, @@ -413,7 +413,7 @@ export function PhotoList({ } }); }, [ - publicCollectionGalleryContext.accessedThroughSharedURL, + publicCollectionGalleryContext.credentials, showAppDownloadBanner, publicCollectionGalleryContext.photoListFooter, ]); @@ -521,7 +521,7 @@ export function PhotoList({ const getVacuumItem = (timeStampList) => { let footerHeight; - if (publicCollectionGalleryContext.accessedThroughSharedURL) { + if (publicCollectionGalleryContext.credentials) { footerHeight = publicCollectionGalleryContext.referralCode ? ALBUM_FOOTER_HEIGHT_WITH_REFERRAL : ALBUM_FOOTER_HEIGHT; diff --git a/web/apps/photos/src/components/PhotoViewer/FileInfo/index.tsx b/web/apps/photos/src/components/PhotoViewer/FileInfo/index.tsx index df4a4ab0a1..f140a3b05c 100644 --- a/web/apps/photos/src/components/PhotoViewer/FileInfo/index.tsx +++ b/web/apps/photos/src/components/PhotoViewer/FileInfo/index.tsx @@ -213,7 +213,7 @@ export const FileInfo: React.FC = ({ title={t("location")} caption={ !mapEnabled || - publicCollectionGalleryContext.accessedThroughSharedURL ? ( + publicCollectionGalleryContext.credentials ? ( = ({ /> } /> - {!publicCollectionGalleryContext.accessedThroughSharedURL && ( + {!publicCollectionGalleryContext.credentials && ( = ({ if ( open && directlyShowUploadFiles && - publicCollectionGalleryContext.accessedThroughSharedURL + publicCollectionGalleryContext.credentials ) { uploadFiles(); onClose(); diff --git a/web/apps/photos/src/components/Upload/Uploader.tsx b/web/apps/photos/src/components/Upload/Uploader.tsx index 505fd992ae..1c3c17e108 100644 --- a/web/apps/photos/src/components/Upload/Uploader.tsx +++ b/web/apps/photos/src/components/Upload/Uploader.tsx @@ -249,7 +249,7 @@ export default function Uploader({ setUploadProgressView, }, onUploadFile, - publicCollectionGalleryContext, + publicCollectionGalleryContext.credentials, ); if (uploadManager.isUploadRunning()) { @@ -288,11 +288,7 @@ export default function Uploader({ setDesktopZipItems(zipItems); }); } - }, [ - publicCollectionGalleryContext.accessedThroughSharedURL, - publicCollectionGalleryContext.token, - publicCollectionGalleryContext.passwordToken, - ]); + }, [publicCollectionGalleryContext.credentials]); // Handle selected files when user selects files for upload through the open // file / open folder selection dialog, or drag-and-drops them. @@ -417,10 +413,10 @@ export default function Uploader({ props.setLoading(false); (async () => { - if (publicCollectionGalleryContext.accessedThroughSharedURL) { + if (publicCollectionGalleryContext.credentials) { const uploaderName = await getPublicCollectionUploaderName( getPublicCollectionUID( - publicCollectionGalleryContext.token, + publicCollectionGalleryContext.credentials.accessToken, ), ); uploaderNameRef.current = uploaderName; @@ -727,7 +723,7 @@ export default function Uploader({ if (!skipSave) { savePublicCollectionUploaderName( getPublicCollectionUID( - publicCollectionGalleryContext.token, + publicCollectionGalleryContext.credentials.accessToken, ), uploaderName, ); diff --git a/web/apps/photos/src/pages/shared-albums.tsx b/web/apps/photos/src/pages/shared-albums.tsx index 5ed99e9478..7c3acdb66a 100644 --- a/web/apps/photos/src/pages/shared-albums.tsx +++ b/web/apps/photos/src/pages/shared-albums.tsx @@ -461,7 +461,7 @@ export default function PublicCollectionGallery() { ); - } else if (!publicFiles) { + } else if (!publicFiles || !credentials.current) { return {t("NOT_FOUND")}; } else if (errorMessage) { return {errorMessage}; @@ -484,17 +484,16 @@ export default function PublicCollectionGallery() { ); } + // TODO: memo this (after the dependencies are traceable). + const context = { + credentials: credentials.current, + referralCode: referralCode.current, + photoListHeader, + photoListFooter, + }; + return ( - + | undefined; - init(publicUploadProps: PublicUploadProps) { - this.publicUploadProps = publicUploadProps; + init(publicAlbumsCredentials: PublicAlbumsCredentials | undefined) { + this.publicAlbumsCredentials = publicAlbumsCredentials; } logout() { this.uploadURLs = []; this.pendingUploadCount = 0; - this.publicUploadProps = undefined; + this.publicAlbumsCredentials = undefined; this.activeUploadURLRefill = undefined; } @@ -153,11 +151,12 @@ class UploadService { } async uploadFile(uploadFile: UploadFile) { - if (this.publicUploadProps.accessedThroughSharedURL) { + if (this.publicAlbumsCredentials) { return publicUploadHttpClient.uploadFile( uploadFile, - this.publicUploadProps.token, - this.publicUploadProps.passwordToken, + // TODO: publicAlbumsCredentials + this.publicAlbumsCredentials.accessToken, + this.publicAlbumsCredentials.accessTokenJWT, ); } else { return UploadHttpClient.uploadFile(uploadFile); @@ -188,16 +187,10 @@ class UploadService { private async _refillUploadURLs() { let urls: UploadURL[]; - if (this.publicUploadProps.accessedThroughSharedURL) { - if (!this.publicUploadProps.token) { - throw Error(CustomError.TOKEN_MISSING); - } + if (this.publicAlbumsCredentials) { urls = await publicUploadHttpClient.fetchUploadURLs( this.pendingUploadCount, - { - accessToken: this.publicUploadProps.token, - accessTokenJWT: this.publicUploadProps.passwordToken, - }, + this.publicAlbumsCredentials, ); } else { urls = await UploadHttpClient.fetchUploadURLs( @@ -208,11 +201,12 @@ class UploadService { } async fetchMultipartUploadURLs(count: number) { - if (this.publicUploadProps.accessedThroughSharedURL) { + if (this.publicAlbumsCredentials) { + // TODO: publicAlbumsCredentials return await publicUploadHttpClient.fetchMultipartUploadURLs( count, - this.publicUploadProps.token, - this.publicUploadProps.passwordToken, + this.publicAlbumsCredentials.accessToken, + this.publicAlbumsCredentials.accessTokenJWT, ); } else { return await UploadHttpClient.fetchMultipartUploadURLs(count); diff --git a/web/apps/photos/src/services/upload/uploadManager.ts b/web/apps/photos/src/services/upload/uploadManager.ts index f49925210e..474a4645f2 100644 --- a/web/apps/photos/src/services/upload/uploadManager.ts +++ b/web/apps/photos/src/services/upload/uploadManager.ts @@ -2,6 +2,7 @@ import { isDesktop } from "@/base/app"; import { createComlinkCryptoWorker } from "@/base/crypto"; import { type CryptoWorker } from "@/base/crypto/worker"; import { lowercaseExtension, nameAndExtension } from "@/base/file-name"; +import type { PublicAlbumsCredentials } from "@/base/http"; import log from "@/base/log"; import type { Electron } from "@/base/types/ipc"; import { ComlinkWorker } from "@/base/worker/comlink-worker"; @@ -96,12 +97,6 @@ export interface LivePhotoAssets { video: UploadItem; } -export interface PublicUploadProps { - token: string; - passwordToken: string; - accessedThroughSharedURL: boolean; -} - interface UploadCancelStatus { value: boolean; } @@ -325,7 +320,7 @@ class UploadManager { private onUploadFile: (file: EnteFile) => void; private collections: Map; private uploadInProgress: boolean; - private publicUploadProps: PublicUploadProps; + private publicAlbumsCredentials: PublicAlbumsCredentials | undefined; private uploaderName: string; private uiService: UIService; @@ -336,12 +331,12 @@ class UploadManager { public async init( progressUpdater: ProgressUpdater, onUploadFile: (file: EnteFile) => void, - publicCollectProps: PublicUploadProps, + publicAlbumsCredentials: PublicAlbumsCredentials | undefined, ) { this.uiService.init(progressUpdater); - UploadService.init(publicCollectProps); + UploadService.init(publicAlbumsCredentials); this.onUploadFile = onUploadFile; - this.publicUploadProps = publicCollectProps; + this.publicAlbumsCredentials = publicAlbumsCredentials; } logout() { @@ -497,9 +492,11 @@ class UploadManager { }; private async updateExistingFilesAndCollections(collections: Collection[]) { - if (this.publicUploadProps.accessedThroughSharedURL) { + if (this.publicAlbumsCredentials) { this.existingFiles = await getLocalPublicFiles( - getPublicCollectionUID(this.publicUploadProps.token), + getPublicCollectionUID( + this.publicAlbumsCredentials.accessToken, + ), ); } else { this.existingFiles = getUserOwnedFiles(await getLocalFiles()); diff --git a/web/apps/photos/src/utils/publicCollectionGallery/index.ts b/web/apps/photos/src/utils/publicCollectionGallery/index.ts index c0e4cc9ff6..d5334d7f3f 100644 --- a/web/apps/photos/src/utils/publicCollectionGallery/index.ts +++ b/web/apps/photos/src/utils/publicCollectionGallery/index.ts @@ -1,21 +1,23 @@ +import type { PublicAlbumsCredentials } from "@/base/http"; import { TimeStampListItem } from "components/PhotoList"; import { createContext } from "react"; export interface PublicCollectionGalleryContextType { - token: string; - passwordToken: string; + /** + * The {@link PublicAlbumsCredentials} to use. These are guaranteed to be + * set if we are in the context of the public albums app, and will be + * undefined when we're in the default photos app context. + */ + credentials: PublicAlbumsCredentials | undefined; referralCode: string | null; - accessedThroughSharedURL: boolean; photoListHeader: TimeStampListItem; photoListFooter: TimeStampListItem; } export const PublicCollectionGalleryContext = createContext({ - token: null, - passwordToken: null, + credentials: undefined, referralCode: null, - accessedThroughSharedURL: false, photoListHeader: null, photoListFooter: null, }); From 929224a2cb63c545d41cc36272cdec2f3a40c58a Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Fri, 29 Nov 2024 17:03:38 +0530 Subject: [PATCH 13/16] Reorder --- web/apps/photos/src/pages/shared-albums.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/apps/photos/src/pages/shared-albums.tsx b/web/apps/photos/src/pages/shared-albums.tsx index 7c3acdb66a..980c62348a 100644 --- a/web/apps/photos/src/pages/shared-albums.tsx +++ b/web/apps/photos/src/pages/shared-albums.tsx @@ -461,8 +461,6 @@ export default function PublicCollectionGallery() { ); - } else if (!publicFiles || !credentials.current) { - return {t("NOT_FOUND")}; } else if (errorMessage) { return {errorMessage}; } else if (isPasswordProtected && !credentials.current.accessTokenJWT) { @@ -482,6 +480,8 @@ export default function PublicCollectionGallery() { ); + } else if (!publicFiles || !credentials.current) { + return {t("NOT_FOUND")}; } // TODO: memo this (after the dependencies are traceable). From b47d541a099e55a781d7aac46fc18b11dc6e801f Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Fri, 29 Nov 2024 17:14:18 +0530 Subject: [PATCH 14/16] Missing dep --- web/apps/photos/src/pages/_app.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/web/apps/photos/src/pages/_app.tsx b/web/apps/photos/src/pages/_app.tsx index 08354383ca..a0aacfc5d6 100644 --- a/web/apps/photos/src/pages/_app.tsx +++ b/web/apps/photos/src/pages/_app.tsx @@ -186,7 +186,13 @@ export default function App({ Component, pageProps }: AppProps) { onGenericError, logout, }), - [showLoadingBar, hideLoadingBar, onGenericError, logout], + [ + showLoadingBar, + hideLoadingBar, + showMiniDialog, + onGenericError, + logout, + ], ); const title = isI18nReady ? t("title_photos") : staticAppTitle; From 52d5ab31aaaebea8151e24c6b1c0b5cd9aa86532 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Fri, 29 Nov 2024 17:18:16 +0530 Subject: [PATCH 15/16] Retain order --- web/apps/photos/src/pages/shared-albums.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/web/apps/photos/src/pages/shared-albums.tsx b/web/apps/photos/src/pages/shared-albums.tsx index 980c62348a..241f31fe15 100644 --- a/web/apps/photos/src/pages/shared-albums.tsx +++ b/web/apps/photos/src/pages/shared-albums.tsx @@ -342,10 +342,6 @@ export default function PublicCollectionGallery() { savePublicCollectionPassword(collectionUID, null); } - if (isPasswordProtected && !credentials.current.accessTokenJWT) { - await removePublicFiles(collectionUID); - } - if ( !isPasswordProtected || (isPasswordProtected && credentials.current.accessTokenJWT) @@ -369,6 +365,10 @@ export default function PublicCollectionGallery() { } } } + + if (isPasswordProtected && !credentials.current.accessTokenJWT) { + await removePublicFiles(collectionUID); + } } catch (e) { const parsedError = parseSharingErrorCodes(e); if ( From 33bda1a4a19dceae26be22f84c06e53c9ff06a1a Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Fri, 29 Nov 2024 17:20:54 +0530 Subject: [PATCH 16/16] Doc --- web/packages/shared/components/OverflowMenu.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/web/packages/shared/components/OverflowMenu.tsx b/web/packages/shared/components/OverflowMenu.tsx index 844246c8e4..2469094358 100644 --- a/web/packages/shared/components/OverflowMenu.tsx +++ b/web/packages/shared/components/OverflowMenu.tsx @@ -33,7 +33,10 @@ interface OverflowMenuProps { * Optional additional properties for the trigger icon button. */ triggerButtonProps?: Partial; - // backgroundColor; + /** + * Optional additional properties for the MUI {@link Paper} that underlies + * the {@link Menu}. + */ menuPaperProps?: Partial; }