From 5ee16e992b83b08fc00ca684c5637ea97c1688d5 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Fri, 27 Jun 2025 15:12:27 +0530 Subject: [PATCH 01/13] Conv --- .../Collections/CollectionHeader.tsx | 5 ++--- .../gallery/services/magic-metadata.ts | 22 ------------------- .../new/photos/components/gallery/reducer.ts | 2 +- .../new/photos/services/collection.ts | 18 ++++++++++++++- 4 files changed, 20 insertions(+), 27 deletions(-) delete mode 100644 web/packages/gallery/services/magic-metadata.ts diff --git a/web/apps/photos/src/components/Collections/CollectionHeader.tsx b/web/apps/photos/src/components/Collections/CollectionHeader.tsx index 6e768917ae..edf74db137 100644 --- a/web/apps/photos/src/components/Collections/CollectionHeader.tsx +++ b/web/apps/photos/src/components/Collections/CollectionHeader.tsx @@ -25,7 +25,6 @@ import { import { SingleInputDialog } from "ente-base/components/SingleInputDialog"; import { useModalVisibility } from "ente-base/components/utils/modal"; import { useBaseContext } from "ente-base/context"; -import { isArchivedCollection } from "ente-gallery/services/magic-metadata"; import { CollectionOrder, type Collection } from "ente-media/collection"; import { ItemVisibility } from "ente-media/file-metadata"; import type { RemotePullOpts } from "ente-new/photos/components/gallery"; @@ -368,7 +367,7 @@ const CollectionHeaderOptions: React.FC = ({ case "incomingShareViewer": case "incomingShareCollaborator": menuOptions = [ - isArchivedCollection(activeCollection) ? ( + collectionSummary.attributes.has("archived") ? ( = ({ ), ...(!isHiddenCollection(activeCollection) ? [ - isArchivedCollection(activeCollection) ? ( + collectionSummary.attributes.has("archived") ? ( { - if (!item) { - return false; - } - - if (item.magicMetadata && item.magicMetadata.data) { - return item.magicMetadata.data.visibility === ItemVisibility.archived; - } - - if (item.sharedMagicMetadata && item.sharedMagicMetadata.data) { - return ( - item.sharedMagicMetadata.data.visibility === ItemVisibility.archived - ); - } - return false; -}; diff --git a/web/packages/new/photos/components/gallery/reducer.ts b/web/packages/new/photos/components/gallery/reducer.ts index 024097da2c..6369b2ed0d 100644 --- a/web/packages/new/photos/components/gallery/reducer.ts +++ b/web/packages/new/photos/components/gallery/reducer.ts @@ -1,5 +1,4 @@ import type { User } from "ente-accounts/services/user"; -import { isArchivedCollection } from "ente-gallery/services/magic-metadata"; import { groupFilesByCollectionID, sortFiles, @@ -18,6 +17,7 @@ import { import type { MagicMetadata } from "ente-media/magic-metadata"; import { createCollectionNameByID, + isArchivedCollection, isHiddenCollection, } from "ente-new/photos/services/collection"; import { sortTrashItems, type TrashItem } from "ente-new/photos/services/trash"; diff --git a/web/packages/new/photos/services/collection.ts b/web/packages/new/photos/services/collection.ts index 307a312f2c..25bb9bfa31 100644 --- a/web/packages/new/photos/services/collection.ts +++ b/web/packages/new/photos/services/collection.ts @@ -696,7 +696,7 @@ export const deleteFromTrash = async (fileIDs: number[]) => * * The move operation is not supported across ownership boundaries. The remove * operation is only supported across ownership boundaries, but the user should - * have owner ship of either the file or collection (not both). + * have ownership of either the file or collection (not both). * * In more detail, the above three scenarios can be described this way. * @@ -1184,9 +1184,25 @@ export const findDefaultHiddenCollectionIDs = (collections: Collection[]) => .map((collection) => collection.id), ); +/** + * Return `true` if the given collection is hidden. + * + * Hidden collections are those that have their visibility set to hidden in the + * collection's owner's private magic metadata. + */ export const isHiddenCollection = (collection: Collection) => collection.magicMetadata?.data.visibility == ItemVisibility.hidden; +/** + * Return `true` if the given collection is archived. + * + * Archived collections are those that have their visibility set to hidden in the + * collection's private magic metadata or per-sharee private metadata. + */ +export const isArchivedCollection = (collection: Collection) => + collection.magicMetadata?.data.visibility == ItemVisibility.archived || + collection.sharedMagicMetadata?.data.visibility == ItemVisibility.archived; + /** * Hide the provided {@link files} by moving them to the default hidden * collection. From 49e2ae3120da8ecc03b8ec7e8c5252ad836995ce Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Fri, 27 Jun 2025 15:20:40 +0530 Subject: [PATCH 02/13] As attribute --- .../Collections/CollectionHeader.tsx | 22 ++++++++++--------- .../new/photos/components/gallery/BarImpl.tsx | 4 ++-- .../new/photos/components/gallery/reducer.ts | 5 ++--- .../new/photos/services/collection-summary.ts | 15 ++++++++++++- 4 files changed, 30 insertions(+), 16 deletions(-) diff --git a/web/apps/photos/src/components/Collections/CollectionHeader.tsx b/web/apps/photos/src/components/Collections/CollectionHeader.tsx index edf74db137..1efa848da6 100644 --- a/web/apps/photos/src/components/Collections/CollectionHeader.tsx +++ b/web/apps/photos/src/components/Collections/CollectionHeader.tsx @@ -83,14 +83,13 @@ export const CollectionHeader: React.FC = (props) => { return <>; } - const { name, type, fileCount } = collectionSummary; + const { name, type, attributes, fileCount } = collectionSummary; const EndIcon = ({ type }: { type: CollectionSummaryType }) => { + if (attributes.has("archived")) return ; switch (type) { case "favorites": return ; - case "archived": - return ; case "incomingShareViewer": case "incomingShareCollaborator": return ; @@ -500,6 +499,7 @@ const CollectionHeaderOptions: React.FC = ({ return ( boolean; onEmptyTrashClick: () => void; onDownloadClick: () => void; @@ -549,6 +550,7 @@ const QuickOptions: React.FC = ({ onEmptyTrashClick, onDownloadClick, onShareClick, + collectionSummary, collectionSummaryType: type, isDownloadInProgress, }) => ( @@ -556,7 +558,7 @@ const QuickOptions: React.FC = ({ {showEmptyTrashQuickOption(type) && ( )} - {showDownloadQuickOption(type) && + {showDownloadQuickOption(collectionSummary) && (isDownloadInProgress() ? ( ) : ( @@ -565,7 +567,7 @@ const QuickOptions: React.FC = ({ collectionSummaryType={type} /> ))} - {showShareQuickOption(type) && ( + {showShareQuickOption(collectionSummary) && ( = ({ onClick }) => ( ); -const showDownloadQuickOption = (type: CollectionSummaryType) => +const showDownloadQuickOption = ({ type, attributes }: CollectionSummary) => type == "album" || type == "folder" || type == "favorites" || @@ -595,7 +597,7 @@ const showDownloadQuickOption = (type: CollectionSummaryType) => type == "incomingShareCollaborator" || type == "outgoingShare" || type == "sharedOnlyViaLink" || - type == "archived" || + attributes.has("archived") || type == "pinned"; type DownloadQuickOptionProps = OptionProps & { @@ -623,13 +625,13 @@ const DownloadQuickOption: React.FC = ({ ); -const showShareQuickOption = (type: CollectionSummaryType) => +const showShareQuickOption = ({ type, attributes }: CollectionSummary) => type == "album" || type == "folder" || type == "favorites" || type == "outgoingShare" || type == "sharedOnlyViaLink" || - type == "archived" || + attributes.has("archived") || type == "incomingShareViewer" || type == "incomingShareCollaborator" || type == "pinned"; diff --git a/web/packages/new/photos/components/gallery/BarImpl.tsx b/web/packages/new/photos/components/gallery/BarImpl.tsx index 49e801d7ca..5d59a46326 100644 --- a/web/packages/new/photos/components/gallery/BarImpl.tsx +++ b/web/packages/new/photos/components/gallery/BarImpl.tsx @@ -23,7 +23,7 @@ import { import { FocusVisibleUnstyledButton } from "ente-new/photos/components/UnstyledButton"; import type { CollectionSummary, - CollectionSummaryType, + CollectionSummaryAttribute, CollectionsSortBy, } from "ente-new/photos/services/collection-summary"; import type { Person } from "ente-new/photos/services/ml/people"; @@ -528,7 +528,7 @@ const CardText: React.FC = ({ children }) => ( ); interface CollectionBarCardIconProps { - attributes: Set; + attributes: Set; } const CollectionBarCardIcon: React.FC = ({ diff --git a/web/packages/new/photos/components/gallery/reducer.ts b/web/packages/new/photos/components/gallery/reducer.ts index 6369b2ed0d..d3469eb0c6 100644 --- a/web/packages/new/photos/components/gallery/reducer.ts +++ b/web/packages/new/photos/components/gallery/reducer.ts @@ -33,6 +33,7 @@ import { CollectionSummarySortPriority, PseudoCollectionID, type CollectionSummary, + type CollectionSummaryAttribute, type CollectionSummaryType, } from "../../services/collection-summary"; import type { PeopleState, Person } from "../../services/ml/people"; @@ -1414,8 +1415,6 @@ const createCollectionSummaries = ( type = "outgoingShare"; } else if (isSharedOnlyViaLink(collection)) { type = "sharedOnlyViaLink"; - } else if (isArchivedCollection(collection)) { - type = "archived"; } else if (isDefaultHiddenCollection(collection)) { type = "defaultHidden"; } else if ( @@ -1429,7 +1428,7 @@ const createCollectionSummaries = ( // This block of code duplicates the above. Such duplication is needed // until type is completely replaced by attributes. - const attributes = new Set(); + const attributes = new Set(); if (collection.owner.id != user.id) { attributes.add( collection.sharees.find((s) => s.id == user.id)?.role == diff --git a/web/packages/new/photos/services/collection-summary.ts b/web/packages/new/photos/services/collection-summary.ts index d13e16e299..a4254abdb9 100644 --- a/web/packages/new/photos/services/collection-summary.ts +++ b/web/packages/new/photos/services/collection-summary.ts @@ -2,6 +2,19 @@ import type { CollectionType } from "ente-media/collection"; import type { EnteFile } from "ente-media/file"; export type CollectionSummaryType = + | CollectionType + | "all" + | "archive" + | "trash" + | "hiddenItems" + | "defaultHidden" + | "outgoingShare" + | "incomingShareViewer" + | "incomingShareCollaborator" + | "sharedOnlyViaLink" + | "pinned"; + +export type CollectionSummaryAttribute = | CollectionType | "all" | "archive" @@ -117,7 +130,7 @@ export interface CollectionSummary { * ad-hoc "UI" attributes which make it easier and more efficient for the UI * elements to render the collection summary in the UI. */ - attributes: Set; + attributes: Set; /** * The name of the collection or pseudo-collection surfaced in the UI. */ From 55a21c123321fb12438a66daa02bc10b1642b127 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Fri, 27 Jun 2025 15:25:43 +0530 Subject: [PATCH 03/13] only as attr --- .../src/components/Collections/CollectionHeader.tsx | 12 ++++-------- .../new/photos/components/gallery/reducer.ts | 6 +----- .../new/photos/services/collection-summary.ts | 3 +-- 3 files changed, 6 insertions(+), 15 deletions(-) diff --git a/web/apps/photos/src/components/Collections/CollectionHeader.tsx b/web/apps/photos/src/components/Collections/CollectionHeader.tsx index 1efa848da6..a82b780fb1 100644 --- a/web/apps/photos/src/components/Collections/CollectionHeader.tsx +++ b/web/apps/photos/src/components/Collections/CollectionHeader.tsx @@ -587,7 +587,7 @@ const EmptyTrashQuickOption: React.FC = ({ onClick }) => ( ); -const showDownloadQuickOption = ({ type, attributes }: CollectionSummary) => +const showDownloadQuickOption = ({ type }: CollectionSummary) => type == "album" || type == "folder" || type == "favorites" || @@ -596,9 +596,7 @@ const showDownloadQuickOption = ({ type, attributes }: CollectionSummary) => type == "incomingShareViewer" || type == "incomingShareCollaborator" || type == "outgoingShare" || - type == "sharedOnlyViaLink" || - attributes.has("archived") || - type == "pinned"; + type == "sharedOnlyViaLink" type DownloadQuickOptionProps = OptionProps & { collectionSummaryType: CollectionSummaryType; @@ -625,16 +623,14 @@ const DownloadQuickOption: React.FC = ({ ); -const showShareQuickOption = ({ type, attributes }: CollectionSummary) => +const showShareQuickOption = ({ type }: CollectionSummary) => type == "album" || type == "folder" || type == "favorites" || type == "outgoingShare" || type == "sharedOnlyViaLink" || - attributes.has("archived") || type == "incomingShareViewer" || - type == "incomingShareCollaborator" || - type == "pinned"; + type == "incomingShareCollaborator" interface ShareQuickOptionProps { onClick: () => void; diff --git a/web/packages/new/photos/components/gallery/reducer.ts b/web/packages/new/photos/components/gallery/reducer.ts index d3469eb0c6..a274d53c2e 100644 --- a/web/packages/new/photos/components/gallery/reducer.ts +++ b/web/packages/new/photos/components/gallery/reducer.ts @@ -1417,11 +1417,6 @@ const createCollectionSummaries = ( type = "sharedOnlyViaLink"; } else if (isDefaultHiddenCollection(collection)) { type = "defaultHidden"; - } else if ( - collection.magicMetadata?.data.order == CollectionOrder.pinned - ) { - type = "pinned"; - sortPriority = CollectionSummarySortPriority.pinned; } else { type = collectionType; } @@ -1451,6 +1446,7 @@ const createCollectionSummaries = ( } if (collection.magicMetadata?.data.order == CollectionOrder.pinned) { attributes.add("pinned"); + sortPriority = CollectionSummarySortPriority.pinned; } switch (collectionType) { case "favorites": diff --git a/web/packages/new/photos/services/collection-summary.ts b/web/packages/new/photos/services/collection-summary.ts index a4254abdb9..aba70552f7 100644 --- a/web/packages/new/photos/services/collection-summary.ts +++ b/web/packages/new/photos/services/collection-summary.ts @@ -11,8 +11,7 @@ export type CollectionSummaryType = | "outgoingShare" | "incomingShareViewer" | "incomingShareCollaborator" - | "sharedOnlyViaLink" - | "pinned"; + | "sharedOnlyViaLink"; export type CollectionSummaryAttribute = | CollectionType From 5d594b49523f86baa1d98c2b46ab7ef9a00365e3 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Fri, 27 Jun 2025 15:35:29 +0530 Subject: [PATCH 04/13] as attr --- .../Collections/CollectionHeader.tsx | 34 +++++++------------ .../new/photos/components/gallery/BarImpl.tsx | 10 +++--- .../new/photos/components/gallery/reducer.ts | 3 ++ .../new/photos/services/collection-summary.ts | 1 + 4 files changed, 23 insertions(+), 25 deletions(-) diff --git a/web/apps/photos/src/components/Collections/CollectionHeader.tsx b/web/apps/photos/src/components/Collections/CollectionHeader.tsx index a82b780fb1..19231e5b28 100644 --- a/web/apps/photos/src/components/Collections/CollectionHeader.tsx +++ b/web/apps/photos/src/components/Collections/CollectionHeader.tsx @@ -85,18 +85,13 @@ export const CollectionHeader: React.FC = (props) => { const { name, type, attributes, fileCount } = collectionSummary; - const EndIcon = ({ type }: { type: CollectionSummaryType }) => { + const EndIcon = () => { if (attributes.has("archived")) return ; + if (attributes.has("sharedOnlyViaLink")) return ; + if (attributes.has("shared")) return ; switch (type) { case "favorites": return ; - case "incomingShareViewer": - case "incomingShareCollaborator": - return ; - case "outgoingShare": - return ; - case "sharedOnlyViaLink": - return ; default: return <>; } @@ -108,7 +103,7 @@ export const CollectionHeader: React.FC = (props) => { } + endIcon={} /> {shouldShowOptions(type) && ( @@ -570,6 +565,7 @@ const QuickOptions: React.FC = ({ {showShareQuickOption(collectionSummary) && ( )} @@ -587,16 +583,13 @@ const EmptyTrashQuickOption: React.FC = ({ onClick }) => ( ); -const showDownloadQuickOption = ({ type }: CollectionSummary) => +const showDownloadQuickOption = ({ type, attributes }: CollectionSummary) => type == "album" || type == "folder" || type == "favorites" || type == "uncategorized" || type == "hiddenItems" || - type == "incomingShareViewer" || - type == "incomingShareCollaborator" || - type == "outgoingShare" || - type == "sharedOnlyViaLink" + attributes.has("shared"); type DownloadQuickOptionProps = OptionProps & { collectionSummaryType: CollectionSummaryType; @@ -623,22 +616,21 @@ const DownloadQuickOption: React.FC = ({ ); -const showShareQuickOption = ({ type }: CollectionSummary) => +const showShareQuickOption = ({ type, attributes }: CollectionSummary) => type == "album" || type == "folder" || type == "favorites" || - type == "outgoingShare" || - type == "sharedOnlyViaLink" || - type == "incomingShareViewer" || - type == "incomingShareCollaborator" + attributes.has("shared"); interface ShareQuickOptionProps { onClick: () => void; + collectionSummary: CollectionSummary; collectionSummaryType: CollectionSummaryType; } const ShareQuickOption: React.FC = ({ onClick, + collectionSummary, collectionSummaryType, }) => ( = ({ collectionSummaryType == "incomingShareViewer" || collectionSummaryType == "incomingShareCollaborator" ? t("sharing_details") - : collectionSummaryType == "outgoingShare" || - collectionSummaryType == "sharedOnlyViaLink" + : collectionSummary.attributes.has("outgoingShare") || + collectionSummary.attributes.has("sharedOnlyViaLink") ? t("modify_sharing") : collectionSummaryType == "favorites" ? t("share_favorites") diff --git a/web/packages/new/photos/components/gallery/BarImpl.tsx b/web/packages/new/photos/components/gallery/BarImpl.tsx index 5d59a46326..7f9c5c6fda 100644 --- a/web/packages/new/photos/components/gallery/BarImpl.tsx +++ b/web/packages/new/photos/components/gallery/BarImpl.tsx @@ -543,10 +543,12 @@ const CollectionBarCardIcon: React.FC = ({ // Need && to override the 20px set in the container. )} - {(attributes.has("outgoingShare") || - attributes.has("incomingShareViewer") || - attributes.has("incomingShareCollaborator")) && } - {attributes.has("sharedOnlyViaLink") && } + {attributes.has("shared") && + (attributes.has("sharedOnlyViaLink") ? ( + + ) : ( + + ))} {attributes.has("archived") && } ); diff --git a/web/packages/new/photos/components/gallery/reducer.ts b/web/packages/new/photos/components/gallery/reducer.ts index a274d53c2e..bd9e5f5821 100644 --- a/web/packages/new/photos/components/gallery/reducer.ts +++ b/web/packages/new/photos/components/gallery/reducer.ts @@ -1425,6 +1425,7 @@ const createCollectionSummaries = ( // until type is completely replaced by attributes. const attributes = new Set(); if (collection.owner.id != user.id) { + attributes.add("shared"); attributes.add( collection.sharees.find((s) => s.id == user.id)?.role == "COLLABORATOR" @@ -1433,9 +1434,11 @@ const createCollectionSummaries = ( ); } if (isOutgoingShare(collection, user)) { + attributes.add("shared"); attributes.add("outgoingShare"); } if (isSharedOnlyViaLink(collection)) { + attributes.add("shared"); attributes.add("sharedOnlyViaLink"); } if (isArchivedCollection(collection)) { diff --git a/web/packages/new/photos/services/collection-summary.ts b/web/packages/new/photos/services/collection-summary.ts index aba70552f7..46ea092163 100644 --- a/web/packages/new/photos/services/collection-summary.ts +++ b/web/packages/new/photos/services/collection-summary.ts @@ -20,6 +20,7 @@ export type CollectionSummaryAttribute = | "trash" | "hiddenItems" | "defaultHidden" + | "shared" | "outgoingShare" | "incomingShareViewer" | "incomingShareCollaborator" From a888d40722e441b4eb9438d303973940d61307d7 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Fri, 27 Jun 2025 16:05:23 +0530 Subject: [PATCH 05/13] Retain the active cs --- .../new/photos/components/gallery/reducer.ts | 132 ++++++++++++------ 1 file changed, 93 insertions(+), 39 deletions(-) diff --git a/web/packages/new/photos/components/gallery/reducer.ts b/web/packages/new/photos/components/gallery/reducer.ts index bd9e5f5821..a4a41f8d49 100644 --- a/web/packages/new/photos/components/gallery/reducer.ts +++ b/web/packages/new/photos/components/gallery/reducer.ts @@ -69,6 +69,10 @@ export type GalleryView = * or {@link hiddenCollections}. */ activeCollection: Collection | undefined; + /** + * The currently active collection summary. + */ + activeCollectionSummary: CollectionSummary; } | { /** @@ -554,10 +558,28 @@ const galleryReducer: React.Reducer = ( collectionFiles, ); + const normalCollectionSummaries = deriveNormalCollectionSummaries( + normalCollections, + action.user, + trashItems, + collectionFiles, + hiddenFileIDs, + archivedFileIDs, + ); + + const hiddenCollectionSummaries = deriveHiddenCollectionSummaries( + hiddenCollections, + action.user, + collectionFiles, + ); + const view = { type: "albums" as const, activeCollectionSummaryID: PseudoCollectionID.all, activeCollection: undefined, + activeCollectionSummary: normalCollectionSummaries.get( + PseudoCollectionID.all, + )!, }; return stateByUpdatingFilteredFiles({ @@ -593,19 +615,8 @@ const galleryReducer: React.Reducer = ( familyData, collections, ), - normalCollectionSummaries: deriveNormalCollectionSummaries( - normalCollections, - action.user, - trashItems, - collectionFiles, - hiddenFileIDs, - archivedFileIDs, - ), - hiddenCollectionSummaries: deriveHiddenCollectionSummaries( - hiddenCollections, - action.user, - collectionFiles, - ), + normalCollectionSummaries, + hiddenCollectionSummaries, uncategorizedCollectionSummaryID: deriveUncategorizedCollectionSummaryID(normalCollections), view, @@ -637,6 +648,7 @@ const galleryReducer: React.Reducer = ( hiddenFileIDs, archivedFileIDs, ); + const hiddenCollectionSummaries = deriveHiddenCollectionSummaries( hiddenCollections, state.user!, @@ -929,6 +941,10 @@ const galleryReducer: React.Reducer = ( type: "albums", activeCollectionSummaryID: PseudoCollectionID.all, activeCollection: undefined, + activeCollectionSummary: + state.normalCollectionSummaries.get( + PseudoCollectionID.all, + )!, }, isInSearchMode: false, }); @@ -946,6 +962,10 @@ const galleryReducer: React.Reducer = ( type: "hidden-albums", activeCollectionSummaryID: PseudoCollectionID.hiddenItems, activeCollection: undefined, + activeCollectionSummary: + state.hiddenCollectionSummaries.get( + PseudoCollectionID.hiddenItems, + )!, }, isInSearchMode: false, }); @@ -972,31 +992,53 @@ const galleryReducer: React.Reducer = ( }); } - case "showCollectionSummary": + case "showCollectionSummary": { + const selectedCollectionSummaryID = action.collectionSummaryID; + const activeCollection = state.collections.find( + ({ id }) => id == selectedCollectionSummaryID, + ); + + let view: GalleryState["view"]; + if ( + selectedCollectionSummaryID !== undefined && + state.hiddenCollectionSummaries.has(selectedCollectionSummaryID) + ) { + const activeCollectionSummaryID = selectedCollectionSummaryID; + view = { + type: "hidden-albums", + activeCollectionSummaryID, + activeCollection, + activeCollectionSummary: + state.hiddenCollectionSummaries.get( + activeCollectionSummaryID, + )!, + }; + } else { + const activeCollectionSummaryID = + selectedCollectionSummaryID ?? PseudoCollectionID.all; + view = { + type: "albums", + activeCollectionSummaryID, + activeCollection, + activeCollectionSummary: + state.normalCollectionSummaries.get( + activeCollectionSummaryID, + )!, + }; + } + return stateByUpdatingFilteredFiles({ ...state, - selectedCollectionSummaryID: action.collectionSummaryID, + selectedCollectionSummaryID, extraVisiblePerson: undefined, searchResults: undefined, searchSuggestion: undefined, isRecomputingSearchResults: false, pendingSearchSuggestions: [], - view: { - type: - action.collectionSummaryID !== undefined && - state.hiddenCollectionSummaries.has( - action.collectionSummaryID, - ) - ? "hidden-albums" - : "albums", - activeCollectionSummaryID: - action.collectionSummaryID ?? PseudoCollectionID.all, - activeCollection: state.collections.find( - ({ id }) => id == action.collectionSummaryID, - ), - }, + view, isInSearchMode: false, }); + } case "showPeople": { const { view, extraVisiblePerson } = derivePeopleView( @@ -1556,11 +1598,17 @@ const deriveAlbumsViewAndSelectedID = ( selectedCollectionSummaryID: GalleryState["selectedCollectionSummaryID"], ) => { // Make sure that the last selected ID is still valid by searching for it. - const activeCollectionSummaryID = selectedCollectionSummaryID - ? collectionSummaries.get(selectedCollectionSummaryID)?.id + const selectedCollectionSummary = selectedCollectionSummaryID + ? collectionSummaries.get(selectedCollectionSummaryID) : undefined; + + const activeCollectionSummaryID = + selectedCollectionSummary?.id ?? PseudoCollectionID.all; + const activeCollectionSummary = collectionSummaries.get( + activeCollectionSummaryID, + )!; const activeCollection = - activeCollectionSummaryID && + selectedCollectionSummary && !hiddenCollectionIDs.has(activeCollectionSummaryID) ? collections.find(({ id }) => id == activeCollectionSummaryID) : undefined; @@ -1568,9 +1616,9 @@ const deriveAlbumsViewAndSelectedID = ( selectedCollectionSummaryID: activeCollectionSummaryID, view: { type: "albums" as const, - activeCollectionSummaryID: - activeCollectionSummaryID ?? PseudoCollectionID.all, + activeCollectionSummaryID, activeCollection, + activeCollectionSummary, }, }; }; @@ -1586,11 +1634,17 @@ const deriveHiddenAlbumsViewAndSelectedID = ( selectedCollectionSummaryID: GalleryState["selectedCollectionSummaryID"], ) => { // Make sure that the last selected ID is still valid by searching for it. - const activeCollectionSummaryID = selectedCollectionSummaryID - ? hiddenCollectionSummaries.get(selectedCollectionSummaryID)?.id + const selectedCollectionSummary = selectedCollectionSummaryID + ? hiddenCollectionSummaries.get(selectedCollectionSummaryID) : undefined; + + const activeCollectionSummaryID = + selectedCollectionSummary?.id ?? PseudoCollectionID.hiddenItems; + const activeCollectionSummary = hiddenCollectionSummaries.get( + activeCollectionSummaryID, + )!; const activeCollection = - activeCollectionSummaryID && + selectedCollectionSummary && hiddenCollectionIDs.has(activeCollectionSummaryID) ? collections.find(({ id }) => id == activeCollectionSummaryID) : undefined; @@ -1598,9 +1652,9 @@ const deriveHiddenAlbumsViewAndSelectedID = ( selectedCollectionSummaryID: activeCollectionSummaryID, view: { type: "hidden-albums" as const, - activeCollectionSummaryID: - activeCollectionSummaryID ?? PseudoCollectionID.hiddenItems, + activeCollectionSummaryID, activeCollection, + activeCollectionSummary, }, }; }; From 9fd950f928383825f39d2e4c8bf81860d7c4f4bb Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Fri, 27 Jun 2025 16:14:22 +0530 Subject: [PATCH 06/13] Move up --- .../Collections/GalleryBarAndListHeader.tsx | 13 ++----------- web/apps/photos/src/pages/gallery.tsx | 18 +++++++++++++++--- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/web/apps/photos/src/components/Collections/GalleryBarAndListHeader.tsx b/web/apps/photos/src/components/Collections/GalleryBarAndListHeader.tsx index 2630464b25..c03b06f6ea 100644 --- a/web/apps/photos/src/components/Collections/GalleryBarAndListHeader.tsx +++ b/web/apps/photos/src/components/Collections/GalleryBarAndListHeader.tsx @@ -48,10 +48,9 @@ type GalleryBarAndListHeaderProps = Omit< * When `true`, the bar is be hidden altogether. */ shouldHide: boolean; - collectionSummaries: CollectionSummaries; + barCollectionSummaries: CollectionSummaries; activeCollection: Collection; setActiveCollectionID: (collectionID: number) => void; - hiddenCollectionSummaries: CollectionSummaries; setPhotoListHeader: (value: TimeStampListItem) => void; filesDownloadProgressAttributesList: FilesDownloadProgressAttributes[]; } & Pick< @@ -88,12 +87,11 @@ export const GalleryBarAndListHeader: React.FC< mode, onChangeMode, user, - collectionSummaries, + barCollectionSummaries: toShowCollectionSummaries, activeCollection, activeCollectionID, setActiveCollectionID, setBlockingLoad, - hiddenCollectionSummaries, people, activePerson, emailByUserID, @@ -114,13 +112,6 @@ export const GalleryBarAndListHeader: React.FC< const [collectionsSortBy, setCollectionsSortBy] = useCollectionsSortByLocalState("updation-time-desc"); - const toShowCollectionSummaries = useMemo( - () => - mode == "hidden-albums" - ? hiddenCollectionSummaries - : collectionSummaries, - [mode, hiddenCollectionSummaries, collectionSummaries], - ); const shouldBeHidden = useMemo( () => diff --git a/web/apps/photos/src/pages/gallery.tsx b/web/apps/photos/src/pages/gallery.tsx index af4a3c83bb..84e0ed2d1b 100644 --- a/web/apps/photos/src/pages/gallery.tsx +++ b/web/apps/photos/src/pages/gallery.tsx @@ -108,7 +108,7 @@ import { import { PromiseQueue } from "ente-utils/promise"; import { t } from "i18next"; import { useRouter, type NextRouter } from "next/router"; -import { useCallback, useEffect, useRef, useState } from "react"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { FileWithPath } from "react-dropzone"; import { Trans } from "react-i18next"; import { @@ -253,6 +253,19 @@ const Page: React.FC = () => { state.view?.type == "people" ? state.view.activePerson : undefined; const activePersonID = activePerson?.id; + // TODO: Move into reducer + const barCollectionSummaries = useMemo( + () => + barMode == "hidden-albums" + ? state.hiddenCollectionSummaries + : state.normalCollectionSummaries, + [ + barMode, + state.hiddenCollectionSummaries, + state.normalCollectionSummaries, + ], + ); + if (process.env.NEXT_PUBLIC_ENTE_TRACE) console.log("render", state); const router = useRouter(); @@ -967,8 +980,7 @@ const Page: React.FC = () => { }} mode={barMode} shouldHide={isInSearchMode} - collectionSummaries={normalCollectionSummaries} - hiddenCollectionSummaries={state.hiddenCollectionSummaries} + barCollectionSummaries={barCollectionSummaries} emailByUserID={state.emailByUserID} shareSuggestionEmails={state.shareSuggestionEmails} people={ From 61fb167cec2ae44e8b5ea006a0c9604631362010 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Fri, 27 Jun 2025 16:29:53 +0530 Subject: [PATCH 07/13] Use --- .../Collections/GalleryBarAndListHeader.tsx | 2 -- web/apps/photos/src/components/Sidebar.tsx | 16 +++++++-------- web/apps/photos/src/pages/gallery.tsx | 20 ++++++++++--------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/web/apps/photos/src/components/Collections/GalleryBarAndListHeader.tsx b/web/apps/photos/src/components/Collections/GalleryBarAndListHeader.tsx index c03b06f6ea..6b3d7ff905 100644 --- a/web/apps/photos/src/components/Collections/GalleryBarAndListHeader.tsx +++ b/web/apps/photos/src/components/Collections/GalleryBarAndListHeader.tsx @@ -38,7 +38,6 @@ import { type GalleryBarAndListHeaderProps = Omit< GalleryBarImplProps, | "collectionSummaries" - | "hiddenCollectionSummaries" | "onSelectCollectionID" | "collectionsSortBy" | "onChangeCollectionsSortBy" @@ -112,7 +111,6 @@ export const GalleryBarAndListHeader: React.FC< const [collectionsSortBy, setCollectionsSortBy] = useCollectionsSortByLocalState("updation-time-desc"); - const shouldBeHidden = useMemo( () => shouldHide || diff --git a/web/apps/photos/src/components/Sidebar.tsx b/web/apps/photos/src/components/Sidebar.tsx index c587b4143e..f28525f9da 100644 --- a/web/apps/photos/src/components/Sidebar.tsx +++ b/web/apps/photos/src/components/Sidebar.tsx @@ -124,12 +124,12 @@ import { SubscriptionCard } from "./SubscriptionCard"; type SidebarProps = ModalVisibilityProps & { /** - * The latest set of collections, sections and pseudo-collections. + * Information about non-hidden collections and pseudo-collections. * * These are used to obtain data about the archive, hidden and trash * "section" entries shown within the shortcut section of the sidebar. */ - collectionSummaries: CollectionSummaries; + normalCollectionSummaries: CollectionSummaries; /** * The ID of the collection summary that should be shown when the user * activates the "Uncategorized" section shortcut. @@ -171,7 +171,7 @@ type SidebarProps = ModalVisibilityProps & { export const Sidebar: React.FC = ({ open, onClose, - collectionSummaries, + normalCollectionSummaries, uncategorizedCollectionSummaryID, onShowPlanSelector, onShowCollectionSummary, @@ -186,7 +186,7 @@ export const Sidebar: React.FC = ({ = ({ type ShortcutSectionProps = SectionProps & Pick< SidebarProps, - | "collectionSummaries" + | "normalCollectionSummaries" | "uncategorizedCollectionSummaryID" | "onShowCollectionSummary" | "onShowHiddenSection" @@ -466,7 +466,7 @@ type ShortcutSectionProps = SectionProps & const ShortcutSection: React.FC = ({ onCloseSidebar, - collectionSummaries, + normalCollectionSummaries, uncategorizedCollectionSummaryID, onShowCollectionSummary, onShowHiddenSection, @@ -489,8 +489,8 @@ const ShortcutSection: React.FC = ({ const openHiddenSection = () => void onShowHiddenSection().then(onCloseSidebar); - const summaryCaption = (collectionSummaryID: number) => - collectionSummaries.get(collectionSummaryID)?.fileCount.toString(); + const summaryCaption = (summaryID: number) => + normalCollectionSummaries.get(summaryID)?.fileCount.toString(); return ( <> diff --git a/web/apps/photos/src/pages/gallery.tsx b/web/apps/photos/src/pages/gallery.tsx index 84e0ed2d1b..d98776cd9c 100644 --- a/web/apps/photos/src/pages/gallery.tsx +++ b/web/apps/photos/src/pages/gallery.tsx @@ -249,6 +249,10 @@ const Page: React.FC = () => { : state.view?.activeCollectionSummaryID; const activeCollection = state.view?.type == "people" ? undefined : state.view?.activeCollection; + const activeCollectionSummary = + state.view?.type == "people" + ? undefined + : state.view?.activeCollectionSummary; const activePerson = state.view?.type == "people" ? state.view.activePerson : undefined; const activePersonID = activePerson?.id; @@ -931,18 +935,16 @@ const Page: React.FC = () => { state.collections, )} isFavoriteCollection={ - normalCollectionSummaries.get(activeCollectionID) - ?.type == "favorites" + activeCollectionSummary?.type == "favorites" } isUncategorizedCollection={ - normalCollectionSummaries.get(activeCollectionID) - ?.type == "uncategorized" + activeCollectionSummary?.type == "uncategorized" } isIncomingSharedCollection={ - normalCollectionSummaries.get(activeCollectionID) - ?.type == "incomingShareCollaborator" || - normalCollectionSummaries.get(activeCollectionID) - ?.type == "incomingShareViewer" + activeCollectionSummary?.type == + "incomingShareCollaborator" || + activeCollectionSummary?.type == + "incomingShareViewer" } isInSearchMode={isInSearchMode} isInHiddenSection={barMode == "hidden-albums"} @@ -1022,7 +1024,7 @@ const Page: React.FC = () => { /> Date: Fri, 27 Jun 2025 16:37:19 +0530 Subject: [PATCH 08/13] attr --- .../pages/gallery/SelectedFileOptions.tsx | 21 ++++++++++++------- web/apps/photos/src/pages/gallery.tsx | 13 +----------- .../new/photos/components/gallery/reducer.ts | 1 + .../new/photos/services/collection-summary.ts | 1 + 4 files changed, 17 insertions(+), 19 deletions(-) diff --git a/web/apps/photos/src/components/pages/gallery/SelectedFileOptions.tsx b/web/apps/photos/src/components/pages/gallery/SelectedFileOptions.tsx index 6be0160d18..5a9ce14662 100644 --- a/web/apps/photos/src/components/pages/gallery/SelectedFileOptions.tsx +++ b/web/apps/photos/src/components/pages/gallery/SelectedFileOptions.tsx @@ -17,7 +17,10 @@ import { useBaseContext } from "ente-base/context"; import type { Collection } from "ente-media/collection"; import type { CollectionSelectorAttributes } from "ente-new/photos/components/CollectionSelector"; import type { GalleryBarMode } from "ente-new/photos/components/gallery/reducer"; -import { PseudoCollectionID } from "ente-new/photos/services/collection-summary"; +import { + PseudoCollectionID, + type CollectionSummary, +} from "ente-new/photos/services/collection-summary"; import { t } from "i18next"; import { type CollectionOp } from "utils/collection"; import { type FileOp } from "utils/file"; @@ -40,8 +43,6 @@ interface Props { clearSelection: () => void; barMode?: GalleryBarMode; activeCollectionID: number; - isFavoriteCollection: boolean; - isUncategorizedCollection: boolean; /** * TODO: Need to implement delete-equivalent from shared albums. * @@ -63,7 +64,7 @@ interface Props { * Also note that that user cannot delete files that are not owned by the * user, even if they are in an album owned by the user. */ - isIncomingSharedCollection: boolean; + activeCollectionSummary: CollectionSummary | undefined; isInSearchMode: boolean; selectedCollection: Collection; isInHiddenSection: boolean; @@ -80,9 +81,7 @@ const SelectedFileOptions = ({ clearSelection, barMode, activeCollectionID, - isFavoriteCollection, - isUncategorizedCollection, - isIncomingSharedCollection, + activeCollectionSummary, isInSearchMode, isInHiddenSection, }: Props) => { @@ -90,6 +89,14 @@ const SelectedFileOptions = ({ const peopleMode = barMode == "people"; + const isFavoriteCollection = activeCollectionSummary?.type == "favorites"; + + const isUncategorizedCollection = + activeCollectionSummary?.type == "uncategorized"; + + const isIncomingSharedCollection = + activeCollectionSummary?.attributes.has("sharedIncoming"); + const addToCollection = () => onOpenCollectionSelector({ action: "add", diff --git a/web/apps/photos/src/pages/gallery.tsx b/web/apps/photos/src/pages/gallery.tsx index d98776cd9c..4bfd515381 100644 --- a/web/apps/photos/src/pages/gallery.tsx +++ b/web/apps/photos/src/pages/gallery.tsx @@ -934,18 +934,7 @@ const Page: React.FC = () => { selected.collectionID, state.collections, )} - isFavoriteCollection={ - activeCollectionSummary?.type == "favorites" - } - isUncategorizedCollection={ - activeCollectionSummary?.type == "uncategorized" - } - isIncomingSharedCollection={ - activeCollectionSummary?.type == - "incomingShareCollaborator" || - activeCollectionSummary?.type == - "incomingShareViewer" - } + activeCollectionSummary={activeCollectionSummary} isInSearchMode={isInSearchMode} isInHiddenSection={barMode == "hidden-albums"} /> diff --git a/web/packages/new/photos/components/gallery/reducer.ts b/web/packages/new/photos/components/gallery/reducer.ts index a4a41f8d49..e0c905b092 100644 --- a/web/packages/new/photos/components/gallery/reducer.ts +++ b/web/packages/new/photos/components/gallery/reducer.ts @@ -1468,6 +1468,7 @@ const createCollectionSummaries = ( const attributes = new Set(); if (collection.owner.id != user.id) { attributes.add("shared"); + attributes.add("sharedIncoming"); attributes.add( collection.sharees.find((s) => s.id == user.id)?.role == "COLLABORATOR" diff --git a/web/packages/new/photos/services/collection-summary.ts b/web/packages/new/photos/services/collection-summary.ts index 46ea092163..38d7b89687 100644 --- a/web/packages/new/photos/services/collection-summary.ts +++ b/web/packages/new/photos/services/collection-summary.ts @@ -22,6 +22,7 @@ export type CollectionSummaryAttribute = | "defaultHidden" | "shared" | "outgoingShare" + | "sharedIncoming" | "incomingShareViewer" | "incomingShareCollaborator" | "sharedOnlyViaLink" From 72028ec9d28265f52ead6e9bd4579452c8489a49 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Fri, 27 Jun 2025 17:04:43 +0530 Subject: [PATCH 09/13] attr --- .../Collections/CollectionHeader.tsx | 9 ++--- .../Collections/CollectionShare.tsx | 25 ++++++-------- .../pages/gallery/SelectedFileOptions.tsx | 4 +-- web/apps/photos/src/pages/gallery.tsx | 9 ++--- .../photos/components/CollectionSelector.tsx | 4 +-- .../new/photos/components/gallery/reducer.ts | 34 +++++-------------- .../new/photos/services/collection-summary.ts | 26 ++++++-------- 7 files changed, 40 insertions(+), 71 deletions(-) diff --git a/web/apps/photos/src/components/Collections/CollectionHeader.tsx b/web/apps/photos/src/components/Collections/CollectionHeader.tsx index 19231e5b28..4e8c251d09 100644 --- a/web/apps/photos/src/components/Collections/CollectionHeader.tsx +++ b/web/apps/photos/src/components/Collections/CollectionHeader.tsx @@ -358,8 +358,7 @@ const CollectionHeaderOptions: React.FC = ({ ]; break; - case "incomingShareViewer": - case "incomingShareCollaborator": + case "sharedIncoming": menuOptions = [ collectionSummary.attributes.has("archived") ? ( = ({ }) => ( = ({ return <>; } - const { type } = collectionSummary; + const isSharedIncoming = collectionSummary.type == "sharedIncoming"; return ( @@ -159,17 +156,17 @@ export const CollectionShare: React.FC = ({ onClose={onClose} onRootClose={onClose} title={ - type == "incomingShareCollaborator" || - type == "incomingShareViewer" + isSharedIncoming ? t("sharing_details") : t("share_album") } caption={collection.name} /> - {type == "incomingShareCollaborator" || - type == "incomingShareViewer" ? ( - + {isSharedIncoming ? ( + ) : ( <> = ({ ); }; -type SharingDetailsProps = { type: CollectionSummaryType } & Pick< +type SharingDetailsProps = Pick< CollectionShareProps, - "user" | "collection" + "user" | "collection" | "collectionSummary" >; const SharingDetails: React.FC = ({ user, collection, - type, + collectionSummary, }) => { const isOwner = user.id == collection.owner?.id; @@ -237,7 +234,7 @@ const SharingDetails: React.FC = ({ /> - {type == "incomingShareCollaborator" && + {collectionSummary.attributes.has("sharedIncomingCollaborator") && collaborators.length > 0 && ( }> diff --git a/web/apps/photos/src/components/pages/gallery/SelectedFileOptions.tsx b/web/apps/photos/src/components/pages/gallery/SelectedFileOptions.tsx index 5a9ce14662..7baa0f80ac 100644 --- a/web/apps/photos/src/components/pages/gallery/SelectedFileOptions.tsx +++ b/web/apps/photos/src/components/pages/gallery/SelectedFileOptions.tsx @@ -94,7 +94,7 @@ const SelectedFileOptions = ({ const isUncategorizedCollection = activeCollectionSummary?.type == "uncategorized"; - const isIncomingSharedCollection = + const isSharedIncomingCollection = activeCollectionSummary?.attributes.has("sharedIncoming"); const addToCollection = () => @@ -286,7 +286,7 @@ const SelectedFileOptions = ({ - ) : isIncomingSharedCollection ? ( + ) : isSharedIncomingCollection ? ( diff --git a/web/apps/photos/src/pages/gallery.tsx b/web/apps/photos/src/pages/gallery.tsx index 4bfd515381..0237dad2b5 100644 --- a/web/apps/photos/src/pages/gallery.tsx +++ b/web/apps/photos/src/pages/gallery.tsx @@ -1054,12 +1054,9 @@ const Page: React.FC = () => { setSelected={setSelected} activeCollectionID={activeCollectionID} activePersonID={activePerson?.id} - isInIncomingSharedCollection={ - normalCollectionSummaries.get(activeCollectionID) - ?.type == "incomingShareCollaborator" || - normalCollectionSummaries.get(activeCollectionID) - ?.type == "incomingShareViewer" - } + isInIncomingSharedCollection={activeCollectionSummary?.attributes.has( + "sharedIncoming", + )} isInHiddenSection={barMode == "hidden-albums"} {...{ favoriteFileIDs, diff --git a/web/packages/new/photos/components/CollectionSelector.tsx b/web/packages/new/photos/components/CollectionSelector.tsx index 7ba14014c5..ee3a031734 100644 --- a/web/packages/new/photos/components/CollectionSelector.tsx +++ b/web/packages/new/photos/components/CollectionSelector.tsx @@ -122,11 +122,11 @@ export const CollectionSelector: React.FC = ({ } const collections = [...collectionSummaries.values()] - .filter(({ id, type }) => { + .filter(({ id, attributes: csAttributes, type }) => { if (id === attributes.relatedCollectionID) { return false; } else if (attributes.action == "add") { - return canAddToCollection(type); + return canAddToCollection(type, csAttributes); } else if (attributes.action == "upload") { return canMoveToCollection(type) || type == "uncategorized"; } else if (attributes.action == "restore") { diff --git a/web/packages/new/photos/components/gallery/reducer.ts b/web/packages/new/photos/components/gallery/reducer.ts index e0c905b092..b11316254c 100644 --- a/web/packages/new/photos/components/gallery/reducer.ts +++ b/web/packages/new/photos/components/gallery/reducer.ts @@ -1424,11 +1424,7 @@ const createCollectionSummaries = ( let sortPriority: CollectionSummarySortPriority = CollectionSummarySortPriority.other; if (collection.owner.id != user.id) { - type = - collection.sharees.find((s) => s.id == user.id)?.role == - "COLLABORATOR" - ? "incomingShareCollaborator" - : "incomingShareViewer"; + type = "sharedIncoming"; } else if (collectionType == "uncategorized") { type = "uncategorized"; sortPriority = CollectionSummarySortPriority.system; @@ -1437,9 +1433,9 @@ const createCollectionSummaries = ( // // "favorites" can be both the user's own favorites, or favorites of // other users shared with them. However, all of the latter will get - // typed as "incomingShareViewer" or "incomingShareCollaborator" in - // the first case above. So if a collection summary has type - // "favorites", it is guaranteed to be the user's own favorites. + // typed as "sharedIncoming" in the first case above. So if a + // collection summary has type "favorites", it is guaranteed to be + // the user's own favorites. // // However, notice that the type of the _collection_ itself is not // changed, so whenever we're checking the type of the collection @@ -1453,10 +1449,6 @@ const createCollectionSummaries = ( // of the `attributes` computed below. type = collectionType; sortPriority = CollectionSummarySortPriority.favorites; - } else if (isOutgoingShare(collection, user)) { - type = "outgoingShare"; - } else if (isSharedOnlyViaLink(collection)) { - type = "sharedOnlyViaLink"; } else if (isDefaultHiddenCollection(collection)) { type = "defaultHidden"; } else { @@ -1472,15 +1464,15 @@ const createCollectionSummaries = ( attributes.add( collection.sharees.find((s) => s.id == user.id)?.role == "COLLABORATOR" - ? "incomingShareCollaborator" - : "incomingShareViewer", + ? "sharedIncomingCollaborator" + : "sharedIncomingViewer", ); } - if (isOutgoingShare(collection, user)) { + if (collection.owner.id == user.id && collection.sharees.length) { attributes.add("shared"); - attributes.add("outgoingShare"); + attributes.add("sharedOutgoing"); } - if (isSharedOnlyViaLink(collection)) { + if (collection.publicURLs.length && !collection.sharees.length) { attributes.add("shared"); attributes.add("sharedOnlyViaLink"); } @@ -1579,14 +1571,6 @@ const findCoverFiles = ( return coverFiles; }; -const isOutgoingShare = (collection: Collection, user: User) => - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - collection.owner.id === user.id && collection.sharees?.length > 0; - -const isSharedOnlyViaLink = (collection: Collection) => - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - collection.publicURLs?.length && !collection.sharees?.length; - /** * Compute the {@link GalleryView} from its dependencies when we are switching * to (or back to) the "albums" view, or the underlying collections might've diff --git a/web/packages/new/photos/services/collection-summary.ts b/web/packages/new/photos/services/collection-summary.ts index 38d7b89687..1bb344299d 100644 --- a/web/packages/new/photos/services/collection-summary.ts +++ b/web/packages/new/photos/services/collection-summary.ts @@ -8,10 +8,7 @@ export type CollectionSummaryType = | "trash" | "hiddenItems" | "defaultHidden" - | "outgoingShare" - | "incomingShareViewer" - | "incomingShareCollaborator" - | "sharedOnlyViaLink"; + | "sharedIncoming"; export type CollectionSummaryAttribute = | CollectionType @@ -21,10 +18,10 @@ export type CollectionSummaryAttribute = | "hiddenItems" | "defaultHidden" | "shared" - | "outgoingShare" + | "sharedOutgoing" | "sharedIncoming" - | "incomingShareViewer" - | "incomingShareCollaborator" + | "sharedIncomingViewer" + | "sharedIncomingCollaborator" | "sharedOnlyViaLink" | "archived" | "pinned"; @@ -224,14 +221,9 @@ const systemCSTypes = new Set([ "defaultHidden", ]); -const addToDisabledCSTypes = new Set([ - ...systemCSTypes, - "incomingShareViewer", -]); - const moveToDisabledCSTypes = new Set([ - ...addToDisabledCSTypes, - "incomingShareCollaborator", + ...systemCSTypes, + "sharedIncoming", ]); const hideFromCollectionBarCSTypes = new Set([ @@ -251,8 +243,10 @@ export const areOnlySystemCollections = ( isSystemCollection(type), ); -export const canAddToCollection = (type: CollectionSummaryType) => - !addToDisabledCSTypes.has(type); +export const canAddToCollection = ( + type: CollectionSummaryType, + attributes: Set, +) => !systemCSTypes.has(type) && !attributes.has("sharedIncomingViewer"); export const canMoveToCollection = (type: CollectionSummaryType) => !moveToDisabledCSTypes.has(type); From 3f87fd1a5aa99628cb5b2ec26b2bf033d24f57d3 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Fri, 27 Jun 2025 17:17:48 +0530 Subject: [PATCH 10/13] Rename --- .../src/components/Collections/CollectionHeader.tsx | 2 +- .../new/photos/components/gallery/reducer.ts | 4 ++-- .../new/photos/services/collection-summary.ts | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/web/apps/photos/src/components/Collections/CollectionHeader.tsx b/web/apps/photos/src/components/Collections/CollectionHeader.tsx index 4e8c251d09..ba37199e03 100644 --- a/web/apps/photos/src/components/Collections/CollectionHeader.tsx +++ b/web/apps/photos/src/components/Collections/CollectionHeader.tsx @@ -114,7 +114,7 @@ export const CollectionHeader: React.FC = (props) => { }; const shouldShowOptions = (type: CollectionSummaryType) => - type != "all" && type != "archive"; + type != "all" && type != "archiveItems"; const CollectionHeaderOptions: React.FC = ({ activeCollection, diff --git a/web/packages/new/photos/components/gallery/reducer.ts b/web/packages/new/photos/components/gallery/reducer.ts index b11316254c..22a77b07e9 100644 --- a/web/packages/new/photos/components/gallery/reducer.ts +++ b/web/packages/new/photos/components/gallery/reducer.ts @@ -1343,8 +1343,8 @@ const deriveNormalCollectionSummaries = ( ...pseudoCollectionOptionsForFiles(archiveItemsFiles), id: PseudoCollectionID.archiveItems, name: t("section_archive"), - type: "archive", - attributes: new Set(["archive"]), + type: "archiveItems", + attributes: new Set(["archiveItems"]), coverFile: undefined, sortPriority: CollectionSummarySortPriority.other, }); diff --git a/web/packages/new/photos/services/collection-summary.ts b/web/packages/new/photos/services/collection-summary.ts index 1bb344299d..7611e62d32 100644 --- a/web/packages/new/photos/services/collection-summary.ts +++ b/web/packages/new/photos/services/collection-summary.ts @@ -4,19 +4,19 @@ import type { EnteFile } from "ente-media/file"; export type CollectionSummaryType = | CollectionType | "all" - | "archive" - | "trash" | "hiddenItems" | "defaultHidden" + | "archiveItems" + | "trash" | "sharedIncoming"; export type CollectionSummaryAttribute = | CollectionType | "all" - | "archive" - | "trash" | "hiddenItems" | "defaultHidden" + | "archiveItems" + | "trash" | "shared" | "sharedOutgoing" | "sharedIncoming" @@ -214,7 +214,7 @@ export type CollectionSummarySortPriority = const systemCSTypes = new Set([ "all", - "archive", + "archiveItems", "trash", "uncategorized", "hiddenItems", @@ -228,7 +228,7 @@ const moveToDisabledCSTypes = new Set([ const hideFromCollectionBarCSTypes = new Set([ "trash", - "archive", + "archiveItems", "uncategorized", "defaultHidden", ]); From e70d704cede6366673a34b40a7859bb6c56e0c61 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Fri, 27 Jun 2025 17:46:15 +0530 Subject: [PATCH 11/13] attr --- .../Collections/CollectionHeader.tsx | 48 ++++++++----------- .../pages/gallery/SelectedFileOptions.tsx | 5 +- .../new/photos/components/gallery/BarImpl.tsx | 2 +- .../new/photos/components/gallery/reducer.ts | 30 +++++------- .../new/photos/services/collection-summary.ts | 9 +--- 5 files changed, 37 insertions(+), 57 deletions(-) diff --git a/web/apps/photos/src/components/Collections/CollectionHeader.tsx b/web/apps/photos/src/components/Collections/CollectionHeader.tsx index ba37199e03..81b710a0b1 100644 --- a/web/apps/photos/src/components/Collections/CollectionHeader.tsx +++ b/web/apps/photos/src/components/Collections/CollectionHeader.tsx @@ -89,12 +89,8 @@ export const CollectionHeader: React.FC = (props) => { if (attributes.has("archived")) return ; if (attributes.has("sharedOnlyViaLink")) return ; if (attributes.has("shared")) return ; - switch (type) { - case "favorites": - return ; - default: - return <>; - } + if (attributes.has("userFavorites")) return ; + return <>; }; return ( @@ -313,7 +309,7 @@ const CollectionHeaderOptions: React.FC = ({ ]; break; - case "favorites": + case "userFavorites": menuOptions = [ = ({ boolean; onEmptyTrashClick: () => void; onDownloadClick: () => void; @@ -545,11 +539,10 @@ const QuickOptions: React.FC = ({ onDownloadClick, onShareClick, collectionSummary, - collectionSummaryType: type, isDownloadInProgress, }) => ( - {showEmptyTrashQuickOption(type) && ( + {showEmptyTrashQuickOption(collectionSummary) && ( )} {showDownloadQuickOption(collectionSummary) && @@ -557,21 +550,20 @@ const QuickOptions: React.FC = ({ ) : ( ))} {showShareQuickOption(collectionSummary) && ( )} ); -const showEmptyTrashQuickOption = (type: CollectionSummaryType) => +const showEmptyTrashQuickOption = ({ type }: CollectionSummary) => type == "trash"; const EmptyTrashQuickOption: React.FC = ({ onClick }) => ( @@ -585,26 +577,26 @@ const EmptyTrashQuickOption: React.FC = ({ onClick }) => ( const showDownloadQuickOption = ({ type, attributes }: CollectionSummary) => type == "album" || type == "folder" || - type == "favorites" || type == "uncategorized" || type == "hiddenItems" || + attributes.has("favorites") || attributes.has("shared"); type DownloadQuickOptionProps = OptionProps & { - collectionSummaryType: CollectionSummaryType; + collectionSummary: CollectionSummary; }; const DownloadQuickOption: React.FC = ({ + collectionSummary: { type }, onClick, - collectionSummaryType, }) => ( = ({ const showShareQuickOption = ({ type, attributes }: CollectionSummary) => type == "album" || type == "folder" || - type == "favorites" || + attributes.has("favorites") || attributes.has("shared"); interface ShareQuickOptionProps { - onClick: () => void; collectionSummary: CollectionSummary; - collectionSummaryType: CollectionSummaryType; + onClick: () => void; } const ShareQuickOption: React.FC = ({ + collectionSummary: { attributes }, onClick, - collectionSummary, - collectionSummaryType, }) => ( onOpenCollectionSelector({ diff --git a/web/packages/new/photos/components/gallery/BarImpl.tsx b/web/packages/new/photos/components/gallery/BarImpl.tsx index 7f9c5c6fda..27198b86dc 100644 --- a/web/packages/new/photos/components/gallery/BarImpl.tsx +++ b/web/packages/new/photos/components/gallery/BarImpl.tsx @@ -538,7 +538,7 @@ const CollectionBarCardIcon: React.FC = ({ // will be true simultaneously even in the rarest of cases (a pinned and // shared album that is also archived), and there is enough space for 3. - {attributes.has("favorites") && } + {attributes.has("userFavorites") && } {attributes.has("pinned") && ( // Need && to override the 20px set in the container. diff --git a/web/packages/new/photos/components/gallery/reducer.ts b/web/packages/new/photos/components/gallery/reducer.ts index 22a77b07e9..429c584c5c 100644 --- a/web/packages/new/photos/components/gallery/reducer.ts +++ b/web/packages/new/photos/components/gallery/reducer.ts @@ -1344,7 +1344,7 @@ const deriveNormalCollectionSummaries = ( id: PseudoCollectionID.archiveItems, name: t("section_archive"), type: "archiveItems", - attributes: new Set(["archiveItems"]), + attributes: new Set(), coverFile: undefined, sortPriority: CollectionSummarySortPriority.other, }); @@ -1424,6 +1424,8 @@ const createCollectionSummaries = ( let sortPriority: CollectionSummarySortPriority = CollectionSummarySortPriority.other; if (collection.owner.id != user.id) { + // This case needs to be the first; the rest assume that they're + // dealing with collections owned by the user. type = "sharedIncoming"; } else if (collectionType == "uncategorized") { type = "uncategorized"; @@ -1433,9 +1435,12 @@ const createCollectionSummaries = ( // // "favorites" can be both the user's own favorites, or favorites of // other users shared with them. However, all of the latter will get - // typed as "sharedIncoming" in the first case above. So if a - // collection summary has type "favorites", it is guaranteed to be - // the user's own favorites. + // typed as "sharedIncoming" in the first case above. + // + // So if we get here and the collection summary has type + // "favorites", it is guaranteed to be the user's own favorites. We + // mark these with the type "userFavorites", which gives it special + // treatment like custom icon etc. // // However, notice that the type of the _collection_ itself is not // changed, so whenever we're checking the type of the collection @@ -1447,7 +1452,7 @@ const createCollectionSummaries = ( // classification of this collection summary is that it is the // user's "favorites", everything else is secondary and can be part // of the `attributes` computed below. - type = collectionType; + type = "userFavorites"; sortPriority = CollectionSummarySortPriority.favorites; } else if (isDefaultHiddenCollection(collection)) { type = "defaultHidden"; @@ -1486,23 +1491,12 @@ const createCollectionSummaries = ( attributes.add("pinned"); sortPriority = CollectionSummarySortPriority.pinned; } - switch (collectionType) { - case "favorites": - // We don't want to treat other folks' favorites specially like - // the user's own favorites (giving it a special icon etc), so - // only apply the favorites attribute if it is the user's own. - if (collection.owner.id == user.id) - attributes.add(collectionType); - break; - default: - attributes.add(collectionType); - break; - } + attributes.add(collectionType); let name: string; if (type == "uncategorized") { name = t("section_uncategorized"); - } else if (type == "favorites") { + } else if (type == "userFavorites") { name = t("favorites"); } else if (collectionType == "favorites") { // See: [Note: User and shared favorites] above. diff --git a/web/packages/new/photos/services/collection-summary.ts b/web/packages/new/photos/services/collection-summary.ts index 7611e62d32..07a6d1e7d5 100644 --- a/web/packages/new/photos/services/collection-summary.ts +++ b/web/packages/new/photos/services/collection-summary.ts @@ -8,18 +8,13 @@ export type CollectionSummaryType = | "defaultHidden" | "archiveItems" | "trash" + | "userFavorites" | "sharedIncoming"; export type CollectionSummaryAttribute = - | CollectionType - | "all" - | "hiddenItems" - | "defaultHidden" - | "archiveItems" - | "trash" + | CollectionSummaryType | "shared" | "sharedOutgoing" - | "sharedIncoming" | "sharedIncomingViewer" | "sharedIncomingCollaborator" | "sharedOnlyViaLink" From 5141b04695cc764e4ab2c1386b2cd05e5914d579 Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Fri, 27 Jun 2025 18:15:16 +0530 Subject: [PATCH 12/13] attr fin --- .../Collections/CollectionHeader.tsx | 12 ++-- .../Collections/GalleryBarAndListHeader.tsx | 12 ++-- web/apps/photos/src/pages/gallery.tsx | 4 +- .../photos/components/CollectionSelector.tsx | 16 +++-- .../new/photos/components/gallery/reducer.ts | 65 ++++++++++--------- .../new/photos/services/collection-summary.ts | 45 +++---------- 6 files changed, 67 insertions(+), 87 deletions(-) diff --git a/web/apps/photos/src/components/Collections/CollectionHeader.tsx b/web/apps/photos/src/components/Collections/CollectionHeader.tsx index 81b710a0b1..31a174b009 100644 --- a/web/apps/photos/src/components/Collections/CollectionHeader.tsx +++ b/web/apps/photos/src/components/Collections/CollectionHeader.tsx @@ -624,12 +624,12 @@ const ShareQuickOption: React.FC = ({ }) => ( diff --git a/web/apps/photos/src/components/Collections/GalleryBarAndListHeader.tsx b/web/apps/photos/src/components/Collections/GalleryBarAndListHeader.tsx index 6b3d7ff905..5ee2715ef9 100644 --- a/web/apps/photos/src/components/Collections/GalleryBarAndListHeader.tsx +++ b/web/apps/photos/src/components/Collections/GalleryBarAndListHeader.tsx @@ -12,11 +12,9 @@ import { } from "ente-new/photos/components/gallery/BarImpl"; import { PeopleHeader } from "ente-new/photos/components/gallery/PeopleHeader"; import { - areOnlySystemCollections, collectionsSortBy, - isSystemCollection, + haveOnlySystemCollections, PseudoCollectionID, - shouldShowOnCollectionBar, type CollectionsSortBy, type CollectionSummaries, } from "ente-new/photos/services/collection-summary"; @@ -114,7 +112,7 @@ export const GalleryBarAndListHeader: React.FC< const shouldBeHidden = useMemo( () => shouldHide || - (areOnlySystemCollections(toShowCollectionSummaries) && + (haveOnlySystemCollections(toShowCollectionSummaries) && activeCollectionID === PseudoCollectionID.all), [shouldHide, toShowCollectionSummaries, activeCollectionID], ); @@ -203,15 +201,15 @@ export const GalleryBarAndListHeader: React.FC< onSelectCollectionID={setActiveCollectionID} onChangeCollectionsSortBy={setCollectionsSortBy} onShowAllAlbums={showAllAlbums} - collectionSummaries={sortedCollectionSummaries.filter((x) => - shouldShowOnCollectionBar(x.type), + collectionSummaries={sortedCollectionSummaries.filter( + (cs) => !cs.attributes.has("hideFromCollectionBar"), )} /> !isSystemCollection(x.type), + (cs) => !cs.attributes.has("system"), )} onSelectCollectionID={setActiveCollectionID} onChangeCollectionsSortBy={setCollectionsSortBy} diff --git a/web/apps/photos/src/pages/gallery.tsx b/web/apps/photos/src/pages/gallery.tsx index 0237dad2b5..898932b45f 100644 --- a/web/apps/photos/src/pages/gallery.tsx +++ b/web/apps/photos/src/pages/gallery.tsx @@ -69,7 +69,7 @@ import { usePeopleStateSnapshot } from "ente-new/photos/components/utils/use-sna import { shouldShowWhatsNew } from "ente-new/photos/services/changelog"; import { createAlbum } from "ente-new/photos/services/collection"; import { - areOnlySystemCollections, + haveOnlySystemCollections, PseudoCollectionID, } from "ente-new/photos/services/collection-summary"; import exportService from "ente-new/photos/services/export"; @@ -1006,7 +1006,7 @@ const Page: React.FC = () => { onRemoteFilesPull={remoteFilesPull} onUploadFile={(file) => dispatch({ type: "uploadFile", file })} onShowPlanSelector={showPlanSelector} - isFirstUpload={areOnlySystemCollections( + isFirstUpload={haveOnlySystemCollections( normalCollectionSummaries, )} showSessionExpiredMessage={showSessionExpiredDialog} diff --git a/web/packages/new/photos/components/CollectionSelector.tsx b/web/packages/new/photos/components/CollectionSelector.tsx index ee3a031734..78e913b0b6 100644 --- a/web/packages/new/photos/components/CollectionSelector.tsx +++ b/web/packages/new/photos/components/CollectionSelector.tsx @@ -122,17 +122,21 @@ export const CollectionSelector: React.FC = ({ } const collections = [...collectionSummaries.values()] - .filter(({ id, attributes: csAttributes, type }) => { - if (id === attributes.relatedCollectionID) { + .filter((cs) => { + if (cs.id === attributes.relatedCollectionID) { return false; } else if (attributes.action == "add") { - return canAddToCollection(type, csAttributes); + return canAddToCollection(cs); } else if (attributes.action == "upload") { - return canMoveToCollection(type) || type == "uncategorized"; + return ( + canMoveToCollection(cs) || cs.type == "uncategorized" + ); } else if (attributes.action == "restore") { - return canMoveToCollection(type) || type == "uncategorized"; + return ( + canMoveToCollection(cs) || cs.type == "uncategorized" + ); } else { - return canMoveToCollection(type); + return canMoveToCollection(cs); } }) .sort((a, b) => a.name.localeCompare(b.name)) diff --git a/web/packages/new/photos/components/gallery/reducer.ts b/web/packages/new/photos/components/gallery/reducer.ts index 429c584c5c..741ffc39d2 100644 --- a/web/packages/new/photos/components/gallery/reducer.ts +++ b/web/packages/new/photos/components/gallery/reducer.ts @@ -1299,7 +1299,11 @@ const deriveNormalCollectionSummaries = ( ...pseudoCollectionOptionsForFiles([]), id, type: "uncategorized", - attributes: new Set(["uncategorized"]), + attributes: new Set([ + "uncategorized", + "system", + "hideFromCollectionBar", + ]), name: t("section_uncategorized"), sortPriority: CollectionSummarySortPriority.system, }); @@ -1321,7 +1325,7 @@ const deriveNormalCollectionSummaries = ( ...pseudoCollectionOptionsForFiles(allSectionFiles), id: PseudoCollectionID.all, type: "all", - attributes: new Set(["all"]), + attributes: new Set(["all", "system"]), name: t("section_all"), sortPriority: CollectionSummarySortPriority.system, }); @@ -1334,7 +1338,7 @@ const deriveNormalCollectionSummaries = ( id: PseudoCollectionID.trash, name: t("section_trash"), type: "trash", - attributes: new Set(["trash"]), + attributes: new Set(["trash", "system", "hideFromCollectionBar"]), coverFile: undefined, sortPriority: CollectionSummarySortPriority.other, }); @@ -1344,7 +1348,11 @@ const deriveNormalCollectionSummaries = ( id: PseudoCollectionID.archiveItems, name: t("section_archive"), type: "archiveItems", - attributes: new Set(), + attributes: new Set([ + "archiveItems", + "system", + "hideFromCollectionBar", + ]), coverFile: undefined, sortPriority: CollectionSummarySortPriority.other, }); @@ -1388,7 +1396,7 @@ const deriveHiddenCollectionSummaries = ( id: PseudoCollectionID.hiddenItems, name: t("hidden_items"), type: "hiddenItems", - attributes: new Set(["hiddenItems"]), + attributes: new Set(["hiddenItems", "system"]), sortPriority: CollectionSummarySortPriority.system, }); @@ -1420,15 +1428,30 @@ const createCollectionSummaries = ( ? collection.type : "album"; + const attributes = new Set(); + let type: CollectionSummaryType; + let name = collection.name; let sortPriority: CollectionSummarySortPriority = CollectionSummarySortPriority.other; + if (collection.owner.id != user.id) { // This case needs to be the first; the rest assume that they're // dealing with collections owned by the user. type = "sharedIncoming"; + attributes.add("shared"); + attributes.add("sharedIncoming"); + attributes.add( + collection.sharees.find((s) => s.id == user.id)?.role == + "COLLABORATOR" + ? "sharedIncomingCollaborator" + : "sharedIncomingViewer", + ); } else if (collectionType == "uncategorized") { type = "uncategorized"; + name = t("section_uncategorized"); + attributes.add("system"); + attributes.add("hideFromCollectionBar"); sortPriority = CollectionSummarySortPriority.system; } else if (collectionType == "favorites") { // [Note: User and shared favorites] @@ -1453,26 +1476,19 @@ const createCollectionSummaries = ( // user's "favorites", everything else is secondary and can be part // of the `attributes` computed below. type = "userFavorites"; + name = t("favorites"); sortPriority = CollectionSummarySortPriority.favorites; } else if (isDefaultHiddenCollection(collection)) { type = "defaultHidden"; + attributes.add("system"); + attributes.add("hideFromCollectionBar"); } else { type = collectionType; } - // This block of code duplicates the above. Such duplication is needed - // until type is completely replaced by attributes. - const attributes = new Set(); - if (collection.owner.id != user.id) { - attributes.add("shared"); - attributes.add("sharedIncoming"); - attributes.add( - collection.sharees.find((s) => s.id == user.id)?.role == - "COLLABORATOR" - ? "sharedIncomingCollaborator" - : "sharedIncomingViewer", - ); - } + attributes.add(type); + attributes.add(collectionType); + if (collection.owner.id == user.id && collection.sharees.length) { attributes.add("shared"); attributes.add("sharedOutgoing"); @@ -1484,21 +1500,12 @@ const createCollectionSummaries = ( if (isArchivedCollection(collection)) { attributes.add("archived"); } - if (isDefaultHiddenCollection(collection)) { - attributes.add("defaultHidden"); - } if (collection.magicMetadata?.data.order == CollectionOrder.pinned) { attributes.add("pinned"); sortPriority = CollectionSummarySortPriority.pinned; } - attributes.add(collectionType); - let name: string; - if (type == "uncategorized") { - name = t("section_uncategorized"); - } else if (type == "userFavorites") { - name = t("favorites"); - } else if (collectionType == "favorites") { + if (type == "sharedIncoming" && collectionType == "favorites") { // See: [Note: User and shared favorites] above. // // Use the first letter of the email of the user who shared this @@ -1511,8 +1518,6 @@ const createCollectionSummaries = ( } else { name = t("shared_favorites"); } - } else { - name = collection.name; } const collectionFiles = filesByCollection.get(collection.id); diff --git a/web/packages/new/photos/services/collection-summary.ts b/web/packages/new/photos/services/collection-summary.ts index 07a6d1e7d5..a735dc8114 100644 --- a/web/packages/new/photos/services/collection-summary.ts +++ b/web/packages/new/photos/services/collection-summary.ts @@ -18,6 +18,8 @@ export type CollectionSummaryAttribute = | "sharedIncomingViewer" | "sharedIncomingCollaborator" | "sharedOnlyViaLink" + | "system" + | "hideFromCollectionBar" | "archived" | "pinned"; @@ -207,44 +209,15 @@ export const CollectionSummarySortPriority = { export type CollectionSummarySortPriority = (typeof CollectionSummarySortPriority)[keyof typeof CollectionSummarySortPriority]; -const systemCSTypes = new Set([ - "all", - "archiveItems", - "trash", - "uncategorized", - "hiddenItems", - "defaultHidden", -]); - -const moveToDisabledCSTypes = new Set([ - ...systemCSTypes, - "sharedIncoming", -]); - -const hideFromCollectionBarCSTypes = new Set([ - "trash", - "archiveItems", - "uncategorized", - "defaultHidden", -]); - -export const isSystemCollection = (type: CollectionSummaryType) => - systemCSTypes.has(type); - -export const areOnlySystemCollections = ( +export const haveOnlySystemCollections = ( collectionSummaries: CollectionSummaries, ) => - [...collectionSummaries.values()].every(({ type }) => - isSystemCollection(type), + [...collectionSummaries.values()].every((cs) => + cs.attributes.has("system"), ); -export const canAddToCollection = ( - type: CollectionSummaryType, - attributes: Set, -) => !systemCSTypes.has(type) && !attributes.has("sharedIncomingViewer"); +export const canAddToCollection = ({ attributes }: CollectionSummary) => + !attributes.has("system") && !attributes.has("sharedIncomingViewer"); -export const canMoveToCollection = (type: CollectionSummaryType) => - !moveToDisabledCSTypes.has(type); - -export const shouldShowOnCollectionBar = (type: CollectionSummaryType) => - !hideFromCollectionBarCSTypes.has(type); +export const canMoveToCollection = ({ attributes }: CollectionSummary) => + !attributes.has("system") && !attributes.has("sharedIncoming"); From 4a81c56a5576301371d40fa498c7ca49347c3bbd Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Fri, 27 Jun 2025 18:54:07 +0530 Subject: [PATCH 13/13] Elsewhere --- .../Collections/CollectionShare.tsx | 83 ++++++++++++++++--- 1 file changed, 70 insertions(+), 13 deletions(-) diff --git a/web/apps/photos/src/components/Collections/CollectionShare.tsx b/web/apps/photos/src/components/Collections/CollectionShare.tsx index d973ff7874..d49f9e389e 100644 --- a/web/apps/photos/src/components/Collections/CollectionShare.tsx +++ b/web/apps/photos/src/components/Collections/CollectionShare.tsx @@ -165,7 +165,12 @@ export const CollectionShare: React.FC = ({ {isSharedIncoming ? ( ) : ( <> @@ -198,13 +203,14 @@ export const CollectionShare: React.FC = ({ type SharingDetailsProps = Pick< CollectionShareProps, - "user" | "collection" | "collectionSummary" + "user" | "collection" | "emailByUserID" | "collectionSummary" >; const SharingDetails: React.FC = ({ user, collection, collectionSummary, + emailByUserID, }) => { const isOwner = user.id == collection.owner?.id; @@ -229,7 +235,12 @@ const SharingDetails: React.FC = ({ } + startIcon={ + + } label={isOwner ? t("you") : ownerEmail} /> @@ -244,7 +255,12 @@ const SharingDetails: React.FC = ({ {collaborators.map((email, index) => ( } + startIcon={ + + } label={userOrEmail(email)} /> {index != collaborators.length - 1 && ( @@ -264,7 +280,12 @@ const SharingDetails: React.FC = ({ {viewers.map((email, index) => ( } + startIcon={ + + } label={userOrEmail(email)} /> {index != viewers.length - 1 && ( @@ -387,6 +408,7 @@ const EmailShare: React.FC = ({ onRootClose, user, collection, + emailByUserID, shareSuggestionEmails, onRemotePull, }} @@ -398,6 +420,7 @@ const EmailShare: React.FC = ({ onRootClose, user, collection, + emailByUserID, shareSuggestionEmails, participantCount, wrap, @@ -471,7 +494,11 @@ type AddParticipantProps = ModalVisibilityProps & { role: CollectionNewParticipantRole; } & Pick< CollectionShareProps, - "user" | "collection" | "shareSuggestionEmails" | "onRemotePull" + | "user" + | "collection" + | "emailByUserID" + | "shareSuggestionEmails" + | "onRemotePull" >; const AddParticipant: React.FC = ({ @@ -480,6 +507,7 @@ const AddParticipant: React.FC = ({ onRootClose, user, collection, + emailByUserID, shareSuggestionEmails, role, onRemotePull, @@ -558,6 +586,7 @@ const AddParticipant: React.FC = ({ caption={collection.name} > = ({ ); }; -interface AddParticipantFormProps { +type AddParticipantFormProps = { /** * Title for the submit button. */ @@ -588,9 +617,11 @@ interface AddParticipantFormProps { emailOrEmails: string | string[], setEmailFieldError: (message: string) => void, ) => Promise; -} +} & Pick; const AddParticipantForm: React.FC = ({ + user, + emailByUserID, existingEmails, submitButtonTitle, onSubmit, @@ -666,7 +697,12 @@ const AddParticipantForm: React.FC = ({ ); }} label={email} - startIcon={} + startIcon={ + + } endIcon={ formik.values.selectedEmails.includes( email, @@ -703,7 +739,11 @@ type ManageEmailShareProps = ModalVisibilityProps & { wrap: (f: () => Promise) => () => void; } & Pick< CollectionShareProps, - "user" | "collection" | "shareSuggestionEmails" | "onRemotePull" + | "user" + | "collection" + | "emailByUserID" + | "shareSuggestionEmails" + | "onRemotePull" >; const ManageEmailShare: React.FC = ({ @@ -712,6 +752,7 @@ const ManageEmailShare: React.FC = ({ onRootClose, user, collection, + emailByUserID, shareSuggestionEmails, participantCount, wrap, @@ -780,7 +821,12 @@ const ManageEmailShare: React.FC = ({ } + startIcon={ + + } label={isOwner ? t("you") : ownerEmail} /> @@ -798,7 +844,12 @@ const ManageEmailShare: React.FC = ({ openManageParticipant(item) } label={item} - startIcon={} + startIcon={ + + } endIcon={} /> @@ -829,7 +880,12 @@ const ManageEmailShare: React.FC = ({ openManageParticipant(item) } label={item} - startIcon={} + startIcon={ + + } endIcon={} /> @@ -853,6 +909,7 @@ const ManageEmailShare: React.FC = ({ {...{ user, collection, + emailByUserID, shareSuggestionEmails, onRootClose, onRemotePull,