Added selectAll checkbox to select all files on a day

Signed-off-by: aakankshabhende <aakanksha0407@gmail.com>
This commit is contained in:
aakankshabhende 2024-03-05 10:42:57 +05:30
parent 9a3c450d34
commit 4a579a93bb
6 changed files with 179 additions and 61 deletions

View file

@ -17,12 +17,16 @@ import DownloadManager, {
LivePhotoSourceURL,
SourceURLs,
} from "services/download";
import {
handleSelectCreator,
updateFileMsrcProps,
updateFileSrcProps,
} from "utils/photoFrame";
import { EnteFile } from "types/file";
import {
SelectedState,
SetFilesDownloadProgressAttributesCreator,
} from "types/gallery";
import { updateFileMsrcProps, updateFileSrcProps } from "utils/photoFrame";
import { PhotoList } from "./PhotoList";
import { DedupePhotoList } from "./PhotoList/dedupe";
import PreviewCard from "./pages/gallery/PreviewCard";
@ -227,52 +231,12 @@ const PhotoFrame = ({
setIsPhotoSwipeOpen?.(true);
};
const handleSelect =
(id: number, isOwnFile: boolean, index?: number) =>
(checked: boolean) => {
if (typeof index !== "undefined") {
if (checked) {
setRangeStart(index);
} else {
setRangeStart(undefined);
}
}
setSelected((selected) => {
if (selected.collectionID !== activeCollectionID) {
selected = { ownCount: 0, count: 0, collectionID: 0 };
}
const handleSelect = handleSelectCreator(
setSelected,
activeCollectionID,
setRangeStart
);
const handleCounterChange = (count: number) => {
if (selected[id] === checked) {
return count;
}
if (checked) {
return count + 1;
} else {
return count - 1;
}
};
const handleAllCounterChange = () => {
if (isOwnFile) {
return {
ownCount: handleCounterChange(selected.ownCount),
count: handleCounterChange(selected.count),
};
} else {
return {
count: handleCounterChange(selected.count),
};
}
};
return {
...selected,
[id]: checked,
collectionID: activeCollectionID,
...handleAllCounterChange(),
};
});
};
const onHoverOver = (index: number) => () => {
setCurrentHover(index);
};

View file

@ -1,8 +1,7 @@
import { FlexWrapper } from "@ente/shared/components/Container";
import { ENTE_WEBSITE_LINK } from "@ente/shared/constants/urls";
import { formatDate } from "@ente/shared/time/format";
import { convertBytesToHumanReadable } from "@ente/shared/utils/size";
import { Box, Link, Typography, styled } from "@mui/material";
import { Box, Link, Typography,Checkbox, styled } from "@mui/material";
import {
DATE_CONTAINER_HEIGHT,
GAP_BTW_TILES,
@ -23,8 +22,10 @@ import {
ListChildComponentProps,
areEqual,
} from "react-window";
import { EnteFile } from "types/file";
import { PublicCollectionGalleryContext } from "utils/publicCollectionGallery";
import { formatDate, getDate, isSameDay } from "@ente/shared/time/format";
import { handleSelectCreator } from "utils/photoFrame";
import { EnteFile } from "types/file";
const A_DAY = 24 * 60 * 60 * 1000;
const FOOTER_HEIGHT = 90;
@ -185,6 +186,9 @@ const NothingContainer = styled(ListItemContainer)`
justify-content: center;
`;
const SelectAllCheckBoxContainer = styled(Checkbox)<{ margin: number }>`
margin-left: ${(props) => props.margin}px;
`;
interface Props {
height: number;
width: number;
@ -265,6 +269,8 @@ export function PhotoList({
const shouldRefresh = useRef(false);
const listRef = useRef(null);
const [checkedDates, setCheckedDates] = useState({});
const fittableColumns = getFractionFittableColumns(width);
let columns = Math.floor(fittableColumns);
@ -473,14 +479,6 @@ export function PhotoList({
});
};
const isSameDay = (first, second) => {
return (
first.getFullYear() === second.getFullYear() &&
first.getMonth() === second.getMonth() &&
first.getDate() === second.getDate()
);
};
const getPhotoListHeader = (photoListHeader) => {
return {
...photoListHeader,
@ -722,6 +720,62 @@ export function PhotoList({
}
};
useEffect(() => {
const notSelectedFiles = displayFiles?.filter(
(item) => !galleryContext.selectedFile[item.id]
);
const unselectedDates = [
...new Set(notSelectedFiles?.map((item) => getDate(item))), // to get file's date which were manually unselected
];
const localSelectedFiles = displayFiles.filter(
// to get files which were manually selected
(item) => !unselectedDates.includes(getDate(item))
);
const localSelectedDates = [
...new Set(localSelectedFiles?.map((item) => getDate(item))),
]; // to get file's date which were manually selected
unselectedDates.forEach((date) => {
setCheckedDates((prev) => ({
...prev,
[date]: false,
})); // To uncheck select all checkbox if any of the file on the date is unselected
});
localSelectedDates.map((date) => {
setCheckedDates((prev) => ({
...prev,
[date]: true,
}));
// To check select all checkbox if all of the files on the date is selected manually
});
}, [galleryContext.selectedFile]);
const handleSelect = handleSelectCreator(
galleryContext.setSelectedFiles,
activeCollectionID
);
const onChangeSelectAllCheckBox = (date: string) => {
const dates = { ...checkedDates, [date]: !checkedDates[date] };
const isDateSelected = !checkedDates[date];
setCheckedDates(dates);
const filesOnADay = displayFiles?.filter(
(item) => getDate(item) === date
); // all files on a checked/unchecked day
filesOnADay.forEach((file) => {
handleSelect(
file.id,
file.ownerID === galleryContext?.user?.id
)(isDateSelected);
});
};
const renderListItem = (
listItem: TimeStampListItem,
isScrolling: boolean,
@ -733,6 +787,15 @@ export function PhotoList({
.map((item) => [
<DateContainer key={item.date} span={item.span}>
{item.date}
<SelectAllCheckBoxContainer
key={item.date}
name={item.date}
checked={checkedDates[item.date]}
onChange={() =>
onChangeSelectAllCheckBox(item.date)
}
margin={columns}
/>
</DateContainer>,
<div key={`${item.date}-gap`} />,
])
@ -740,6 +803,15 @@ export function PhotoList({
) : (
<DateContainer span={columns}>
{listItem.date}
<SelectAllCheckBoxContainer
key={listItem.date}
name={listItem.date}
checked={checkedDates[listItem.date]}
onChange={() =>
onChangeSelectAllCheckBox(listItem.date)
}
margin={columns}
/>
</DateContainer>
);
case ITEM_TYPE.SIZE_AND_COUNT:

View file

@ -163,6 +163,8 @@ const defaultGalleryContext: GalleryContextType = {
emailList: null,
openHiddenSection: () => null,
isClipSearchResult: null,
selectedFile: null,
setSelectedFiles: () => null,
};
export const GalleryContext = createContext<GalleryContextType>(
@ -1013,8 +1015,9 @@ export default function Gallery() {
emailList,
openHiddenSection,
isClipSearchResult,
}}
>
selectedFile: selected,
setSelectedFiles: setSelected,
}}>
<FullScreenDropZone
getDragAndDropRootProps={getDragAndDropRootProps}
>

View file

@ -11,6 +11,9 @@ export type SelectedState = {
count: number;
collectionID: number;
};
export type SetSelectedState = React.Dispatch<
React.SetStateAction<SelectedState>
>;
export type SetFiles = React.Dispatch<React.SetStateAction<EnteFile[]>>;
export type SetCollections = React.Dispatch<React.SetStateAction<Collection[]>>;
export type SetLoading = React.Dispatch<React.SetStateAction<boolean>>;
@ -54,6 +57,8 @@ export type GalleryContextType = {
emailList: string[];
openHiddenSection: (callback?: () => void) => void;
isClipSearchResult: boolean;
setSelectedFiles: (value) => void;
selectedFile: SelectedState;
};
export enum CollectionSelectorIntent {

View file

@ -1,7 +1,8 @@
import { logError } from "@ente/shared/sentry";
import { FILE_TYPE } from "constants/file";
import { LivePhotoSourceURL, SourceURLs } from "services/download";
import { EnteFile } from "types/file";
import { logError } from "@ente/shared/sentry";
import { LivePhotoSourceURL, SourceURLs } from "services/download";
import { SetSelectedState } from "types/gallery";
const WAIT_FOR_VIDEO_PLAYBACK = 1 * 1000;
@ -129,3 +130,55 @@ export async function updateFileSrcProps(
file.src = url as string;
}
}
export const handleSelectCreator =
(
setSelected: SetSelectedState,
activeCollectionID: number,
setRangeStart?
) =>
(id: number, isOwnFile: boolean, index?: number) =>
(checked: boolean) => {
if (typeof index !== 'undefined') {
if (checked) {
setRangeStart(index);
} else {
setRangeStart(undefined);
}
}
setSelected((selected) => {
if (selected.collectionID !== activeCollectionID) {
selected = { ownCount: 0, count: 0, collectionID: 0 };
}
const handleCounterChange = (count: number) => {
if (selected[id] === checked) {
return count;
}
if (checked) {
return count + 1;
} else {
return count - 1;
}
};
const handleAllCounterChange = () => {
if (isOwnFile) {
return {
ownCount: handleCounterChange(selected.ownCount),
count: handleCounterChange(selected.count),
};
} else {
return {
count: handleCounterChange(selected.count),
};
}
};
return {
...selected,
[id]: checked,
collectionID: activeCollectionID,
...handleAllCounterChange(),
};
});
};

View file

@ -1,5 +1,7 @@
import i18n, { t } from "i18next";
const A_DAY = 24 * 60 * 60 * 1000;
const dateTimeFullFormatter1 = new Intl.DateTimeFormat(i18n.language, {
weekday: "short",
month: "short",
@ -76,3 +78,22 @@ export function formatDateRelative(date: number) {
u as Intl.RelativeTimeFormatUnit,
);
}
export const isSameDay = (first, second) => {
return (
first.getFullYear() === second.getFullYear() &&
first.getMonth() === second.getMonth() &&
first.getDate() === second.getDate()
);
};
export const getDate = (item) => {
const currentDate = item.metadata.creationTime / 1000;
const date = isSameDay(new Date(currentDate), new Date())
? t('TODAY')
: isSameDay(new Date(currentDate), new Date(Date.now() - A_DAY))
? t('YESTERDAY')
: formatDate(currentDate);
return date;
};