move deduplicate logic to seperate page
This commit is contained in:
parent
3210474a2f
commit
6622016df4
|
@ -1,20 +0,0 @@
|
|||
import React from 'react';
|
||||
import { IconButton } from './Container';
|
||||
import LeftArrow from './icons/LeftArrow';
|
||||
|
||||
export default function BackButton({ setIsDeduplicating }) {
|
||||
return (
|
||||
<IconButton
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: '1em',
|
||||
left: '1em',
|
||||
zIndex: 10,
|
||||
}}
|
||||
onClick={() => {
|
||||
setIsDeduplicating(false);
|
||||
}}>
|
||||
<LeftArrow />
|
||||
</IconButton>
|
||||
);
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
import { GalleryContext } from 'pages/gallery';
|
||||
import React, { useContext } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import constants from 'utils/strings/constants';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
position: fixed;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
top: 0;
|
||||
z-index: 1002;
|
||||
min-height: 64px;
|
||||
right: 64px;
|
||||
`;
|
||||
|
||||
export default function ClubDuplicateFilesByTime() {
|
||||
const galleryContext = useContext(GalleryContext);
|
||||
return (
|
||||
<Wrapper>
|
||||
<input
|
||||
type="checkbox"
|
||||
style={{
|
||||
width: '1em',
|
||||
height: '1em',
|
||||
}}
|
||||
value={galleryContext.clubSameTimeFilesOnly ? 'true' : 'false'}
|
||||
onChange={() => {
|
||||
galleryContext.setClubSameTimeFilesOnly(
|
||||
!galleryContext.clubSameTimeFilesOnly
|
||||
);
|
||||
}}></input>
|
||||
<div
|
||||
style={{
|
||||
marginLeft: '0.5em',
|
||||
fontSize: '16px',
|
||||
marginRight: '0.8em',
|
||||
}}>
|
||||
{constants.CLUB_BY_CAPTURE_TIME}
|
||||
</div>
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import constants from 'utils/strings/constants';
|
||||
import { IconWithMessage } from './pages/gallery/SelectedFileOptions';
|
||||
import { IconWithMessage } from './pages/gallery/SelectedFileOptions/GalleryOptions';
|
||||
|
||||
const Wrapper = styled.button`
|
||||
border: none;
|
||||
|
|
|
@ -2,7 +2,7 @@ import React, { useContext } from 'react';
|
|||
import { Button } from 'react-bootstrap';
|
||||
import styled from 'styled-components';
|
||||
import constants from 'utils/strings/constants';
|
||||
import { GalleryContext } from 'pages/gallery';
|
||||
import { DeduplicateContext } from 'pages/deduplicate';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
display: flex;
|
||||
|
@ -18,10 +18,10 @@ const Wrapper = styled.div`
|
|||
`;
|
||||
|
||||
export default function EmptyScreen({ openFileUploader }) {
|
||||
const galleryContext = useContext(GalleryContext);
|
||||
const deduplicateContext = useContext(DeduplicateContext);
|
||||
return (
|
||||
<Wrapper>
|
||||
{galleryContext.isDeduplicating ? (
|
||||
{deduplicateContext.isOnDeduplicatePage ? (
|
||||
<b
|
||||
style={{
|
||||
fontSize: '2em',
|
||||
|
|
|
@ -24,6 +24,7 @@ import { PublicCollectionGalleryContext } from 'utils/publicCollectionGallery';
|
|||
import { useRouter } from 'next/router';
|
||||
import EmptyScreen from './EmptyScreen';
|
||||
import { AppContext } from 'pages/_app';
|
||||
import { DeduplicateContext } from 'pages/deduplicate';
|
||||
|
||||
const Container = styled.div`
|
||||
display: block;
|
||||
|
@ -44,20 +45,20 @@ interface Props {
|
|||
files: EnteFile[];
|
||||
setFiles: SetFiles;
|
||||
syncWithRemote: () => Promise<void>;
|
||||
favItemIds: Set<number>;
|
||||
favItemIds?: Set<number>;
|
||||
setSelected: (
|
||||
selected: SelectedState | ((selected: SelectedState) => SelectedState)
|
||||
) => void;
|
||||
selected: SelectedState;
|
||||
isFirstLoad;
|
||||
isFirstLoad?;
|
||||
openFileUploader?;
|
||||
isInSearchMode: boolean;
|
||||
isInSearchMode?: boolean;
|
||||
search?: Search;
|
||||
setSearchStats?: setSearchStats;
|
||||
deleted?: number[];
|
||||
activeCollection: number;
|
||||
isSharedCollection: boolean;
|
||||
enableDownload: boolean;
|
||||
isSharedCollection?: boolean;
|
||||
enableDownload?: boolean;
|
||||
}
|
||||
|
||||
type SourceURL = {
|
||||
|
@ -88,6 +89,7 @@ const PhotoFrame = ({
|
|||
const startTime = Date.now();
|
||||
const galleryContext = useContext(GalleryContext);
|
||||
const appContext = useContext(AppContext);
|
||||
const deduplicateContext = useContext(DeduplicateContext);
|
||||
const publicCollectionGalleryContext = useContext(
|
||||
PublicCollectionGalleryContext
|
||||
);
|
||||
|
@ -171,7 +173,7 @@ const PhotoFrame = ({
|
|||
}),
|
||||
}))
|
||||
.filter((item) => {
|
||||
if (deleted.includes(item.id)) {
|
||||
if (deleted?.includes(item.id)) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
|
@ -550,7 +552,7 @@ const PhotoFrame = ({
|
|||
showAppDownloadBanner={
|
||||
files.length < 30 &&
|
||||
!isInSearchMode &&
|
||||
!galleryContext.isDeduplicating
|
||||
!deduplicateContext.isOnDeduplicatePage
|
||||
}
|
||||
resetFetching={resetFetching}
|
||||
/>
|
||||
|
|
|
@ -15,8 +15,8 @@ import constants from 'utils/strings/constants';
|
|||
import { PublicCollectionGalleryContext } from 'utils/publicCollectionGallery';
|
||||
import { ENTE_WEBSITE_LINK } from 'constants/urls';
|
||||
import { getVariantColor, ButtonVariant } from './pages/gallery/LinkButton';
|
||||
import { GalleryContext } from 'pages/gallery';
|
||||
import { convertBytesToHumanReadable } from 'utils/billing';
|
||||
import { DeduplicateContext } from 'pages/deduplicate';
|
||||
|
||||
const A_DAY = 24 * 60 * 60 * 1000;
|
||||
const NO_OF_PAGES = 2;
|
||||
|
@ -159,7 +159,7 @@ export function PhotoList({
|
|||
const publicCollectionGalleryContext = useContext(
|
||||
PublicCollectionGalleryContext
|
||||
);
|
||||
const galleryContext = useContext(GalleryContext);
|
||||
const deduplicateContext = useContext(DeduplicateContext);
|
||||
|
||||
let columns = Math.floor(width / IMAGE_CONTAINER_MAX_WIDTH);
|
||||
let listItemHeight = IMAGE_CONTAINER_MAX_HEIGHT;
|
||||
|
@ -178,16 +178,13 @@ export function PhotoList({
|
|||
|
||||
useEffect(() => {
|
||||
let timeStampList: TimeStampListItem[] = [];
|
||||
if (galleryContext.isDeduplicating) {
|
||||
if (deduplicateContext.isOnDeduplicatePage) {
|
||||
skipMerge = true;
|
||||
groupByFileSize(timeStampList);
|
||||
} else {
|
||||
groupByTime(timeStampList);
|
||||
}
|
||||
|
||||
if (galleryContext.isDeduplicating) {
|
||||
skipMerge = true;
|
||||
}
|
||||
|
||||
if (!skipMerge) {
|
||||
timeStampList = mergeTimeStampList(timeStampList, columns);
|
||||
}
|
||||
|
@ -221,16 +218,16 @@ export function PhotoList({
|
|||
let index = 0;
|
||||
while (index < filteredData.length) {
|
||||
const file = filteredData[index];
|
||||
const currentFileSize = galleryContext.fileSizeMap.get(file.id);
|
||||
const currentFileSize = deduplicateContext.fileSizeMap.get(file.id);
|
||||
const currentCreationTime = file.metadata.creationTime;
|
||||
let lastFileIndex = index;
|
||||
|
||||
while (lastFileIndex < filteredData.length) {
|
||||
if (
|
||||
galleryContext.fileSizeMap.get(
|
||||
deduplicateContext.fileSizeMap.get(
|
||||
filteredData[lastFileIndex].id
|
||||
) !== currentFileSize ||
|
||||
(galleryContext.clubSameTimeFilesOnly &&
|
||||
(deduplicateContext.clubSameTimeFilesOnly &&
|
||||
filteredData[lastFileIndex].metadata.creationTime !==
|
||||
currentCreationTime)
|
||||
) {
|
||||
|
|
|
@ -290,7 +290,7 @@ export default function Sidebar(props: Props) {
|
|||
<LinkButton
|
||||
style={{ marginTop: '30px' }}
|
||||
onClick={() => {
|
||||
galleryContext.setIsDeduplicating(true);
|
||||
router.push(PAGES.DEDUPLICATE);
|
||||
}}>
|
||||
{constants.DEDUPLICATE_FILES}
|
||||
</LinkButton>
|
||||
|
|
|
@ -5,7 +5,7 @@ import { OverlayTrigger } from 'react-bootstrap';
|
|||
import { COLLECTION_SORT_BY } from 'constants/collection';
|
||||
import constants from 'utils/strings/constants';
|
||||
import CollectionSortOptions from './CollectionSortOptions';
|
||||
import { IconWithMessage } from './SelectedFileOptions';
|
||||
import { IconWithMessage } from './SelectedFileOptions/GalleryOptions';
|
||||
|
||||
interface Props {
|
||||
setCollectionSortBy: (sortBy: COLLECTION_SORT_BY) => void;
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
import { IconButton } from 'components/Container';
|
||||
import {
|
||||
IconWithMessage,
|
||||
SelectionBar,
|
||||
SelectionContainer,
|
||||
} from './GalleryOptions';
|
||||
import constants from 'utils/strings/constants';
|
||||
import DeleteIcon from 'components/icons/DeleteIcon';
|
||||
import React, { useContext } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { DeduplicateContext } from 'pages/deduplicate';
|
||||
import CloseIcon from 'components/icons/CloseIcon';
|
||||
import LeftArrow from 'components/icons/LeftArrow';
|
||||
import { SetDialogMessage } from 'components/MessageDialog';
|
||||
|
||||
const VerticalLine = styled.div`
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
background: #303030;
|
||||
`;
|
||||
|
||||
interface IProps {
|
||||
deleteFileHelper: () => void;
|
||||
setDialogMessage: SetDialogMessage;
|
||||
close: () => void;
|
||||
count: number;
|
||||
}
|
||||
|
||||
export default function DeduplicateOptions({
|
||||
setDialogMessage,
|
||||
deleteFileHelper,
|
||||
close,
|
||||
count,
|
||||
}: IProps) {
|
||||
const deduplicateContext = useContext(DeduplicateContext);
|
||||
|
||||
const trashHandler = () =>
|
||||
setDialogMessage({
|
||||
title: constants.CONFIRM_DELETE,
|
||||
content: constants.TRASH_MESSAGE,
|
||||
staticBackdrop: true,
|
||||
proceed: {
|
||||
action: deleteFileHelper,
|
||||
text: constants.MOVE_TO_TRASH,
|
||||
variant: 'danger',
|
||||
},
|
||||
close: { text: constants.CANCEL },
|
||||
});
|
||||
|
||||
return (
|
||||
<SelectionBar>
|
||||
<SelectionContainer>
|
||||
<IconButton onClick={close}>
|
||||
{deduplicateContext.isOnDeduplicatePage ? (
|
||||
<LeftArrow />
|
||||
) : (
|
||||
<CloseIcon />
|
||||
)}
|
||||
</IconButton>
|
||||
<div>
|
||||
{count} {constants.SELECTED}
|
||||
</div>
|
||||
</SelectionContainer>
|
||||
|
||||
<input
|
||||
type="checkbox"
|
||||
style={{
|
||||
width: '1em',
|
||||
height: '1em',
|
||||
}}
|
||||
value={
|
||||
deduplicateContext.clubSameTimeFilesOnly ? 'true' : 'false'
|
||||
}
|
||||
onChange={() => {
|
||||
deduplicateContext.setClubSameTimeFilesOnly(
|
||||
!deduplicateContext.clubSameTimeFilesOnly
|
||||
);
|
||||
}}></input>
|
||||
<div
|
||||
style={{
|
||||
marginLeft: '0.5em',
|
||||
fontSize: '16px',
|
||||
marginRight: '0.8em',
|
||||
}}>
|
||||
{constants.CLUB_BY_CAPTURE_TIME}
|
||||
</div>
|
||||
<div>
|
||||
<VerticalLine />
|
||||
</div>
|
||||
<IconWithMessage message={constants.DELETE}>
|
||||
<IconButton onClick={trashHandler}>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</IconWithMessage>
|
||||
</SelectionBar>
|
||||
);
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
import { IconButton } from 'components/Container';
|
||||
import { IconWithMessage } from '.';
|
||||
import constants from 'utils/strings/constants';
|
||||
import DeleteIcon from 'components/icons/DeleteIcon';
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
const VerticalLine = styled.div`
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
background: #303030;
|
||||
`;
|
||||
|
||||
export default function DeduplicatingOptions({ trashHandler }) {
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<VerticalLine />
|
||||
</div>
|
||||
<IconWithMessage message={constants.DELETE}>
|
||||
<IconButton onClick={trashHandler}>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</IconWithMessage>
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import { SetDialogMessage } from 'components/MessageDialog';
|
||||
import React, { useEffect, useState, useContext } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { SetCollectionSelectorAttributes } from '../CollectionSelector';
|
||||
import styled from 'styled-components';
|
||||
import Navbar from 'components/Navbar';
|
||||
|
@ -17,7 +17,6 @@ import {
|
|||
TRASH_SECTION,
|
||||
} from 'constants/collection';
|
||||
import UnArchive from 'components/icons/UnArchive';
|
||||
import { OverlayTrigger } from 'react-bootstrap';
|
||||
import { Collection } from 'types/collection';
|
||||
import RemoveIcon from 'components/icons/RemoveIcon';
|
||||
import RestoreIcon from 'components/icons/RestoreIcon';
|
||||
|
@ -26,8 +25,7 @@ import { getData, LS_KEYS } from 'utils/storage/localStorage';
|
|||
import { FIX_CREATION_TIME_VISIBLE_TO_USER_IDS } from 'constants/user';
|
||||
import DownloadIcon from 'components/icons/DownloadIcon';
|
||||
import { User } from 'types/user';
|
||||
import { GalleryContext } from 'pages/gallery';
|
||||
import DeduplicatingOptions from './DeduplicatingOptions';
|
||||
import { OverlayTrigger } from 'react-bootstrap';
|
||||
|
||||
interface Props {
|
||||
addToCollectionHelper?: (collection: Collection) => void;
|
||||
|
@ -48,7 +46,7 @@ interface Props {
|
|||
isFavoriteCollection?: boolean;
|
||||
}
|
||||
|
||||
const SelectionBar = styled(Navbar)`
|
||||
export const SelectionBar = styled(Navbar)`
|
||||
position: fixed;
|
||||
top: 0;
|
||||
color: #fff;
|
||||
|
@ -57,7 +55,7 @@ const SelectionBar = styled(Navbar)`
|
|||
padding: 0 16px;
|
||||
`;
|
||||
|
||||
const SelectionContainer = styled.div`
|
||||
export const SelectionContainer = styled.div`
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
|
@ -67,6 +65,7 @@ interface IconWithMessageProps {
|
|||
children?: any;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export const IconWithMessage = (props: IconWithMessageProps) => (
|
||||
<OverlayTrigger
|
||||
placement="bottom"
|
||||
|
@ -94,7 +93,6 @@ const SelectedFileOptions = ({
|
|||
isFavoriteCollection,
|
||||
}: Props) => {
|
||||
const [showFixCreationTime, setShowFixCreationTime] = useState(false);
|
||||
const galleryContext = useContext(GalleryContext);
|
||||
|
||||
useEffect(() => {
|
||||
const user: User = getData(LS_KEYS.USER);
|
||||
|
@ -177,9 +175,7 @@ const SelectedFileOptions = ({
|
|||
{count} {constants.SELECTED}
|
||||
</div>
|
||||
</SelectionContainer>
|
||||
{galleryContext.isDeduplicating ? (
|
||||
<DeduplicatingOptions trashHandler={trashHandler} />
|
||||
) : activeCollection === TRASH_SECTION ? (
|
||||
{activeCollection === TRASH_SECTION ? (
|
||||
<>
|
||||
<IconWithMessage message={constants.RESTORE}>
|
||||
<IconButton onClick={restoreHandler}>
|
|
@ -13,6 +13,7 @@ export enum PAGES {
|
|||
VERIFY = '/verify',
|
||||
ROOT = '/',
|
||||
SHARED_ALBUMS = '/shared-albums',
|
||||
DEDUPLICATE = '/deduplicate',
|
||||
}
|
||||
export const getAlbumSiteHost = () =>
|
||||
process.env.NODE_ENV === 'production'
|
||||
|
|
142
src/pages/deduplicate/index.tsx
Normal file
142
src/pages/deduplicate/index.tsx
Normal file
|
@ -0,0 +1,142 @@
|
|||
import constants from 'utils/strings/constants';
|
||||
import PhotoFrame from 'components/PhotoFrame';
|
||||
import { ALL_SECTION } from 'constants/collection';
|
||||
import { AppContext } from 'pages/_app';
|
||||
import React, { createContext, useContext, useEffect, useState } from 'react';
|
||||
import {
|
||||
getDuplicateFiles,
|
||||
clubDuplicatesByTime,
|
||||
} from 'services/deduplicationService';
|
||||
import { trashFiles } from 'services/fileService';
|
||||
import { EnteFile } from 'types/file';
|
||||
import { SelectedState } from 'types/gallery';
|
||||
|
||||
import { ServerErrorCodes } from 'utils/error';
|
||||
import { getSelectedFiles } from 'utils/file';
|
||||
import {
|
||||
DeduplicateContextType,
|
||||
DefaultDeduplicateContext,
|
||||
} from 'types/deduplicate';
|
||||
import Router from 'next/router';
|
||||
import DeduplicateOptions from 'components/pages/gallery/SelectedFileOptions/DeduplicateOptions';
|
||||
|
||||
export const DeduplicateContext = createContext<DeduplicateContextType>(
|
||||
DefaultDeduplicateContext
|
||||
);
|
||||
|
||||
export default function Deduplicate() {
|
||||
const { setDialogMessage, startLoading, finishLoading, showNavBar } =
|
||||
useContext(AppContext);
|
||||
const [duplicateFiles, setDuplicateFiles] = useState<EnteFile[]>([]);
|
||||
const [clubSameTimeFilesOnly, setClubSameTimeFilesOnly] = useState(false);
|
||||
const [fileSizeMap, setFileSizeMap] = useState(new Map<number, number>());
|
||||
const [selected, setSelected] = useState<SelectedState>({
|
||||
count: 0,
|
||||
collectionID: 0,
|
||||
});
|
||||
const closeDeduplication = function () {
|
||||
Router.back();
|
||||
setSelected({ count: 0, collectionID: 0 });
|
||||
};
|
||||
useEffect(() => {
|
||||
showNavBar(true);
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
syncWithRemote();
|
||||
}, [clubSameTimeFilesOnly]);
|
||||
|
||||
const syncWithRemote = async () => {
|
||||
startLoading();
|
||||
let duplicates = await getDuplicateFiles();
|
||||
if (clubSameTimeFilesOnly) {
|
||||
duplicates = clubDuplicatesByTime(duplicates);
|
||||
}
|
||||
|
||||
const currFileSizeMap = new Map<number, number>();
|
||||
|
||||
let allDuplicateFiles: EnteFile[] = [];
|
||||
let toSelectFileIDs: number[] = [];
|
||||
let count = 0;
|
||||
|
||||
for (const dupe of duplicates) {
|
||||
allDuplicateFiles = allDuplicateFiles.concat(dupe.files);
|
||||
// select all except first file
|
||||
toSelectFileIDs = toSelectFileIDs.concat(
|
||||
dupe.files.slice(1).map((f) => f.id)
|
||||
);
|
||||
count += dupe.files.length - 1;
|
||||
|
||||
for (const file of dupe.files) {
|
||||
currFileSizeMap.set(file.id, dupe.size);
|
||||
}
|
||||
}
|
||||
setDuplicateFiles(allDuplicateFiles);
|
||||
setFileSizeMap(currFileSizeMap);
|
||||
|
||||
const selectedFiles = {
|
||||
count: count,
|
||||
collectionID: ALL_SECTION,
|
||||
};
|
||||
|
||||
for (const fileID of toSelectFileIDs) {
|
||||
selectedFiles[fileID] = true;
|
||||
}
|
||||
setSelected(selectedFiles);
|
||||
finishLoading();
|
||||
};
|
||||
|
||||
const deleteFileHelper = async () => {
|
||||
try {
|
||||
startLoading();
|
||||
const selectedFiles = getSelectedFiles(selected, duplicateFiles);
|
||||
await trashFiles(selectedFiles);
|
||||
closeDeduplication();
|
||||
} catch (e) {
|
||||
switch (e.status?.toString()) {
|
||||
case ServerErrorCodes.FORBIDDEN:
|
||||
setDialogMessage({
|
||||
title: constants.ERROR,
|
||||
staticBackdrop: true,
|
||||
close: { variant: 'danger' },
|
||||
content: constants.NOT_FILE_OWNER,
|
||||
});
|
||||
}
|
||||
setDialogMessage({
|
||||
title: constants.ERROR,
|
||||
staticBackdrop: true,
|
||||
close: { variant: 'danger' },
|
||||
content: constants.UNKNOWN_ERROR,
|
||||
});
|
||||
} finally {
|
||||
await syncWithRemote();
|
||||
finishLoading();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<DeduplicateContext.Provider
|
||||
value={{
|
||||
...DefaultDeduplicateContext,
|
||||
clubSameTimeFilesOnly,
|
||||
setClubSameTimeFilesOnly,
|
||||
fileSizeMap,
|
||||
isOnDeduplicatePage: true,
|
||||
}}>
|
||||
<PhotoFrame
|
||||
files={duplicateFiles}
|
||||
setFiles={setDuplicateFiles}
|
||||
syncWithRemote={syncWithRemote}
|
||||
setSelected={setSelected}
|
||||
selected={selected}
|
||||
activeCollection={ALL_SECTION}
|
||||
/>
|
||||
<DeduplicateOptions
|
||||
setDialogMessage={setDialogMessage}
|
||||
deleteFileHelper={deleteFileHelper}
|
||||
count={selected.count}
|
||||
close={closeDeduplication}
|
||||
/>
|
||||
</DeduplicateContext.Provider>
|
||||
);
|
||||
}
|
|
@ -51,7 +51,7 @@ import {
|
|||
sortFilesIntoCollections,
|
||||
} from 'utils/file';
|
||||
import SearchBar from 'components/Search';
|
||||
import SelectedFileOptions from 'components/pages/gallery/SelectedFileOptions';
|
||||
import SelectedFileOptions from 'components/pages/gallery/SelectedFileOptions/GalleryOptions';
|
||||
import CollectionSelector, {
|
||||
CollectionSelectorAttributes,
|
||||
} from 'components/pages/gallery/CollectionSelector';
|
||||
|
@ -103,12 +103,6 @@ import {
|
|||
import Collections from 'components/pages/gallery/Collections';
|
||||
import { VISIBILITY_STATE } from 'constants/file';
|
||||
import ToastNotification from 'components/ToastNotification';
|
||||
import {
|
||||
clubDuplicatesByTime,
|
||||
getDuplicateFiles,
|
||||
} from 'services/deduplicationService';
|
||||
import ClubDuplicateFilesByTime from 'components/ClubDuplicateFilesByTime';
|
||||
import BackButton from 'components/BackButton';
|
||||
|
||||
export const DeadCenter = styled.div`
|
||||
flex: 1;
|
||||
|
@ -134,11 +128,6 @@ const defaultGalleryContext: GalleryContextType = {
|
|||
|
||||
setNotificationAttributes: () => null,
|
||||
setBlockingLoad: () => null,
|
||||
clubSameTimeFilesOnly: false,
|
||||
setClubSameTimeFilesOnly: null,
|
||||
fileSizeMap: new Map<number, number>(),
|
||||
isDeduplicating: false,
|
||||
setIsDeduplicating: null,
|
||||
};
|
||||
|
||||
export const GalleryContext = createContext<GalleryContextType>(
|
||||
|
@ -205,11 +194,6 @@ export default function Gallery() {
|
|||
const [notificationAttributes, setNotificationAttributes] =
|
||||
useState<NotificationAttributes>(null);
|
||||
|
||||
const [isDeduplicating, setIsDeduplicating] = useState(false);
|
||||
const [duplicateFiles, setDuplicateFiles] = useState<EnteFile[]>([]);
|
||||
const [clubSameTimeFilesOnly, setClubSameTimeFilesOnly] = useState(false);
|
||||
const [fileSizeMap, setFileSizeMap] = useState(new Map<number, number>());
|
||||
|
||||
const showPlanSelectorModal = () => setPlanModalView(true);
|
||||
|
||||
const clearNotificationAttributes = () => setNotificationAttributes(null);
|
||||
|
@ -246,57 +230,6 @@ export default function Gallery() {
|
|||
appContext.showNavBar(true);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const main = async () => {
|
||||
if (isDeduplicating) {
|
||||
startLoading();
|
||||
let duplicates = await getDuplicateFiles();
|
||||
if (clubSameTimeFilesOnly) {
|
||||
duplicates = clubDuplicatesByTime(duplicates);
|
||||
}
|
||||
|
||||
const currFileSizeMap = new Map<number, number>();
|
||||
|
||||
let allDuplicateFiles: EnteFile[] = [];
|
||||
let toSelectFileIDs: number[] = [];
|
||||
let count = 0;
|
||||
|
||||
for (const dupe of duplicates) {
|
||||
allDuplicateFiles = allDuplicateFiles.concat(dupe.files);
|
||||
// select all except first file
|
||||
toSelectFileIDs = toSelectFileIDs.concat(
|
||||
dupe.files.slice(1).map((f) => f.id)
|
||||
);
|
||||
count += dupe.files.length - 1;
|
||||
|
||||
for (const file of dupe.files) {
|
||||
currFileSizeMap.set(file.id, dupe.size);
|
||||
}
|
||||
}
|
||||
setDuplicateFiles(allDuplicateFiles);
|
||||
setFileSizeMap(currFileSizeMap);
|
||||
|
||||
const selectedFiles = {
|
||||
count: count,
|
||||
collectionID: ALL_SECTION,
|
||||
};
|
||||
|
||||
for (const fileID of toSelectFileIDs) {
|
||||
selectedFiles[fileID] = true;
|
||||
}
|
||||
setSelected(selectedFiles);
|
||||
setActiveCollection(ALL_SECTION);
|
||||
finishLoading();
|
||||
} else {
|
||||
setDuplicateFiles([]);
|
||||
setFileSizeMap(new Map<number, number>());
|
||||
setClubSameTimeFilesOnly(false);
|
||||
}
|
||||
};
|
||||
|
||||
main();
|
||||
}, [isDeduplicating, clubSameTimeFilesOnly]);
|
||||
|
||||
useEffect(
|
||||
() => collectionSelectorAttributes && setCollectionSelectorView(true),
|
||||
[collectionSelectorAttributes]
|
||||
|
@ -619,11 +552,6 @@ export default function Gallery() {
|
|||
|
||||
setNotificationAttributes,
|
||||
setBlockingLoad,
|
||||
clubSameTimeFilesOnly,
|
||||
setClubSameTimeFilesOnly,
|
||||
fileSizeMap,
|
||||
setIsDeduplicating: setIsDeduplicating,
|
||||
isDeduplicating: isDeduplicating,
|
||||
}}>
|
||||
<FullScreenDropZone
|
||||
getRootProps={getRootProps}
|
||||
|
@ -657,35 +585,29 @@ export default function Gallery() {
|
|||
setFiles={setFiles}
|
||||
isFirstUpload={collectionsAndTheirLatestFile?.length === 0}
|
||||
/>
|
||||
{!isDeduplicating && (
|
||||
<>
|
||||
<SearchBar
|
||||
isOpen={isInSearchMode}
|
||||
setOpen={setIsInSearchMode}
|
||||
isFirstFetch={isFirstFetch}
|
||||
collections={collections}
|
||||
files={getNonTrashedUniqueUserFiles(files)}
|
||||
setActiveCollection={setActiveCollection}
|
||||
setSearch={updateSearch}
|
||||
searchStats={searchStats}
|
||||
/>
|
||||
<Collections
|
||||
collections={collections}
|
||||
collectionAndTheirLatestFile={
|
||||
collectionsAndTheirLatestFile
|
||||
}
|
||||
isInSearchMode={isInSearchMode}
|
||||
activeCollection={activeCollection}
|
||||
setActiveCollection={setActiveCollection}
|
||||
syncWithRemote={syncWithRemote}
|
||||
setDialogMessage={setDialogMessage}
|
||||
setCollectionNamerAttributes={
|
||||
setCollectionNamerAttributes
|
||||
}
|
||||
collectionFilesCount={collectionFilesCount}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
<SearchBar
|
||||
isOpen={isInSearchMode}
|
||||
setOpen={setIsInSearchMode}
|
||||
isFirstFetch={isFirstFetch}
|
||||
collections={collections}
|
||||
files={getNonTrashedUniqueUserFiles(files)}
|
||||
setActiveCollection={setActiveCollection}
|
||||
setSearch={updateSearch}
|
||||
searchStats={searchStats}
|
||||
/>
|
||||
<Collections
|
||||
collections={collections}
|
||||
collectionAndTheirLatestFile={collectionsAndTheirLatestFile}
|
||||
isInSearchMode={isInSearchMode}
|
||||
activeCollection={activeCollection}
|
||||
setActiveCollection={setActiveCollection}
|
||||
syncWithRemote={syncWithRemote}
|
||||
setDialogMessage={setDialogMessage}
|
||||
setCollectionNamerAttributes={setCollectionNamerAttributes}
|
||||
collectionFilesCount={collectionFilesCount}
|
||||
/>
|
||||
|
||||
{blockingLoad && (
|
||||
<LoadingOverlay>
|
||||
<EnteSpinner />
|
||||
|
@ -722,28 +644,19 @@ export default function Gallery() {
|
|||
attributes={fixCreationTimeAttributes}
|
||||
/>
|
||||
|
||||
{isDeduplicating ? (
|
||||
<>
|
||||
<BackButton setIsDeduplicating={setIsDeduplicating} />
|
||||
<ClubDuplicateFilesByTime />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<UploadButton
|
||||
isFirstFetch={isFirstFetch}
|
||||
openFileUploader={openFileUploader}
|
||||
/>
|
||||
<Sidebar
|
||||
collections={collections}
|
||||
setDialogMessage={setDialogMessage}
|
||||
setLoading={setBlockingLoad}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<UploadButton
|
||||
isFirstFetch={isFirstFetch}
|
||||
openFileUploader={openFileUploader}
|
||||
/>
|
||||
<Sidebar
|
||||
collections={collections}
|
||||
setDialogMessage={setDialogMessage}
|
||||
setLoading={setBlockingLoad}
|
||||
/>
|
||||
|
||||
<PhotoFrame
|
||||
files={isDeduplicating ? duplicateFiles : files}
|
||||
setFiles={isDeduplicating ? setDuplicateFiles : setFiles}
|
||||
files={files}
|
||||
setFiles={setFiles}
|
||||
syncWithRemote={syncWithRemote}
|
||||
favItemIds={favItemIds}
|
||||
setSelected={setSelected}
|
||||
|
|
13
src/types/deduplicate/index.ts
Normal file
13
src/types/deduplicate/index.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
export type DeduplicateContextType = {
|
||||
clubSameTimeFilesOnly: boolean;
|
||||
setClubSameTimeFilesOnly: (clubSameTimeFilesOnly: boolean) => void;
|
||||
fileSizeMap: Map<number, number>;
|
||||
isOnDeduplicatePage: boolean;
|
||||
};
|
||||
|
||||
export const DefaultDeduplicateContext = {
|
||||
clubSameTimeFilesOnly: false,
|
||||
setClubSameTimeFilesOnly: () => null,
|
||||
fileSizeMap: new Map<number, number>(),
|
||||
isOnDeduplicatePage: false,
|
||||
};
|
|
@ -31,11 +31,6 @@ export type GalleryContextType = {
|
|||
|
||||
setNotificationAttributes: (attributes: NotificationAttributes) => void;
|
||||
setBlockingLoad: (value: boolean) => void;
|
||||
clubSameTimeFilesOnly: boolean;
|
||||
setClubSameTimeFilesOnly: (clubSameTimeFilesOnly: boolean) => void;
|
||||
fileSizeMap: Map<number, number>;
|
||||
isDeduplicating: boolean;
|
||||
setIsDeduplicating: (value: boolean) => void;
|
||||
};
|
||||
|
||||
export interface NotificationAttributes {
|
||||
|
|
Loading…
Reference in a new issue