[web] Introduce selection context
- For handling collection / people split - This can be done better (much!), need to revisit
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import log from "@/base/log";
|
||||
import { FileType } from "@/media/file-type";
|
||||
import type { GalleryBarMode } from "@/new/photos/components/Gallery/BarImpl";
|
||||
import DownloadManager from "@/new/photos/services/download";
|
||||
import type { LivePhotoSourceURL, SourceURLs } from "@/new/photos/types/file";
|
||||
import { EnteFile } from "@/new/photos/types/file";
|
||||
@@ -48,6 +49,7 @@ interface Props {
|
||||
| PHOTOS_PAGES.GALLERY
|
||||
| PHOTOS_PAGES.DEDUPLICATE
|
||||
| PHOTOS_PAGES.SHARED_ALBUMS;
|
||||
mode?: GalleryBarMode;
|
||||
files: EnteFile[];
|
||||
duplicates?: Duplicate[];
|
||||
syncWithRemote: () => Promise<void>;
|
||||
@@ -58,7 +60,10 @@ interface Props {
|
||||
selected: SelectedState;
|
||||
tempDeletedFileIds?: Set<number>;
|
||||
setTempDeletedFileIds?: (value: Set<number>) => void;
|
||||
/** This will be set if mode is not "people". */
|
||||
activeCollectionID: number;
|
||||
/** This will be set if mode is "people". */
|
||||
activePersonID?: string | undefined;
|
||||
enableDownload?: boolean;
|
||||
fileToCollectionsMap: Map<number, number[]>;
|
||||
collectionNameMap: Map<number, string>;
|
||||
@@ -72,6 +77,7 @@ interface Props {
|
||||
const PhotoFrame = ({
|
||||
page,
|
||||
duplicates,
|
||||
mode,
|
||||
files,
|
||||
syncWithRemote,
|
||||
favItemIds,
|
||||
@@ -80,6 +86,7 @@ const PhotoFrame = ({
|
||||
tempDeletedFileIds,
|
||||
setTempDeletedFileIds,
|
||||
activeCollectionID,
|
||||
activePersonID,
|
||||
enableDownload,
|
||||
fileToCollectionsMap,
|
||||
collectionNameMap,
|
||||
@@ -232,7 +239,9 @@ const PhotoFrame = ({
|
||||
|
||||
const handleSelect = handleSelectCreator(
|
||||
setSelected,
|
||||
mode,
|
||||
activeCollectionID,
|
||||
activePersonID,
|
||||
setRangeStart,
|
||||
);
|
||||
|
||||
@@ -287,8 +296,13 @@ const PhotoFrame = ({
|
||||
index,
|
||||
)}
|
||||
selected={
|
||||
selected.collectionID === activeCollectionID &&
|
||||
selected[item.id]
|
||||
(!mode
|
||||
? selected.collectionID === activeCollectionID
|
||||
: mode == selected.context?.mode &&
|
||||
(selected.context.mode == "people"
|
||||
? selected.context.personID == activePersonID
|
||||
: selected.context.collectionID ==
|
||||
activeCollectionID)) && selected[item.id]
|
||||
}
|
||||
selectOnClick={selected.count > 0}
|
||||
onHover={onHoverOver(index)}
|
||||
@@ -539,8 +553,10 @@ const PhotoFrame = ({
|
||||
width={width}
|
||||
height={height}
|
||||
getThumbnail={getThumbnail}
|
||||
mode={mode}
|
||||
displayFiles={displayFiles}
|
||||
activeCollectionID={activeCollectionID}
|
||||
activePersonID={activePersonID}
|
||||
showAppDownloadBanner={showAppDownloadBanner}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { GalleryBarMode } from "@/new/photos/components/Gallery/BarImpl";
|
||||
import { EnteFile } from "@/new/photos/types/file";
|
||||
import { formattedByteSize } from "@/new/photos/utils/units";
|
||||
import { FlexWrapper } from "@ente/shared/components/Container";
|
||||
@@ -189,6 +190,7 @@ const NothingContainer = styled(ListItemContainer)`
|
||||
interface Props {
|
||||
height: number;
|
||||
width: number;
|
||||
mode?: GalleryBarMode;
|
||||
displayFiles: EnteFile[];
|
||||
showAppDownloadBanner: boolean;
|
||||
getThumbnail: (
|
||||
@@ -197,6 +199,7 @@ interface Props {
|
||||
isScrolling?: boolean,
|
||||
) => JSX.Element;
|
||||
activeCollectionID: number;
|
||||
activePersonID?: string;
|
||||
}
|
||||
|
||||
interface ItemData {
|
||||
@@ -253,10 +256,12 @@ const PhotoListRow = React.memo(
|
||||
export function PhotoList({
|
||||
height,
|
||||
width,
|
||||
mode,
|
||||
displayFiles,
|
||||
showAppDownloadBanner,
|
||||
getThumbnail,
|
||||
activeCollectionID,
|
||||
activePersonID,
|
||||
}: Props) {
|
||||
const galleryContext = useContext(GalleryContext);
|
||||
const publicCollectionGalleryContext = useContext(
|
||||
@@ -768,7 +773,9 @@ export function PhotoList({
|
||||
|
||||
const handleSelect = handleSelectCreator(
|
||||
galleryContext.setSelectedFiles,
|
||||
mode,
|
||||
activeCollectionID,
|
||||
activePersonID,
|
||||
);
|
||||
|
||||
const onChangeSelectAllCheckBox = (date: string) => {
|
||||
|
||||
@@ -49,6 +49,7 @@ export default function Deduplicate() {
|
||||
count: 0,
|
||||
collectionID: 0,
|
||||
ownCount: 0,
|
||||
context: undefined,
|
||||
});
|
||||
const closeDeduplication = function () {
|
||||
Router.push(PAGES.GALLERY);
|
||||
@@ -97,6 +98,7 @@ export default function Deduplicate() {
|
||||
count: count,
|
||||
ownCount: count,
|
||||
collectionID: ALL_SECTION,
|
||||
context: undefined,
|
||||
};
|
||||
for (const fileID of toSelectFileIDs) {
|
||||
selectedFiles[fileID] = true;
|
||||
@@ -157,7 +159,12 @@ export default function Deduplicate() {
|
||||
};
|
||||
|
||||
const clearSelection = function () {
|
||||
setSelected({ count: 0, collectionID: 0, ownCount: 0 });
|
||||
setSelected({
|
||||
count: 0,
|
||||
collectionID: 0,
|
||||
ownCount: 0,
|
||||
context: undefined,
|
||||
});
|
||||
};
|
||||
|
||||
if (!duplicates) {
|
||||
|
||||
@@ -202,6 +202,7 @@ export default function Gallery() {
|
||||
ownCount: 0,
|
||||
count: 0,
|
||||
collectionID: 0,
|
||||
context: { mode: "albums", collectionID: ALL_SECTION },
|
||||
});
|
||||
const [planModalView, setPlanModalView] = useState(false);
|
||||
const [blockingLoad, setBlockingLoad] = useState(false);
|
||||
@@ -661,6 +662,13 @@ export default function Gallery() {
|
||||
ownCount: 0,
|
||||
count: 0,
|
||||
collectionID: activeCollectionID,
|
||||
context:
|
||||
barMode == "people" && activePerson
|
||||
? { mode: "people" as const, personID: activePerson.id }
|
||||
: {
|
||||
mode: "albums" as const,
|
||||
collectionID: ensure(activeCollectionID),
|
||||
},
|
||||
};
|
||||
|
||||
filteredData.forEach((item) => {
|
||||
@@ -677,7 +685,12 @@ export default function Gallery() {
|
||||
if (!selected?.count) {
|
||||
return;
|
||||
}
|
||||
setSelected({ ownCount: 0, count: 0, collectionID: 0 });
|
||||
setSelected({
|
||||
ownCount: 0,
|
||||
count: 0,
|
||||
collectionID: 0,
|
||||
context: undefined,
|
||||
});
|
||||
};
|
||||
|
||||
const keyboardShortcutHandlerRef = useRef({
|
||||
@@ -1207,6 +1220,7 @@ export default function Gallery() {
|
||||
) : (
|
||||
<PhotoFrame
|
||||
page={PAGES.GALLERY}
|
||||
mode={barMode}
|
||||
files={filteredData}
|
||||
syncWithRemote={syncWithRemote}
|
||||
favItemIds={favItemIds}
|
||||
@@ -1216,6 +1230,7 @@ export default function Gallery() {
|
||||
setTempDeletedFileIds={setTempDeletedFileIds}
|
||||
setIsPhotoSwipeOpen={setIsPhotoSwipeOpen}
|
||||
activeCollectionID={activeCollectionID}
|
||||
activePersonID={activePerson?.id}
|
||||
enableDownload={true}
|
||||
fileToCollectionsMap={fileToCollectionsMap}
|
||||
collectionNameMap={collectionNameMap}
|
||||
|
||||
@@ -109,6 +109,7 @@ export default function PublicCollectionGallery() {
|
||||
ownCount: 0,
|
||||
count: 0,
|
||||
collectionID: 0,
|
||||
context: undefined,
|
||||
});
|
||||
|
||||
const {
|
||||
@@ -472,7 +473,12 @@ export default function PublicCollectionGallery() {
|
||||
if (!selected?.count) {
|
||||
return;
|
||||
}
|
||||
setSelected({ ownCount: 0, count: 0, collectionID: 0 });
|
||||
setSelected({
|
||||
ownCount: 0,
|
||||
count: 0,
|
||||
collectionID: 0,
|
||||
context: undefined,
|
||||
});
|
||||
};
|
||||
|
||||
const downloadFilesHelper = async () => {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { Collection } from "@/media/collection";
|
||||
import { type SelectionContext } from "@/new/photos/components/Gallery";
|
||||
import { EnteFile } from "@/new/photos/types/file";
|
||||
import type { User } from "@ente/shared/user/types";
|
||||
import { CollectionSelectorAttributes } from "components/Collections/CollectionSelector";
|
||||
@@ -10,6 +11,12 @@ export type SelectedState = {
|
||||
ownCount: number;
|
||||
count: number;
|
||||
collectionID: number;
|
||||
/**
|
||||
* The context in which the selection was made. Only set by newer code if
|
||||
* there is an active selection (older code continues to rely on the
|
||||
* {@link collectionID} logic).
|
||||
*/
|
||||
context: SelectionContext | undefined;
|
||||
};
|
||||
export type SetSelectedState = React.Dispatch<
|
||||
React.SetStateAction<SelectedState>
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import log from "@/base/log";
|
||||
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 { LivePhotoSourceURL, SourceURLs } from "@/new/photos/types/file";
|
||||
import { EnteFile } from "@/new/photos/types/file";
|
||||
import { ensure } from "@/utils/ensure";
|
||||
import { SetSelectedState } from "types/gallery";
|
||||
|
||||
export async function playVideo(livePhotoVideo, livePhotoImage) {
|
||||
@@ -102,7 +105,9 @@ export async function updateFileSrcProps(
|
||||
export const handleSelectCreator =
|
||||
(
|
||||
setSelected: SetSelectedState,
|
||||
mode: GalleryBarMode | undefined,
|
||||
activeCollectionID: number,
|
||||
activePersonID: string | undefined,
|
||||
setRangeStart?,
|
||||
) =>
|
||||
(id: number, isOwnFile: boolean, index?: number) =>
|
||||
@@ -115,10 +120,84 @@ export const handleSelectCreator =
|
||||
}
|
||||
}
|
||||
setSelected((selected) => {
|
||||
if (selected.collectionID !== activeCollectionID) {
|
||||
selected = { ownCount: 0, count: 0, collectionID: 0 };
|
||||
if (!mode) {
|
||||
// Retain older behavior for non-gallery call sites.
|
||||
if (selected.collectionID !== activeCollectionID) {
|
||||
selected = {
|
||||
ownCount: 0,
|
||||
count: 0,
|
||||
collectionID: 0,
|
||||
context: undefined,
|
||||
};
|
||||
}
|
||||
} else if (!selected.context) {
|
||||
// Gallery will specify a mode, but a fresh selection starts off
|
||||
// without a context, so fill it in with the current context.
|
||||
selected = {
|
||||
...selected,
|
||||
context:
|
||||
mode == "people"
|
||||
? { mode, personID: ensure(activePersonID) }
|
||||
: {
|
||||
mode,
|
||||
collectionID: ensure(activeCollectionID),
|
||||
},
|
||||
};
|
||||
} else {
|
||||
// Both mode and context are defined.
|
||||
if (selected.context.mode != mode) {
|
||||
// Clear selection if mode has changed.
|
||||
selected = {
|
||||
ownCount: 0,
|
||||
count: 0,
|
||||
collectionID: 0,
|
||||
context:
|
||||
mode == "people"
|
||||
? { mode, personID: ensure(activePersonID) }
|
||||
: {
|
||||
mode,
|
||||
collectionID: ensure(activeCollectionID),
|
||||
},
|
||||
};
|
||||
} else {
|
||||
if (selected.context?.mode == "people") {
|
||||
if (selected.context.personID != activePersonID) {
|
||||
// Clear selection if person has changed.
|
||||
selected = {
|
||||
ownCount: 0,
|
||||
count: 0,
|
||||
collectionID: 0,
|
||||
context: {
|
||||
mode: selected.context?.mode,
|
||||
personID: ensure(activePersonID),
|
||||
},
|
||||
};
|
||||
}
|
||||
} else {
|
||||
if (
|
||||
selected.context.collectionID != activeCollectionID
|
||||
) {
|
||||
// Clear selection if collection has changed.
|
||||
selected = {
|
||||
ownCount: 0,
|
||||
count: 0,
|
||||
collectionID: 0,
|
||||
context: {
|
||||
mode: selected.context?.mode,
|
||||
collectionID: ensure(activeCollectionID),
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const newContext: SelectionContext | undefined = !mode
|
||||
? undefined
|
||||
: mode == "people"
|
||||
? { mode, personID: ensure(activePersonID) }
|
||||
: { mode, collectionID: ensure(activeCollectionID) };
|
||||
|
||||
const handleCounterChange = (count: number) => {
|
||||
if (selected[id] === checked) {
|
||||
return count;
|
||||
@@ -146,6 +225,7 @@ export const handleSelectCreator =
|
||||
...selected,
|
||||
[id]: checked,
|
||||
collectionID: activeCollectionID,
|
||||
context: newContext,
|
||||
...handleAllCounterChange(),
|
||||
};
|
||||
});
|
||||
|
||||
@@ -19,6 +19,16 @@ import React from "react";
|
||||
import { SpaceBetweenFlex } from "../mui-custom";
|
||||
import { GalleryItemsHeaderAdapter, GalleryItemsSummary } from "./ListHeader";
|
||||
|
||||
/**
|
||||
* The context in which a selection was made.
|
||||
*
|
||||
* This allows us to reset the selection if user moves to a different context
|
||||
* and starts a new selection.
|
||||
* */
|
||||
export type SelectionContext =
|
||||
| { mode: "albums" | "hidden-albums"; collectionID: number }
|
||||
| { mode: "people"; personID: string };
|
||||
|
||||
interface SearchResultsHeaderProps {
|
||||
selectedOption: SearchOption;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user