+ sidebar

This commit is contained in:
Manav Rathi
2025-07-07 10:57:59 +05:30
parent ec5b5ca80d
commit a5f3085e01
3 changed files with 96 additions and 97 deletions

View File

@@ -24,7 +24,11 @@ interface DownloadStatusNotificationsProps {
onRemoveSaveGroup: (saveGroup: SaveGroup) => void;
/**
* Called when the collection summary with the given {@link collectionID}
* and "hidden" {@link attribute} should be shown.
* should be shown. If {@link isHiddenCollectionSummary} is set, then any
* reauthentication as appropriate before switching to the hidden section of
* the app is performed first.
*
* and hidden attribute should be shown.
*
* This is only relevant in the context of the photos app, and can be
* omitted by the public albums app. See the documentation of

View File

@@ -140,18 +140,23 @@ type SidebarProps = ModalVisibilityProps & {
*/
onShowPlanSelector: () => void;
/**
* Called when the collection summary with the given
* {@link collectionSummaryID} should be shown.
*/
onShowCollectionSummary: (collectionSummaryID: number) => void;
/**
* Called when the hidden section should be shown.
* Called when the collection summary with the given {@link collectionID}
* should be shown.
*
* This triggers the display of the dialog to authenticate the user, exactly
* as if {@link onAuthenticateUser} were called. Then, on successful
* authentication, the gallery will switch to the hidden section.
* @param collectionSummaryID The ID of the {@link CollectionSummary} to
* switch to.
*
* @param isHiddenCollectionSummary If `true`, then any reauthentication as
* appropriate before switching to the hidden section of the app is
* performed first before showing the collection summary.
*
* @return A promise that fullfills after any needed reauthentication has
* been peformed (The view transition might still be in progress).
*/
onShowHiddenSection: () => Promise<void>;
onShowCollectionSummary: (
collectionSummaryID: number,
isHiddenCollectionSummary?: boolean,
) => Promise<void>;
/**
* Called when the export dialog should be shown.
*/
@@ -175,7 +180,6 @@ export const Sidebar: React.FC<SidebarProps> = ({
uncategorizedCollectionSummaryID,
onShowPlanSelector,
onShowCollectionSummary,
onShowHiddenSection,
onShowExport,
onAuthenticateUser,
}) => (
@@ -189,7 +193,6 @@ export const Sidebar: React.FC<SidebarProps> = ({
normalCollectionSummaries,
uncategorizedCollectionSummaryID,
onShowCollectionSummary,
onShowHiddenSection,
}}
/>
<UtilitySection
@@ -461,7 +464,6 @@ type ShortcutSectionProps = SectionProps &
| "normalCollectionSummaries"
| "uncategorizedCollectionSummaryID"
| "onShowCollectionSummary"
| "onShowHiddenSection"
>;
const ShortcutSection: React.FC<ShortcutSectionProps> = ({
@@ -469,25 +471,26 @@ const ShortcutSection: React.FC<ShortcutSectionProps> = ({
normalCollectionSummaries,
uncategorizedCollectionSummaryID,
onShowCollectionSummary,
onShowHiddenSection,
}) => {
const openUncategorizedSection = () => {
onShowCollectionSummary(uncategorizedCollectionSummaryID);
onCloseSidebar();
};
const handleOpenUncategorizedSection = () =>
void onShowCollectionSummary(uncategorizedCollectionSummaryID).then(
onCloseSidebar,
);
const openTrashSection = () => {
onShowCollectionSummary(PseudoCollectionID.trash);
onCloseSidebar();
};
const handleOpenTrashSection = () =>
void onShowCollectionSummary(PseudoCollectionID.trash).then(
onCloseSidebar,
);
const openArchiveSection = () => {
onShowCollectionSummary(PseudoCollectionID.archiveItems);
onCloseSidebar();
};
const handleOpenArchiveSection = () =>
void onShowCollectionSummary(PseudoCollectionID.archiveItems).then(
onCloseSidebar,
);
const openHiddenSection = () =>
void onShowHiddenSection().then(onCloseSidebar);
const handleOpenHiddenSection = () =>
void onShowCollectionSummary(PseudoCollectionID.hiddenItems, true).then(
onCloseSidebar,
);
const summaryCaption = (summaryID: number) =>
normalCollectionSummaries.get(summaryID)?.fileCount.toString();
@@ -498,13 +501,13 @@ const ShortcutSection: React.FC<ShortcutSectionProps> = ({
startIcon={<CategoryIcon />}
label={t("section_uncategorized")}
caption={summaryCaption(uncategorizedCollectionSummaryID)}
onClick={openUncategorizedSection}
onClick={handleOpenUncategorizedSection}
/>
<RowButton
startIcon={<ArchiveOutlinedIcon />}
label={t("section_archive")}
caption={summaryCaption(PseudoCollectionID.archiveItems)}
onClick={openArchiveSection}
onClick={handleOpenArchiveSection}
/>
<RowButton
startIcon={<VisibilityOffIcon />}
@@ -517,13 +520,13 @@ const ShortcutSection: React.FC<ShortcutSectionProps> = ({
}}
/>
}
onClick={openHiddenSection}
onClick={handleOpenHiddenSection}
/>
<RowButton
startIcon={<DeleteOutlineIcon />}
label={t("section_trash")}
caption={summaryCaption(PseudoCollectionID.trash)}
onClick={openTrashSection}
onClick={handleOpenTrashSection}
/>
</>
);

View File

@@ -89,7 +89,6 @@ import {
import {
haveOnlySystemCollections,
PseudoCollectionID,
type CollectionSummary,
} from "ente-new/photos/services/collection-summary";
import exportService from "ente-new/photos/services/export";
import { updateFilesVisibility } from "ente-new/photos/services/file";
@@ -790,80 +789,74 @@ const Page: React.FC = () => {
setUploadTypeSelectorIntent(intent ?? "upload");
};
const handleShowCollectionSummaryWithID = (
collectionSummaryID: number | undefined,
) => {
// Trigger a pull of the latest data from remote when opening the trash.
//
// This is needed for a specific scenario:
//
// 1. User deletes a collection, selecting the option to delete files.
// 2. Museum acks, and then client does a trash pull.
//
// This trash pull will not contain the files that belonged to the
// collection that got deleted because the collection deletion is a
// asynchronous operation.
//
// So the user might not see the entry for the just deleted file if they
// were to go to the trash meanwhile (until the next pull happens). To
// avoid this, we trigger a trash pull whenever it is opened.
if (collectionSummaryID == PseudoCollectionID.trash) {
void remoteFilesPull();
}
const handleShowCollectionSummaryWithID = useCallback(
(collectionSummaryID: number | undefined) => {
// Trigger a pull of the latest data from remote when opening the trash.
//
// This is needed for a specific scenario:
//
// 1. User deletes a collection, selecting the option to delete files.
// 2. Museum acks, and then client does a trash pull.
//
// This trash pull will not contain the files that belonged to the
// collection that got deleted because the collection deletion is a
// asynchronous operation.
//
// So the user might not see the entry for the just deleted file if they
// were to go to the trash meanwhile (until the next pull happens). To
// avoid this, we trigger a trash pull whenever it is opened.
if (collectionSummaryID == PseudoCollectionID.trash) {
void remoteFilesPull();
}
dispatch({ type: "showCollectionSummary", collectionSummaryID });
};
dispatch({ type: "showCollectionSummary", collectionSummaryID });
},
[],
);
/**
* Switch to gallery view to show the {@link CollectionSummary}.
* Switch to gallery view to show a collection or pseudo-collection.
*
* @param cs The {@link CollectionSummary} to show.
* If a {@link CollectionSummary} is not provided, show the "All" section.
* @param collectionSummaryID The ID of the {@link CollectionSummary} to
* show. If not provided, show the "All" section.
*
* If the given {@link CollectionSummary} is hidden, first perform any
* reauthentication as would be needed for showing the hidden section in the
* app, and then shows the {@link CollectionSummary}.
* @param isHidden If `true`, then any reauthentication as appropriate
* before switching to the hidden section of the app is performed first
* before before switching to the relevant collection or pseudo-collection.
*/
// TODO(RE):
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const handleShowCollectionSummary = (cs: CollectionSummary | undefined) => {
if (cs?.attributes.has("hidden")) {
void handleShowHiddenSection().then(() => {
handleShowCollectionSummaryWithID(cs.id);
});
} else {
handleShowCollectionSummaryWithID(cs.id);
}
};
/**
* A variant / reimplementation of {@link handleShowCollectionSummary} for
* use by the {@link DownloadStatusNotifications} component (which does not
* know about the {@link CollectionSummary} TypeScript type).
*/
const handleDownloadStatusNotificationsShowCollectionSummary = (
collectionSummaryID: number | undefined,
isHiddenCollectionSummary: boolean | undefined,
) => {
if (isHiddenCollectionSummary) {
void handleShowHiddenSection().then(() => {
handleShowCollectionSummaryWithID(collectionSummaryID);
});
} else {
const showCollectionSummary = useCallback(
async (
collectionSummaryID: number | undefined,
isHiddenCollectionSummary: boolean | undefined,
) => {
if (isHiddenCollectionSummary) {
await authenticateUser();
}
handleShowCollectionSummaryWithID(collectionSummaryID);
}
};
},
[authenticateUser, handleShowCollectionSummaryWithID],
);
const handleSidebarShowCollectionSummary = showCollectionSummary;
const handleDownloadStatusNotificationsShowCollectionSummary = useCallback(
(
collectionSummaryID: number | undefined,
isHiddenCollectionSummary: boolean | undefined,
) => {
void showCollectionSummary(
collectionSummaryID,
isHiddenCollectionSummary,
);
},
[showCollectionSummary],
);
const handleChangeBarMode = (mode: GalleryBarMode) =>
mode == "people"
? dispatch({ type: "showPeople" })
: dispatch({ type: "showAlbums" });
const handleShowHiddenSection = useCallback(
() => authenticateUser().then(() => dispatch({ type: "showHidden" })),
[],
);
const handleFileViewerToggleFavorite = useCallback(
async (file: EnteFile) => {
const fileID = file.id;
@@ -1114,8 +1107,7 @@ const Page: React.FC = () => {
state.uncategorizedCollectionSummaryID
}
onShowPlanSelector={showPlanSelector}
onShowCollectionSummary={handleShowCollectionSummaryWithID}
onShowHiddenSection={handleShowHiddenSection}
onShowCollectionSummary={handleSidebarShowCollectionSummary}
onShowExport={showExport}
onAuthenticateUser={authenticateUser}
/>