[web] Use reducer for gallery - Part 4/x (#3790)
This commit is contained in:
@@ -2,7 +2,7 @@ import log from "@/base/log";
|
||||
import type { LivePhotoSourceURL, SourceURLs } from "@/media/file";
|
||||
import { EnteFile } from "@/media/file";
|
||||
import { FileType } from "@/media/file-type";
|
||||
import type { GalleryBarMode } from "@/new/photos/components/gallery/BarImpl";
|
||||
import type { GalleryBarMode } from "@/new/photos/components/gallery/reducer";
|
||||
import { TRASH_SECTION } from "@/new/photos/services/collection";
|
||||
import DownloadManager from "@/new/photos/services/download";
|
||||
import { PHOTOS_PAGES } from "@ente/shared/constants/pages";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { SelectionBar } from "@/base/components/Navbar";
|
||||
import type { Collection } from "@/media/collection";
|
||||
import type { CollectionSelectorAttributes } from "@/new/photos/components/CollectionSelector";
|
||||
import type { GalleryBarMode } from "@/new/photos/components/gallery/BarImpl";
|
||||
import type { GalleryBarMode } from "@/new/photos/components/gallery/reducer";
|
||||
import {
|
||||
ALL_SECTION,
|
||||
ARCHIVE_SECTION,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { stashRedirect } from "@/accounts/services/redirect";
|
||||
import { ActivityIndicator } from "@/base/components/mui/ActivityIndicator";
|
||||
import { ALL_SECTION } from "@/new/photos/services/collection";
|
||||
import { createFileCollectionIDs } from "@/new/photos/services/file";
|
||||
import { getLocalFiles } from "@/new/photos/services/files";
|
||||
import { AppContext } from "@/new/photos/types/context";
|
||||
import { VerticallyCentered } from "@ente/shared/components/Container";
|
||||
@@ -28,7 +29,7 @@ import {
|
||||
DefaultDeduplicateContext,
|
||||
} from "types/deduplicate";
|
||||
import { SelectedState } from "types/gallery";
|
||||
import { constructFileToCollectionMap, getSelectedFiles } from "utils/file";
|
||||
import { getSelectedFiles } from "utils/file";
|
||||
|
||||
export const DeduplicateContext = createContext<DeduplicateContextType>(
|
||||
DefaultDeduplicateContext,
|
||||
@@ -114,7 +115,7 @@ export default function Deduplicate() {
|
||||
}, [duplicates]);
|
||||
|
||||
const fileToCollectionsMap = useMemoSingleThreaded(() => {
|
||||
return constructFileToCollectionMap(duplicateFiles);
|
||||
return createFileCollectionIDs(duplicateFiles ?? []);
|
||||
}, [duplicateFiles]);
|
||||
|
||||
const deleteFileHelper = async () => {
|
||||
|
||||
@@ -19,10 +19,10 @@ import {
|
||||
PeopleEmptyState,
|
||||
SearchResultsHeader,
|
||||
} from "@/new/photos/components/gallery";
|
||||
import type { GalleryBarMode } from "@/new/photos/components/gallery/BarImpl";
|
||||
import {
|
||||
uniqueFilesByID,
|
||||
useGalleryReducer,
|
||||
type GalleryBarMode,
|
||||
} from "@/new/photos/components/gallery/reducer";
|
||||
import { usePeopleStateSnapshot } from "@/new/photos/components/utils/ml";
|
||||
import { shouldShowWhatsNew } from "@/new/photos/services/changelog";
|
||||
@@ -137,16 +137,10 @@ import {
|
||||
import { checkSubscriptionPurchase } from "utils/billing";
|
||||
import {
|
||||
COLLECTION_OPS_TYPE,
|
||||
constructCollectionNameMap,
|
||||
getSelectedCollection,
|
||||
handleCollectionOps,
|
||||
} from "utils/collection";
|
||||
import {
|
||||
FILE_OPS_TYPE,
|
||||
constructFileToCollectionMap,
|
||||
getSelectedFiles,
|
||||
handleFileOps,
|
||||
} from "utils/file";
|
||||
import { FILE_OPS_TYPE, getSelectedFiles, handleFileOps } from "utils/file";
|
||||
import { getSessionExpiredMessage } from "utils/ui";
|
||||
import { getLocalFamilyData } from "utils/user/family";
|
||||
|
||||
@@ -252,8 +246,6 @@ export default function Gallery() {
|
||||
const [userIDToEmailMap, setUserIDToEmailMap] =
|
||||
useState<Map<number, string>>(null);
|
||||
const [emailList, setEmailList] = useState<string[]>(null);
|
||||
const [activeCollectionID, setActiveCollectionID] =
|
||||
useState<number>(undefined);
|
||||
const [fixCreationTimeView, setFixCreationTimeView] = useState(false);
|
||||
const [fixCreationTimeAttributes, setFixCreationTimeAttributes] =
|
||||
useState<FixCreationTimeAttributes>(null);
|
||||
@@ -283,20 +275,11 @@ export default function Gallery() {
|
||||
const closeAuthenticateUserModal = () =>
|
||||
setAuthenticateUserModalView(false);
|
||||
|
||||
// True if we're in "search mode". See: [Note: "search mode"].
|
||||
const [isInSearchMode, setIsInSearchMode] = useState(false);
|
||||
|
||||
// The option selected by the user selected from the search bar dropdown.
|
||||
const [selectedSearchOption, setSelectedSearchOption] = useState<
|
||||
SearchOption | undefined
|
||||
>();
|
||||
|
||||
// If visible, what should the (sticky) gallery bar show.
|
||||
const [barMode, setBarMode] = useState<GalleryBarMode>("albums");
|
||||
|
||||
// The ID of the currently selected person in the gallery bar (if any).
|
||||
const [activePersonID, setActivePersonID] = useState<string | undefined>();
|
||||
|
||||
const peopleState = usePeopleStateSnapshot();
|
||||
|
||||
const [isClipSearchResult, setIsClipSearchResult] =
|
||||
@@ -339,8 +322,14 @@ export default function Gallery() {
|
||||
const archivedCollectionIDs = state.archivedCollectionIDs;
|
||||
const defaultHiddenCollectionIDs = state.defaultHiddenCollectionIDs;
|
||||
const hiddenFileIDs = state.hiddenFileIDs;
|
||||
const collectionNameMap = state.allCollectionNameByID;
|
||||
const fileToCollectionsMap = state.fileCollectionIDs;
|
||||
const collectionSummaries = state.collectionSummaries;
|
||||
const hiddenCollectionSummaries = state.hiddenCollectionSummaries;
|
||||
const barMode = state.barMode ?? "albums";
|
||||
const activeCollectionID = state.activeCollectionID;
|
||||
const activePersonID = state.activePersonID;
|
||||
const isInSearchMode = state.isInSearchMode;
|
||||
|
||||
if (process.env.NEXT_PUBLIC_ENTE_WIP_CL) {
|
||||
console.log("render", { collections, hiddenCollections, files });
|
||||
@@ -379,7 +368,7 @@ export default function Gallery() {
|
||||
}
|
||||
await downloadManager.init(token);
|
||||
setupSelectAllKeyBoardShortcutHandler();
|
||||
setActiveCollectionID(ALL_SECTION);
|
||||
dispatch({ type: "showAll" });
|
||||
setIsFirstLoad(isFirstLogin());
|
||||
if (justSignedUp()) {
|
||||
setPlanModalView(true);
|
||||
@@ -746,20 +735,6 @@ export default function Gallery() {
|
||||
};
|
||||
}, [selectAll, clearSelection]);
|
||||
|
||||
const fileToCollectionsMap = useMemoSingleThreaded(() => {
|
||||
return constructFileToCollectionMap(files);
|
||||
}, [files]);
|
||||
|
||||
const collectionNameMap = useMemo(() => {
|
||||
if (!collections || !hiddenCollections) {
|
||||
return new Map();
|
||||
}
|
||||
return constructCollectionNameMap([
|
||||
...collections,
|
||||
...hiddenCollections,
|
||||
]);
|
||||
}, [collections, hiddenCollections]);
|
||||
|
||||
const showSessionExpiredMessage = () => {
|
||||
setDialogMessage(getSessionExpiredMessage(logout));
|
||||
};
|
||||
@@ -999,18 +974,24 @@ export default function Gallery() {
|
||||
) => {
|
||||
const type = searchOption?.suggestion.type;
|
||||
if (type == "collection" || type == "person") {
|
||||
setIsInSearchMode(false);
|
||||
setSelectedSearchOption(undefined);
|
||||
if (type == "collection") {
|
||||
setBarMode("albums");
|
||||
setActiveCollectionID(searchOption.suggestion.collectionID);
|
||||
dispatch({
|
||||
type: "showNormalOrHiddenCollectionSummary",
|
||||
collectionSummaryID: searchOption.suggestion.collectionID,
|
||||
});
|
||||
} else {
|
||||
setBarMode("people");
|
||||
setActivePersonID(searchOption.suggestion.person.id);
|
||||
dispatch({
|
||||
type: "showPerson",
|
||||
personID: searchOption.suggestion.person.id,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
setIsInSearchMode(!!searchOption);
|
||||
setSelectedSearchOption(undefined);
|
||||
} else if (searchOption) {
|
||||
dispatch({ type: "enterSearchMode" });
|
||||
setSelectedSearchOption(searchOption);
|
||||
} else {
|
||||
dispatch({ type: "exitSearch" });
|
||||
setSelectedSearchOption(undefined);
|
||||
}
|
||||
setIsClipSearchResult(type == "clip");
|
||||
};
|
||||
@@ -1031,38 +1012,32 @@ export default function Gallery() {
|
||||
setExportModalView(false);
|
||||
};
|
||||
|
||||
const handleShowCollection = (collectionID: number) => {
|
||||
setBarMode("albums");
|
||||
setActiveCollectionID(collectionID);
|
||||
setIsInSearchMode(false);
|
||||
};
|
||||
const handleSetActiveCollectionID = (
|
||||
collectionSummaryID: number | undefined,
|
||||
) =>
|
||||
dispatch({
|
||||
type: "showNormalOrHiddenCollectionSummary",
|
||||
collectionSummaryID,
|
||||
});
|
||||
|
||||
const handleShowSearchInput = () => setIsInSearchMode(true);
|
||||
const handleChangeBarMode = (mode: GalleryBarMode) =>
|
||||
mode == "people"
|
||||
? dispatch({ type: "showPeople" })
|
||||
: dispatch({ type: "showAll" });
|
||||
|
||||
const openHiddenSection: GalleryContextType["openHiddenSection"] = (
|
||||
callback,
|
||||
) => {
|
||||
authenticateUser(() => {
|
||||
setBarMode("hidden-albums");
|
||||
setActiveCollectionID(HIDDEN_ITEMS_SECTION);
|
||||
dispatch({ type: "showHidden" });
|
||||
callback?.();
|
||||
});
|
||||
};
|
||||
|
||||
const exitHiddenSection = () => {
|
||||
setBarMode("albums");
|
||||
setActiveCollectionID(ALL_SECTION);
|
||||
};
|
||||
|
||||
const handleSelectPerson = (person: Person | undefined) => {
|
||||
setActivePersonID(person?.id);
|
||||
setBarMode("people");
|
||||
};
|
||||
|
||||
const handleSelectFileInfoPerson = (personID: string) => {
|
||||
setActivePersonID(personID);
|
||||
setBarMode("people");
|
||||
};
|
||||
const handleSelectPerson = (person: Person | undefined) =>
|
||||
person
|
||||
? dispatch({ type: "showPerson", personID: person.id })
|
||||
: dispatch({ type: "showPeople" });
|
||||
|
||||
const handleOpenCollectionSelector = useCallback(
|
||||
(attributes: CollectionSelectorAttributes) => {
|
||||
@@ -1091,8 +1066,12 @@ export default function Gallery() {
|
||||
value={{
|
||||
...defaultGalleryContext,
|
||||
showPlanSelectorModal,
|
||||
setActiveCollectionID,
|
||||
onShowCollection: handleShowCollection,
|
||||
setActiveCollectionID: handleSetActiveCollectionID,
|
||||
onShowCollection: (id) =>
|
||||
dispatch({
|
||||
type: "showNormalOrHiddenCollectionSummary",
|
||||
collectionSummaryID: id,
|
||||
}),
|
||||
syncWithRemote,
|
||||
setBlockingLoad,
|
||||
photoListHeader,
|
||||
@@ -1170,7 +1149,7 @@ export default function Gallery() {
|
||||
>
|
||||
{barMode == "hidden-albums" ? (
|
||||
<HiddenSectionNavbarContents
|
||||
onBack={exitHiddenSection}
|
||||
onBack={() => dispatch({ type: "showAll" })}
|
||||
/>
|
||||
) : (
|
||||
<NormalNavbarContents
|
||||
@@ -1178,7 +1157,8 @@ export default function Gallery() {
|
||||
openSidebar,
|
||||
openUploader,
|
||||
isInSearchMode,
|
||||
onShowSearchInput: handleShowSearchInput,
|
||||
onShowSearchInput: () =>
|
||||
dispatch({ type: "enterSearchMode" }),
|
||||
onSelectSearchOption: handleSelectSearchOption,
|
||||
onSelectPerson: handleSelectPerson,
|
||||
}}
|
||||
@@ -1190,11 +1170,11 @@ export default function Gallery() {
|
||||
{...{
|
||||
shouldHide: isInSearchMode,
|
||||
mode: barMode,
|
||||
onChangeMode: setBarMode,
|
||||
onChangeMode: handleChangeBarMode,
|
||||
collectionSummaries,
|
||||
activeCollection,
|
||||
activeCollectionID,
|
||||
setActiveCollectionID,
|
||||
setActiveCollectionID: handleSetActiveCollectionID,
|
||||
hiddenCollectionSummaries,
|
||||
showPeopleSectionButton,
|
||||
people: galleryPeopleState?.people ?? [],
|
||||
@@ -1284,7 +1264,9 @@ export default function Gallery() {
|
||||
setFilesDownloadProgressAttributesCreator
|
||||
}
|
||||
selectable={true}
|
||||
onSelectPerson={handleSelectFileInfoPerson}
|
||||
onSelectPerson={(personID) => {
|
||||
dispatch({ type: "showPerson", personID });
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{selected.count > 0 &&
|
||||
|
||||
@@ -9,6 +9,10 @@ import {
|
||||
} from "@/media/file-metadata";
|
||||
import { FileType } from "@/media/file-type";
|
||||
import { decodeLivePhoto } from "@/media/live-photo";
|
||||
import {
|
||||
createCollectionNameByID,
|
||||
getCollectionUserFacingName,
|
||||
} from "@/new/photos/services/collection";
|
||||
import downloadManager from "@/new/photos/services/download";
|
||||
import { updateExifIfNeededAndPossible } from "@/new/photos/services/exif-update";
|
||||
import {
|
||||
@@ -34,10 +38,6 @@ import {
|
||||
ExportUIUpdaters,
|
||||
FileExportNames,
|
||||
} from "types/export";
|
||||
import {
|
||||
constructCollectionNameMap,
|
||||
getCollectionUserFacingName,
|
||||
} from "utils/collection";
|
||||
import { getAllLocalCollections } from "../collectionService";
|
||||
import { migrateExport } from "./migration";
|
||||
|
||||
@@ -330,7 +330,7 @@ class ExportService {
|
||||
convertCollectionIDExportNameObjectToMap(
|
||||
exportRecord.collectionExportNames,
|
||||
);
|
||||
const collectionIDNameMap = constructCollectionNameMap(collections);
|
||||
const collectionIDNameMap = createCollectionNameByID(collections);
|
||||
|
||||
const renamedCollections = getRenamedExportedCollections(
|
||||
collections,
|
||||
|
||||
@@ -11,8 +11,8 @@ import {
|
||||
import { EnteFile } from "@/media/file";
|
||||
import { ItemVisibility } from "@/media/file-metadata";
|
||||
import {
|
||||
DEFAULT_HIDDEN_COLLECTION_USER_FACING_NAME,
|
||||
findDefaultHiddenCollectionIDs,
|
||||
isDefaultHiddenCollection,
|
||||
isHiddenCollection,
|
||||
isIncomingShare,
|
||||
} from "@/new/photos/services/collection";
|
||||
@@ -391,26 +391,6 @@ export function getHiddenCollections(collections: Collection[]): Collection[] {
|
||||
return collections.filter((collection) => isHiddenCollection(collection));
|
||||
}
|
||||
|
||||
export function constructCollectionNameMap(
|
||||
collections: Collection[],
|
||||
): Map<number, string> {
|
||||
return new Map<number, string>(
|
||||
(collections ?? []).map((collection) => [
|
||||
collection.id,
|
||||
getCollectionUserFacingName(collection),
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
const DEFAULT_HIDDEN_COLLECTION_USER_FACING_NAME = "Hidden";
|
||||
|
||||
export const getCollectionUserFacingName = (collection: Collection) => {
|
||||
if (isDefaultHiddenCollection(collection)) {
|
||||
return DEFAULT_HIDDEN_COLLECTION_USER_FACING_NAME;
|
||||
}
|
||||
return collection.name;
|
||||
};
|
||||
|
||||
export const getOrCreateAlbum = async (
|
||||
albumName: string,
|
||||
existingCollections: Collection[],
|
||||
|
||||
@@ -517,17 +517,6 @@ export function getIDBasedSortedFiles(files: EnteFile[]) {
|
||||
return files.sort((a, b) => a.id - b.id);
|
||||
}
|
||||
|
||||
export function constructFileToCollectionMap(files: EnteFile[]) {
|
||||
const fileToCollectionsMap = new Map<number, number[]>();
|
||||
(files ?? []).forEach((file) => {
|
||||
if (!fileToCollectionsMap.get(file.id)) {
|
||||
fileToCollectionsMap.set(file.id, []);
|
||||
}
|
||||
fileToCollectionsMap.get(file.id).push(file.collectionID);
|
||||
});
|
||||
return fileToCollectionsMap;
|
||||
}
|
||||
|
||||
export const shouldShowAvatar = (file: EnteFile, user: User) => {
|
||||
if (!file || !user) {
|
||||
return false;
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { LivePhotoSourceURL, SourceURLs } from "@/media/file";
|
||||
import { EnteFile } from "@/media/file";
|
||||
import { FileType } from "@/media/file-type";
|
||||
import type { SelectionContext } from "@/new/photos/components/gallery";
|
||||
import type { GalleryBarMode } from "@/new/photos/components/gallery/BarImpl";
|
||||
import type { GalleryBarMode } from "@/new/photos/components/gallery/reducer";
|
||||
import { ensure } from "@/utils/ensure";
|
||||
import { SetSelectedState } from "types/gallery";
|
||||
|
||||
|
||||
@@ -42,11 +42,7 @@ import {
|
||||
type ListChildComponentProps,
|
||||
areEqual,
|
||||
} from "react-window";
|
||||
|
||||
/**
|
||||
* Specifies what the bar is displaying currently.
|
||||
*/
|
||||
export type GalleryBarMode = "albums" | "hidden-albums" | "people";
|
||||
import type { GalleryBarMode } from "./reducer";
|
||||
|
||||
export interface GalleryBarImplProps {
|
||||
/**
|
||||
|
||||
@@ -5,7 +5,10 @@ import {
|
||||
} from "@/media/collection";
|
||||
import type { EnteFile } from "@/media/file";
|
||||
import { mergeMetadata } from "@/media/file";
|
||||
import { isHiddenCollection } from "@/new/photos/services/collection";
|
||||
import {
|
||||
createCollectionNameByID,
|
||||
isHiddenCollection,
|
||||
} from "@/new/photos/services/collection";
|
||||
import { splitByPredicate } from "@/utils/array";
|
||||
import { ensure } from "@/utils/ensure";
|
||||
import type { User } from "@ente/shared/user/types";
|
||||
@@ -26,6 +29,7 @@ import type {
|
||||
CollectionSummaryType,
|
||||
} from "../../services/collection/ui";
|
||||
import {
|
||||
createFileCollectionIDs,
|
||||
getLatestVersionFiles,
|
||||
groupFilesByCollectionID,
|
||||
} from "../../services/file";
|
||||
@@ -38,6 +42,25 @@ import {
|
||||
import type { Person } from "../../services/ml/people";
|
||||
import type { FamilyData } from "../../services/user";
|
||||
|
||||
/**
|
||||
* Specifies what the bar at the top of the gallery is displaying currently.
|
||||
*/
|
||||
export type GalleryBarMode = "albums" | "hidden-albums" | "people";
|
||||
|
||||
/**
|
||||
* Specifies what the gallery is currently displaying.
|
||||
*
|
||||
* TODO: An experiment at consolidating state.
|
||||
*/
|
||||
export type GalleryFocus =
|
||||
| {
|
||||
type: "albums" | "hidden-albums";
|
||||
activeCollectionID: number;
|
||||
activeCollection: Collection | undefined;
|
||||
activeCollectionSummary: CollectionSummary;
|
||||
}
|
||||
| { type: "people"; activePersonID: string; activePerson: Person };
|
||||
|
||||
/**
|
||||
* Derived UI state backing the gallery.
|
||||
*
|
||||
@@ -109,6 +132,17 @@ export interface GalleryState {
|
||||
* File IDs of all the files that the user has marked as a favorite.
|
||||
*/
|
||||
favoriteFileIDs: Set<number>;
|
||||
/**
|
||||
* User visible collection names indexed by collection IDs for fast lookup.
|
||||
*
|
||||
* This map will contain entries for all (both normal and hidden)
|
||||
* collections.
|
||||
*/
|
||||
allCollectionNameByID: Map<number, string>;
|
||||
/**
|
||||
* A list of collection IDs to which a file belongs, indexed by file ID.
|
||||
*/
|
||||
fileCollectionIDs: Map<number, number[]>;
|
||||
|
||||
/*--< Derived UI state >--*/
|
||||
|
||||
@@ -124,6 +158,18 @@ export interface GalleryState {
|
||||
|
||||
/*--< Transient UI state >--*/
|
||||
|
||||
/**
|
||||
* If visible, what should the (sticky) gallery bar show.
|
||||
*/
|
||||
barMode: GalleryBarMode | undefined;
|
||||
/**
|
||||
* The section / area, and the item within it, that the gallery is currently
|
||||
* showing.
|
||||
*/
|
||||
focus: GalleryFocus | undefined;
|
||||
activeCollectionID: number | undefined;
|
||||
activePersonID: string | undefined;
|
||||
|
||||
filteredData: EnteFile[];
|
||||
/**
|
||||
* The currently selected person, if any.
|
||||
@@ -136,6 +182,28 @@ export interface GalleryState {
|
||||
* The list of people to show.
|
||||
*/
|
||||
people: Person[] | undefined;
|
||||
/**
|
||||
* `true` if we are in "search mode".
|
||||
*
|
||||
* We will always be in search mode if we are showing search results, but we
|
||||
* also may be in search mode earlier on smaller screens, where the search
|
||||
* input is only shown on entering search mode. See: [Note: "Search mode"].
|
||||
*
|
||||
* That is, {@link isInSearchMode} may be true even when
|
||||
* {@link searchResults} is undefined.
|
||||
*/
|
||||
isInSearchMode: boolean;
|
||||
/**
|
||||
* List of files that match the selected search option.
|
||||
*
|
||||
* This will be set only if we are showing search results.
|
||||
*
|
||||
* The search dropdown shows a list of options ("suggestions") that match
|
||||
* the user's search term. If the user selects from one of these options,
|
||||
* then we run a search to find all files that match that suggestion, and
|
||||
* set this value to the result.
|
||||
*/
|
||||
searchResults: EnteFile[] | undefined;
|
||||
}
|
||||
|
||||
export type GalleryAction =
|
||||
@@ -169,7 +237,18 @@ export type GalleryAction =
|
||||
| { type: "uploadFile"; file: EnteFile }
|
||||
| { type: "resetHiddenFiles"; hiddenFiles: EnteFile[] }
|
||||
| { type: "fetchHiddenFiles"; hiddenFiles: EnteFile[] }
|
||||
| { type: "setTrashedFiles"; trashedFiles: EnteFile[] };
|
||||
| { type: "setTrashedFiles"; trashedFiles: EnteFile[] }
|
||||
| { type: "showAll" }
|
||||
| { type: "showHidden" }
|
||||
| {
|
||||
type: "showNormalOrHiddenCollectionSummary";
|
||||
collectionSummaryID: number | undefined;
|
||||
}
|
||||
| { type: "showPeople" }
|
||||
| { type: "showPerson"; personID: string }
|
||||
| { type: "searchResults"; searchResults: EnteFile[] }
|
||||
| { type: "enterSearchMode" }
|
||||
| { type: "exitSearch" };
|
||||
|
||||
const initialGalleryState: GalleryState = {
|
||||
user: undefined,
|
||||
@@ -183,11 +262,19 @@ const initialGalleryState: GalleryState = {
|
||||
defaultHiddenCollectionIDs: new Set(),
|
||||
hiddenFileIDs: new Set(),
|
||||
favoriteFileIDs: new Set(),
|
||||
allCollectionNameByID: new Map(),
|
||||
fileCollectionIDs: new Map(),
|
||||
collectionSummaries: new Map(),
|
||||
hiddenCollectionSummaries: new Map(),
|
||||
barMode: undefined,
|
||||
focus: undefined,
|
||||
activeCollectionID: undefined,
|
||||
activePersonID: undefined,
|
||||
filteredData: [],
|
||||
activePerson: undefined,
|
||||
people: [],
|
||||
isInSearchMode: false,
|
||||
searchResults: undefined,
|
||||
};
|
||||
|
||||
const galleryReducer: React.Reducer<GalleryState, GalleryAction> = (
|
||||
@@ -222,6 +309,10 @@ const galleryReducer: React.Reducer<GalleryState, GalleryAction> = (
|
||||
collections,
|
||||
action.files,
|
||||
),
|
||||
allCollectionNameByID: createCollectionNameByID(
|
||||
action.allCollections,
|
||||
),
|
||||
fileCollectionIDs: createFileCollectionIDs(action.files),
|
||||
collectionSummaries: deriveCollectionSummaries(
|
||||
action.user,
|
||||
collections,
|
||||
@@ -255,6 +346,9 @@ const galleryReducer: React.Reducer<GalleryState, GalleryAction> = (
|
||||
action.collections,
|
||||
state.files,
|
||||
),
|
||||
allCollectionNameByID: createCollectionNameByID(
|
||||
action.collections.concat(state.hiddenCollections),
|
||||
),
|
||||
collectionSummaries: deriveCollectionSummaries(
|
||||
ensure(state.user),
|
||||
action.collections,
|
||||
@@ -280,6 +374,9 @@ const galleryReducer: React.Reducer<GalleryState, GalleryAction> = (
|
||||
action.collections,
|
||||
state.files,
|
||||
),
|
||||
allCollectionNameByID: createCollectionNameByID(
|
||||
action.collections.concat(action.hiddenCollections),
|
||||
),
|
||||
collectionSummaries: deriveCollectionSummaries(
|
||||
ensure(state.user),
|
||||
action.collections,
|
||||
@@ -303,6 +400,7 @@ const galleryReducer: React.Reducer<GalleryState, GalleryAction> = (
|
||||
state.collections,
|
||||
files,
|
||||
),
|
||||
fileCollectionIDs: createFileCollectionIDs(action.files),
|
||||
collectionSummaries: deriveCollectionSummaries(
|
||||
ensure(state.user),
|
||||
state.collections,
|
||||
@@ -325,6 +423,7 @@ const galleryReducer: React.Reducer<GalleryState, GalleryAction> = (
|
||||
state.collections,
|
||||
files,
|
||||
),
|
||||
fileCollectionIDs: createFileCollectionIDs(action.files),
|
||||
collectionSummaries: deriveCollectionSummaries(
|
||||
ensure(state.user),
|
||||
state.collections,
|
||||
@@ -343,6 +442,7 @@ const galleryReducer: React.Reducer<GalleryState, GalleryAction> = (
|
||||
state.collections,
|
||||
files,
|
||||
),
|
||||
fileCollectionIDs: createFileCollectionIDs(files),
|
||||
// TODO: Consider batching this instead of doing it per file
|
||||
// upload to speed up uploads. Perf test first though.
|
||||
collectionSummaries: deriveCollectionSummaries(
|
||||
@@ -399,6 +499,65 @@ const galleryReducer: React.Reducer<GalleryState, GalleryAction> = (
|
||||
state.archivedCollectionIDs,
|
||||
),
|
||||
};
|
||||
case "showAll":
|
||||
return {
|
||||
...state,
|
||||
barMode: "albums",
|
||||
activeCollectionID: ALL_SECTION,
|
||||
isInSearchMode: false,
|
||||
searchResults: undefined,
|
||||
};
|
||||
case "showHidden":
|
||||
return {
|
||||
...state,
|
||||
barMode: "hidden-albums",
|
||||
activeCollectionID: HIDDEN_ITEMS_SECTION,
|
||||
isInSearchMode: false,
|
||||
searchResults: undefined,
|
||||
};
|
||||
case "showNormalOrHiddenCollectionSummary":
|
||||
return {
|
||||
...state,
|
||||
barMode:
|
||||
action.collectionSummaryID !== undefined &&
|
||||
state.hiddenCollectionSummaries.has(
|
||||
action.collectionSummaryID,
|
||||
)
|
||||
? "hidden-albums"
|
||||
: "albums",
|
||||
activeCollectionID: action.collectionSummaryID ?? ALL_SECTION,
|
||||
isInSearchMode: false,
|
||||
searchResults: undefined,
|
||||
};
|
||||
case "showPeople":
|
||||
return {
|
||||
...state,
|
||||
barMode: "people",
|
||||
activePersonID: undefined,
|
||||
isInSearchMode: false,
|
||||
searchResults: undefined,
|
||||
};
|
||||
case "showPerson":
|
||||
return {
|
||||
...state,
|
||||
barMode: "people",
|
||||
activePersonID: action.personID,
|
||||
isInSearchMode: false,
|
||||
searchResults: undefined,
|
||||
};
|
||||
case "enterSearchMode":
|
||||
return { ...state, isInSearchMode: true };
|
||||
case "searchResults":
|
||||
return {
|
||||
...state,
|
||||
searchResults: action.searchResults,
|
||||
};
|
||||
case "exitSearch":
|
||||
return {
|
||||
...state,
|
||||
isInSearchMode: false,
|
||||
searchResults: undefined,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -41,3 +41,30 @@ export const isHiddenCollection = (collection: Collection) =>
|
||||
// TODO: Need to audit the types
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
collection.magicMetadata?.data.visibility === ItemVisibility.hidden;
|
||||
|
||||
// TODO: Does this need localizations?
|
||||
export const DEFAULT_HIDDEN_COLLECTION_USER_FACING_NAME = "Hidden";
|
||||
|
||||
/**
|
||||
* Return the "user facing" name of the given collection.
|
||||
*
|
||||
* Usually this is the same as the collection name, but it might be a different
|
||||
* string for special collections like default hidden collections.
|
||||
*/
|
||||
export const getCollectionUserFacingName = (collection: Collection) => {
|
||||
if (isDefaultHiddenCollection(collection)) {
|
||||
return DEFAULT_HIDDEN_COLLECTION_USER_FACING_NAME;
|
||||
}
|
||||
return collection.name;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return a map of the (user-facing) collection name, indexed by collection ID.
|
||||
*/
|
||||
export const createCollectionNameByID = (allCollections: Collection[]) =>
|
||||
new Map<number, string>(
|
||||
allCollections.map((collection) => [
|
||||
collection.id,
|
||||
getCollectionUserFacingName(collection),
|
||||
]),
|
||||
);
|
||||
|
||||
@@ -14,6 +14,19 @@ export const groupFilesByCollectionID = (files: EnteFile[]) =>
|
||||
return result;
|
||||
}, new Map<number, EnteFile[]>());
|
||||
|
||||
/**
|
||||
* Construct a map from file IDs to the list of collections (IDs) to which the
|
||||
* file belongs.
|
||||
*/
|
||||
export const createFileCollectionIDs = (files: EnteFile[]) =>
|
||||
files.reduce((result, file) => {
|
||||
const id = file.id;
|
||||
let fs = result.get(id);
|
||||
if (!fs) result.set(id, (fs = []));
|
||||
fs.push(file.collectionID);
|
||||
return result;
|
||||
}, new Map<number, number[]>());
|
||||
|
||||
export function getLatestVersionFiles(files: EnteFile[]) {
|
||||
const latestVersionFiles = new Map<string, EnteFile>();
|
||||
files.forEach((file) => {
|
||||
|
||||
Reference in New Issue
Block a user