From 0263e5cb38a0dca251f10d5104f9cdcf554c31b8 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Tue, 2 Nov 2021 13:05:41 +0530 Subject: [PATCH 01/43] ad change file title logic --- src/services/fileService.ts | 1 + src/utils/file/index.ts | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/src/services/fileService.ts b/src/services/fileService.ts index 45d2062f3..dab497d06 100644 --- a/src/services/fileService.ts +++ b/src/services/fileService.ts @@ -75,6 +75,7 @@ export interface MagicMetadata extends Omit { export interface PublicMagicMetadataProps { editedTime?: number; + editedName?: string; } export interface PublicMagicMetadata extends Omit { diff --git a/src/utils/file/index.ts b/src/utils/file/index.ts index 13d5bec00..9efd84447 100644 --- a/src/utils/file/index.ts +++ b/src/utils/file/index.ts @@ -381,6 +381,17 @@ export async function changeFileCreationTime(file: File, editedTime: number) { ); } +export async function changeFileName(file: File, editedName: string) { + const updatedPublicMagicMetadataProps: PublicMagicMetadataProps = { + editedName, + }; + + return await updatePublicMagicMetadataProps( + file, + updatedPublicMagicMetadataProps + ); +} + export function isSharedFile(file: File) { const user: User = getData(LS_KEYS.USER); @@ -400,6 +411,9 @@ export function mergeMetadata(files: File[]): File[] { ...(file.pubMagicMetadata?.data.editedTime && { creationTime: file.pubMagicMetadata.data.editedTime, }), + ...(file.pubMagicMetadata?.data.editedName && { + title: file.pubMagicMetadata.data.editedName, + }), } : {}), ...(file.magicMetadata?.data ? file.magicMetadata.data : {}), From 70c175a10b3c2b5e7125c16e5684d4d0e64cea43 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Tue, 2 Nov 2021 13:06:04 +0530 Subject: [PATCH 02/43] update FIle name Ui to have edit option --- src/components/PhotoSwipe/PhotoSwipe.tsx | 99 +++++++++++++++++++++++- 1 file changed, 95 insertions(+), 4 deletions(-) diff --git a/src/components/PhotoSwipe/PhotoSwipe.tsx b/src/components/PhotoSwipe/PhotoSwipe.tsx index 76e0a373b..aff474ead 100644 --- a/src/components/PhotoSwipe/PhotoSwipe.tsx +++ b/src/components/PhotoSwipe/PhotoSwipe.tsx @@ -20,6 +20,7 @@ import styled from 'styled-components'; import events from './events'; import { changeFileCreationTime, + changeFileName, downloadFile, formatDateTime, updateExistingFilePubMetadata, @@ -27,11 +28,19 @@ import { import { FormCheck } from 'react-bootstrap'; import { prettyPrintExif } from 'utils/exif'; import EditIcon from 'components/icons/EditIcon'; -import { IconButton, Label, Row, Value } from 'components/Container'; +import { + FlexWrapper, + IconButton, + Label, + Row, + Value, +} from 'components/Container'; import { logError } from 'utils/sentry'; import DatePicker from 'react-datepicker'; import 'react-datepicker/dist/react-datepicker.css'; +import TickIcon from 'components/icons/TickIcon'; +import CloseIcon from 'components/icons/CloseIcon'; interface Iprops { isOpen: boolean; @@ -83,7 +92,7 @@ function RenderCreationTime({ file: File; scheduleUpdate: () => void; }) { - const originalCreationTime = new Date(file.metadata.creationTime / 1000); + const originalCreationTime = new Date(file?.metadata.creationTime / 1000); const [isInEditMode, setIsInEditMode] = useState(false); const [pickedTime, setPickedTime] = useState(originalCreationTime); @@ -177,6 +186,84 @@ function RenderCreationTime({ ); } + +function RenderFileName({ + file, + scheduleUpdate, +}: { + file: File; + scheduleUpdate: () => void; +}) { + const originalFileName = file?.metadata.title; + const [isInEditMode, setIsInEditMode] = useState(false); + + const [newFileName, setNewFileName] = useState(originalFileName); + + const openEditMode = () => setIsInEditMode(true); + + const saveEdits = async () => { + try { + if (isInEditMode && file) { + if (newFileName === originalFileName) { + return; + } + let updatedFile = await changeFileName(file, newFileName); + updatedFile = ( + await updatePublicMagicMetadata([updatedFile]) + )[0]; + updateExistingFilePubMetadata(file, updatedFile); + scheduleUpdate(); + } + } catch (e) { + logError(e, 'failed to update file name'); + } + setIsInEditMode(false); + }; + const discardEdits = () => { + setNewFileName(originalFileName); + setIsInEditMode(false); + }; + const handleChange = (event) => { + setNewFileName(event.target.value); + }; + return ( + <> + + + + {isInEditMode ? ( + + ) : ( + <>{newFileName} + )} + + + {isInEditMode ? ( + + + + + + + + + ) : ( + + + + )} + + + + ); +} function ExifData(props: { exif: any }) { const { exif } = props; const [showAll, setShowAll] = useState(false); @@ -252,8 +339,12 @@ function InfoModal({ constants.FILE_ID, items[photoSwipe?.getCurrentIndex()]?.id )} - {metadata?.title && - renderInfoItem(constants.FILE_NAME, metadata.title)} + {metadata?.title && ( + + )} {metadata?.creationTime && ( Date: Tue, 2 Nov 2021 13:21:00 +0530 Subject: [PATCH 03/43] added file name charcter limit --- src/components/PhotoSwipe/PhotoSwipe.tsx | 32 ++++++++++++++++++------ src/services/fileService.ts | 2 ++ src/utils/strings/englishConstants.tsx | 1 + 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/components/PhotoSwipe/PhotoSwipe.tsx b/src/components/PhotoSwipe/PhotoSwipe.tsx index aff474ead..7054d7e88 100644 --- a/src/components/PhotoSwipe/PhotoSwipe.tsx +++ b/src/components/PhotoSwipe/PhotoSwipe.tsx @@ -9,6 +9,7 @@ import { } from 'services/collectionService'; import { File, + MAX_EDITED_FILE_NAME_LENGTH, MIN_EDITED_CREATION_TIME, updatePublicMagicMetadata, } from 'services/fileService'; @@ -77,6 +78,12 @@ const ButtonContainer = styled.div` width: 200px; padding: 5px 10px; `; +const WarningMessage = styled.div` + width: 100%; + margin-top: 0.25rem; + font-size: 80%; + color: #dc3545; +`; const renderInfoItem = (label: string, value: string | JSX.Element) => ( @@ -224,7 +231,10 @@ function RenderFileName({ setIsInEditMode(false); }; const handleChange = (event) => { - setNewFileName(event.target.value); + const newName = event.target.value.replace(/(\r\n|\n|\r)/gm, ''); + if (newName.length <= MAX_EDITED_FILE_NAME_LENGTH) { + setNewFileName(event.target.value); + } }; return ( <> @@ -232,12 +242,20 @@ function RenderFileName({ {isInEditMode ? ( - +
+ + {newFileName.length === + MAX_EDITED_FILE_NAME_LENGTH && ( + + {constants.FILE_NAME_CHARACTER_LIMIT} + + )} +
) : ( <>{newFileName} )} diff --git a/src/services/fileService.ts b/src/services/fileService.ts index dab497d06..522f61a8c 100644 --- a/src/services/fileService.ts +++ b/src/services/fileService.ts @@ -24,6 +24,8 @@ const FILES = 'files'; export const MIN_EDITED_CREATION_TIME = '1800-01-01T00:00:00.000Z'; +export const MAX_EDITED_FILE_NAME_LENGTH = 100; + export interface fileAttribute { encryptedData?: DataStream | Uint8Array; objectKey?: string; diff --git a/src/utils/strings/englishConstants.tsx b/src/utils/strings/englishConstants.tsx index 03a5b4544..8cf784a08 100644 --- a/src/utils/strings/englishConstants.tsx +++ b/src/utils/strings/englishConstants.tsx @@ -596,6 +596,7 @@ const englishConstants = { REPLACE_THUMBNAIL_COMPLETED_WITH_ERROR: () => ( <>could not compress some of your thumbnails, please retry ), + FILE_NAME_CHARACTER_LIMIT: '100 characters max', }; export default englishConstants; From dbe55543736dd5b494923be91b6d11c2cbda58fa Mon Sep 17 00:00:00 2001 From: Abhinav Date: Tue, 2 Nov 2021 14:06:15 +0530 Subject: [PATCH 04/43] close edit mode if saved without changes --- src/components/PhotoSwipe/PhotoSwipe.tsx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/components/PhotoSwipe/PhotoSwipe.tsx b/src/components/PhotoSwipe/PhotoSwipe.tsx index 7054d7e88..27c7e495f 100644 --- a/src/components/PhotoSwipe/PhotoSwipe.tsx +++ b/src/components/PhotoSwipe/PhotoSwipe.tsx @@ -105,12 +105,13 @@ function RenderCreationTime({ const [pickedTime, setPickedTime] = useState(originalCreationTime); const openEditMode = () => setIsInEditMode(true); - + const closeEditMode = () => setIsInEditMode(false); const saveEdits = async () => { try { if (isInEditMode && file) { const unixTimeInMicroSec = pickedTime.getTime() * 1000; if (unixTimeInMicroSec === file.metadata.creationTime) { + closeEditMode(); return; } let updatedFile = await changeFileCreationTime( @@ -126,11 +127,11 @@ function RenderCreationTime({ } catch (e) { logError(e, 'failed to update creationTime'); } - setIsInEditMode(false); + closeEditMode(); }; const discardEdits = () => { setPickedTime(originalCreationTime); - setIsInEditMode(false); + closeEditMode(); }; const handleChange = (newDate) => { if (newDate instanceof Date) { @@ -207,11 +208,13 @@ function RenderFileName({ const [newFileName, setNewFileName] = useState(originalFileName); const openEditMode = () => setIsInEditMode(true); + const closeEditMode = () => setIsInEditMode(false); const saveEdits = async () => { try { if (isInEditMode && file) { if (newFileName === originalFileName) { + closeEditMode(); return; } let updatedFile = await changeFileName(file, newFileName); @@ -224,11 +227,11 @@ function RenderFileName({ } catch (e) { logError(e, 'failed to update file name'); } - setIsInEditMode(false); + closeEditMode(); }; const discardEdits = () => { setNewFileName(originalFileName); - setIsInEditMode(false); + closeEditMode(); }; const handleChange = (event) => { const newName = event.target.value.replace(/(\r\n|\n|\r)/gm, ''); From 5095bde562b8405930d36036bedef1e123a3e8d2 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 3 Nov 2021 16:01:47 +0530 Subject: [PATCH 05/43] adds updateCreationTimeWithExif script --- src/services/updateCreationTimeWithExif.ts | 82 ++++++++++++++++++++++ src/services/upload/exifService.ts | 17 +++++ 2 files changed, 99 insertions(+) create mode 100644 src/services/updateCreationTimeWithExif.ts diff --git a/src/services/updateCreationTimeWithExif.ts b/src/services/updateCreationTimeWithExif.ts new file mode 100644 index 000000000..f0c6b169d --- /dev/null +++ b/src/services/updateCreationTimeWithExif.ts @@ -0,0 +1,82 @@ +import { SetProgressTracker } from 'components/FixLargeThumbnail'; +import { + changeFileCreationTime, + updateExistingFilePubMetadata, +} from 'utils/file'; +import { logError } from 'utils/sentry'; +import localForage from 'utils/storage/localForage'; +import downloadManager from './downloadManager'; +import { getLocalFiles, File, updatePublicMagicMetadata } from './fileService'; +import { getLocalTrash, getTrashedFiles } from './trashService'; +import { getExifDataFromURL } from './upload/exifService'; + +const CREATION_TIME_UPDATED_FILES_TABLE = 'creation-time-updated-file-table'; + +export async function getCreationTimeUpdatedFiles() { + return ( + (await localForage.getItem( + CREATION_TIME_UPDATED_FILES_TABLE + )) ?? [] + ); +} + +export async function setCreationTimeUpdatedFiles(creationTimeUpdatedFiles) { + return await localForage.setItem( + CREATION_TIME_UPDATED_FILES_TABLE, + creationTimeUpdatedFiles + ); +} + +export async function getFilesPendingCreationTimeUpdate() { + const files = await getLocalFiles(); + const trash = await getLocalTrash(); + const trashFiles = getTrashedFiles(trash); + const allFiles = [...files, ...trashFiles]; + const updateFiles = new Set(await getCreationTimeUpdatedFiles()); + + const pendingFiles = allFiles.filter((file) => !updateFiles.has(file.id)); + return pendingFiles; +} + +export async function updateCreationTimeWithExif( + filesToBeUpdated: File[], + setProgressTracker: SetProgressTracker +) { + let completedWithError = false; + try { + if (filesToBeUpdated.length === 0) { + return completedWithError; + } + const updatedFiles = await getCreationTimeUpdatedFiles(); + setProgressTracker({ current: 0, total: filesToBeUpdated.length }); + for (const [index, file] of filesToBeUpdated.entries()) { + try { + const fileURL = await downloadManager.getFile(file); + const exifData = await getExifDataFromURL(fileURL); + if (exifData?.creationTime) { + let updatedFile = await changeFileCreationTime( + file, + exifData.creationTime + ); + updatedFile = ( + await updatePublicMagicMetadata([updatedFile]) + )[0]; + updateExistingFilePubMetadata(file, updatedFile); + } + setProgressTracker({ + current: index, + total: filesToBeUpdated.length, + }); + updatedFiles.push(file.id); + setCreationTimeUpdatedFiles(updatedFiles); + } catch (e) { + logError(e, 'failed to updated a CreationTime With Exif'); + completedWithError = true; + } + } + } catch (e) { + logError(e, 'update CreationTime With Exif failed'); + completedWithError = true; + } + return completedWithError; +} diff --git a/src/services/upload/exifService.ts b/src/services/upload/exifService.ts index cacd81f2c..447b727ef 100644 --- a/src/services/upload/exifService.ts +++ b/src/services/upload/exifService.ts @@ -36,6 +36,23 @@ export async function getExifData( } } +export async function getExifDataFromURL(url: string): Promise { + try { + const exifData = await exifr.parse(url, EXIF_TAGS_NEEDED); + if (!exifData) { + return { location: NULL_LOCATION, creationTime: null }; + } + const parsedEXIFData = { + location: getEXIFLocation(exifData), + creationTime: getUNIXTime(exifData), + }; + return parsedEXIFData; + } catch (e) { + logError(e, 'error reading exif data'); + // ignore exif parsing errors + } +} + function getUNIXTime(exifData: any) { const dateTime: Date = exifData.DateTimeOriginal ?? exifData.CreateDate ?? exifData.ModifyDate; From e052da48ff24c53e15f5cad587cca151f12e3b57 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 3 Nov 2021 16:04:11 +0530 Subject: [PATCH 06/43] adds Fix creationTime UI --- src/components/FixCreationTime.tsx | 183 +++++++++++++++++++++++++ src/components/Sidebar.tsx | 14 ++ src/utils/storage/localStorage.ts | 1 + src/utils/strings/englishConstants.tsx | 17 +++ 4 files changed, 215 insertions(+) create mode 100644 src/components/FixCreationTime.tsx diff --git a/src/components/FixCreationTime.tsx b/src/components/FixCreationTime.tsx new file mode 100644 index 000000000..db1295d04 --- /dev/null +++ b/src/components/FixCreationTime.tsx @@ -0,0 +1,183 @@ +import constants from 'utils/strings/constants'; +import MessageDialog from './MessageDialog'; +import React, { useEffect, useState } from 'react'; +import { ProgressBar, Button } from 'react-bootstrap'; +import { ComfySpan } from './ExportInProgress'; +import { getData, LS_KEYS, setData } from 'utils/storage/localStorage'; +import { + getFilesPendingCreationTimeUpdate, + updateCreationTimeWithExif, +} from 'services/updateCreationTimeWithExif'; + +interface Props { + isOpen: boolean; + show: () => void; + hide: () => void; +} +export enum FIX_STATE { + NOT_STARTED, + FIX_LATER, + NOOP, + RUNNING, + COMPLETED, + COMPLETED_WITH_ERRORS, +} +function Message(props: { fixState: FIX_STATE }) { + let message = null; + switch (props.fixState) { + case FIX_STATE.NOT_STARTED: + case FIX_STATE.FIX_LATER: + message = constants.UPDATE_CREATION_TIME_NOT_STARTED(); + break; + case FIX_STATE.COMPLETED: + message = constants.UPDATE_CREATION_TIME_COMPLETED(); + break; + case FIX_STATE.NOOP: + message = constants.UPDATE_CREATION_TIME_NOOP(); + break; + case FIX_STATE.COMPLETED_WITH_ERRORS: + message = constants.UPDATE_CREATION_TIME_COMPLETED_WITH_ERROR(); + break; + } + return message ? ( +
{message}
+ ) : ( + <> + ); +} +export default function FixCreationTime(props: Props) { + const [fixState, setFixState] = useState(FIX_STATE.NOT_STARTED); + const [progressTracker, setProgressTracker] = useState({ + current: 0, + total: 0, + }); + + const updateFixState = (fixState: FIX_STATE) => { + setFixState(fixState); + setData(LS_KEYS.CREATION_TIME_FIX_STATE, { state: fixState }); + }; + + const init = () => { + let fixState = getData(LS_KEYS.CREATION_TIME_FIX_STATE)?.state; + if (!fixState || fixState === FIX_STATE.RUNNING) { + fixState = FIX_STATE.NOT_STARTED; + updateFixState(fixState); + } + if (fixState === FIX_STATE.COMPLETED) { + fixState = FIX_STATE.NOOP; + updateFixState(fixState); + } + setFixState(fixState); + return fixState; + }; + + useEffect(() => { + init(); + }, []); + + const startFix = async () => { + updateFixState(FIX_STATE.RUNNING); + const filesToBeUpdated = await getFilesPendingCreationTimeUpdate(); + const completedWithoutError = await updateCreationTimeWithExif( + filesToBeUpdated, + setProgressTracker + ); + if (!completedWithoutError) { + updateFixState(FIX_STATE.COMPLETED); + } else { + updateFixState(FIX_STATE.COMPLETED_WITH_ERRORS); + } + }; + + return ( + +
+ + + {fixState === FIX_STATE.RUNNING && ( + <> +
+ + {' '} + {progressTracker.current} /{' '} + {progressTracker.total}{' '} + {' '} + + {' '} + {constants.CREATION_TIME_UPDATED} + +
+
+ +
+ + )} +
+ {fixState === FIX_STATE.NOT_STARTED || + fixState === FIX_STATE.FIX_LATER ? ( + + ) : ( + + )} + {(fixState === FIX_STATE.NOT_STARTED || + fixState === FIX_STATE.FIX_LATER || + fixState === FIX_STATE.COMPLETED_WITH_ERRORS) && ( + <> +
+ + + + )} +
+
+ + ); +} diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index 8d7358048..024599002 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -37,6 +37,7 @@ import { TRASH_SECTION, } from 'components/pages/gallery/Collections'; import FixLargeThumbnails from './FixLargeThumbnail'; +import FixCreationTime from './FixCreationTime'; interface Props { collections: Collection[]; setDialogMessage: SetDialogMessage; @@ -55,6 +56,7 @@ export default function Sidebar(props: Props) { const [twoFactorModalView, setTwoFactorModalView] = useState(false); const [exportModalView, setExportModalView] = useState(false); const [fixLargeThumbsView, setFixLargeThumbsView] = useState(false); + const [fixCreationTimeView, setFixCreationTimeView] = useState(false); const galleryContext = useContext(GalleryContext); useEffect(() => { const main = async () => { @@ -292,6 +294,18 @@ export default function Sidebar(props: Props) { {constants.FIX_LARGE_THUMBNAILS} + <> + setFixCreationTimeView(false)} + show={() => setFixCreationTimeView(true)} + /> + setFixCreationTimeView(true)}> + {constants.FIX_CREATION_TIME} + + diff --git a/src/utils/storage/localStorage.ts b/src/utils/storage/localStorage.ts index fddfe119e..16a9ea112 100644 --- a/src/utils/storage/localStorage.ts +++ b/src/utils/storage/localStorage.ts @@ -13,6 +13,7 @@ export enum LS_KEYS { EXPORT = 'export', AnonymizeUserID = 'anonymizedUserID', THUMBNAIL_FIX_STATE = 'thumbnailFixState', + CREATION_TIME_FIX_STATE = 'creationTimeFixState', } export const setData = (key: LS_KEYS, value: object) => { diff --git a/src/utils/strings/englishConstants.tsx b/src/utils/strings/englishConstants.tsx index 03a5b4544..53e8bbb79 100644 --- a/src/utils/strings/englishConstants.tsx +++ b/src/utils/strings/englishConstants.tsx @@ -596,6 +596,23 @@ const englishConstants = { REPLACE_THUMBNAIL_COMPLETED_WITH_ERROR: () => ( <>could not compress some of your thumbnails, please retry ), + FIX_CREATION_TIME: 'fix creation time', + CREATION_TIME_UPDATED: `file's creation time updated`, + + UPDATE_CREATION_TIME_NOT_STARTED: () => ( + <>update creation time with exif values + ), + UPDATE_CREATION_TIME_COMPLETED: () => <>successfully updated all files, + UPDATE_CREATION_TIME_NOOP: () => ( + <> + you have no files that whose CreationTime is not updated with exif + value + + ), + UPDATE_CREATION_TIME_COMPLETED_WITH_ERROR: () => ( + <>updating creation time failed for some files ,try again + ), + FIX_CREATION_TIME_LATER: 'update later', }; export default englishConstants; From 51923850cb6b878bc2f91630a88bbd2113853a56 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 3 Nov 2021 16:05:01 +0530 Subject: [PATCH 07/43] update string constant name for fix large thumbnail --- src/components/FixLargeThumbnail.tsx | 7 ++++--- src/utils/strings/englishConstants.tsx | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/components/FixLargeThumbnail.tsx b/src/components/FixLargeThumbnail.tsx index bc9f6f2c2..9d574901e 100644 --- a/src/components/FixLargeThumbnail.tsx +++ b/src/components/FixLargeThumbnail.tsx @@ -189,7 +189,8 @@ export default function FixLargeThumbnails(props: Props) { display: 'flex', justifyContent: 'space-around', }}> - {fixState === FIX_STATE.NOT_STARTED ? ( + {fixState === FIX_STATE.NOT_STARTED || + fixState === FIX_STATE.FIX_LATER ? ( ) : ( )} diff --git a/src/utils/strings/englishConstants.tsx b/src/utils/strings/englishConstants.tsx index 53e8bbb79..2eac8fa86 100644 --- a/src/utils/strings/englishConstants.tsx +++ b/src/utils/strings/englishConstants.tsx @@ -579,8 +579,8 @@ const englishConstants = { SORT_BY_COLLECTION_NAME: 'album name', FIX_LARGE_THUMBNAILS: 'compress thumbnails', THUMBNAIL_REPLACED: 'thumbnails compressed', - FIX: 'compress', - FIX_LATER: 'compress later', + FIX_THUMBNAIL: 'compress', + FIX_THUMBNAIL_LATER: 'compress later', REPLACE_THUMBNAIL_NOT_STARTED: () => ( <> some of your videos thumbnails can be compressed to save space. From 40846245fff5549c84dd27a4f8f7a05785fd856d Mon Sep 17 00:00:00 2001 From: Abhinav Date: Mon, 8 Nov 2021 12:49:04 +0530 Subject: [PATCH 08/43] break long file name --- src/components/PhotoSwipe/PhotoSwipe.tsx | 3 ++- src/components/RecoveryKeyModal.tsx | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/PhotoSwipe/PhotoSwipe.tsx b/src/components/PhotoSwipe/PhotoSwipe.tsx index e7e39c2b0..050a2a482 100644 --- a/src/components/PhotoSwipe/PhotoSwipe.tsx +++ b/src/components/PhotoSwipe/PhotoSwipe.tsx @@ -44,6 +44,7 @@ import DatePicker from 'react-datepicker'; import 'react-datepicker/dist/react-datepicker.css'; import CloseIcon from 'components/icons/CloseIcon'; import TickIcon from 'components/icons/TickIcon'; +import { FreeFlowText } from 'components/RecoveryKeyModal'; interface Iprops { isOpen: boolean; @@ -257,7 +258,7 @@ function RenderFileName({ )}
) : ( - <>{newFileName} + {newFileName} )}
Date: Tue, 9 Nov 2021 15:31:09 +0530 Subject: [PATCH 09/43] update EnteSpinner to use props passed to it --- src/components/EnteSpinner.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/EnteSpinner.tsx b/src/components/EnteSpinner.tsx index 35f195675..054a6c62f 100644 --- a/src/components/EnteSpinner.tsx +++ b/src/components/EnteSpinner.tsx @@ -2,16 +2,18 @@ import React from 'react'; import { Spinner } from 'react-bootstrap'; export default function EnteSpinner(props) { + const { style, ...others } = props ?? {}; return ( ); From 4f3faf3d780ab7d8e69e83514aacf9e33b02d71a Mon Sep 17 00:00:00 2001 From: Abhinav Date: Tue, 9 Nov 2021 15:32:27 +0530 Subject: [PATCH 10/43] use formik for file name update form state management --- src/components/PhotoSwipe/PhotoSwipe.tsx | 206 ++++++++++++++++------- src/pages/_app.tsx | 3 + 2 files changed, 145 insertions(+), 64 deletions(-) diff --git a/src/components/PhotoSwipe/PhotoSwipe.tsx b/src/components/PhotoSwipe/PhotoSwipe.tsx index 050a2a482..e6f58e8a3 100644 --- a/src/components/PhotoSwipe/PhotoSwipe.tsx +++ b/src/components/PhotoSwipe/PhotoSwipe.tsx @@ -28,7 +28,7 @@ import { formatDateTime, updateExistingFilePubMetadata, } from 'utils/file'; -import { FormCheck } from 'react-bootstrap'; +import { Col, Form, FormCheck, FormControl } from 'react-bootstrap'; import { prettyPrintExif } from 'utils/exif'; import EditIcon from 'components/icons/EditIcon'; import { @@ -45,6 +45,11 @@ import 'react-datepicker/dist/react-datepicker.css'; import CloseIcon from 'components/icons/CloseIcon'; import TickIcon from 'components/icons/TickIcon'; import { FreeFlowText } from 'components/RecoveryKeyModal'; +import { Formik } from 'formik'; +import * as Yup from 'yup'; +import EnteSpinner from 'components/EnteSpinner'; +import { extensions } from 'file-type'; +import { FileExtension } from 'file-type/core'; interface Iprops { isOpen: boolean; @@ -76,13 +81,6 @@ const Pre = styled.pre` padding: 7px 15px; `; -const WarningMessage = styled.div` - width: 100%; - margin-top: 0.25rem; - font-size: 80%; - color: #dc3545; -`; - const renderInfoItem = (label: string, value: string | JSX.Element) => ( @@ -192,6 +190,106 @@ function RenderCreationTime({ ); } +const getFileTitle = (filename, extension) => { + if (extension) { + return filename + '.' + extension; + } else { + return filename; + } +}; +interface formValues { + filename: string; +} + +const FileNameEditForm = ({ filename, saveEdits, discardEdits, extension }) => { + const [loading, setLoading] = useState(false); + + const onSubmit = async (values: formValues) => { + try { + setLoading(true); + await saveEdits(values.filename); + } finally { + setLoading(false); + } + }; + return ( + + initialValues={{ filename }} + validationSchema={Yup.object().shape({ + filename: Yup.string() + .required(constants.REQUIRED) + .max( + MAX_EDITED_FILE_NAME_LENGTH, + constants.FILE_NAME_CHARACTER_LIMIT + ), + })} + validateOnBlur={false} + onSubmit={onSubmit}> + {({ values, errors, handleChange, handleSubmit }) => ( +
+ + + + + {errors.filename} + + + {extension && ( + + + {`.${extension}`} + + + )} + + + {loading ? ( + + ) : ( + + )} + + + + + + + + +
+ )} + + ); +}; function RenderFileName({ file, @@ -200,22 +298,26 @@ function RenderFileName({ file: File; scheduleUpdate: () => void; }) { - const originalFileName = file?.metadata.title; + const originalTitle = file?.metadata.title; const [isInEditMode, setIsInEditMode] = useState(false); - - const [newFileName, setNewFileName] = useState(originalFileName); - + let [originalFileName, extension] = originalTitle?.split('.', 2); + if (extension && !extensions.has(extension as FileExtension)) { + originalFileName = originalTitle; + } + const [filename, setFilename] = useState(originalFileName); const openEditMode = () => setIsInEditMode(true); const closeEditMode = () => setIsInEditMode(false); - const saveEdits = async () => { + const saveEdits = async (newFilename: string) => { try { - if (isInEditMode && file) { - if (newFileName === originalFileName) { + if (file) { + if (filename === newFilename) { closeEditMode(); return; } - let updatedFile = await changeFileName(file, newFileName); + setFilename(newFilename); + const newTitle = getFileTitle(newFilename, extension); + let updatedFile = await changeFileName(file, newTitle); updatedFile = ( await updatePublicMagicMetadata([updatedFile]) )[0]; @@ -224,61 +326,37 @@ function RenderFileName({ } } catch (e) { logError(e, 'failed to update file name'); - } - closeEditMode(); - }; - const discardEdits = () => { - setNewFileName(originalFileName); - closeEditMode(); - }; - const handleChange = (event) => { - const newName = event.target.value.replace(/(\r\n|\n|\r)/gm, ''); - if (newName.length <= MAX_EDITED_FILE_NAME_LENGTH) { - setNewFileName(event.target.value); + } finally { + closeEditMode(); } }; return ( <> - - {isInEditMode ? ( -
- - {newFileName.length === - MAX_EDITED_FILE_NAME_LENGTH && ( - - {constants.FILE_NAME_CHARACTER_LIMIT} - - )} -
- ) : ( - {newFileName} - )} -
- - {isInEditMode ? ( - - - + {!isInEditMode ? ( + <> + + + {getFileTitle(filename, extension)} + + + + + - - - - - ) : ( - - - - )} - +
+ + ) : ( + + )}
); diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index b324f1eea..8ba26963f 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -446,6 +446,9 @@ const GlobalStyles = createGlobalStyle` .react-datepicker__day--disabled:hover { background-color: #202020; } + .ente-form-group{ + margin:0; + } `; export const LogoImage = styled.img` From d807828b778ae0fb707d893c83d4efb0731eef2d Mon Sep 17 00:00:00 2001 From: Abhinav Date: Tue, 9 Nov 2021 15:43:10 +0530 Subject: [PATCH 11/43] remove valid file extension detection logic --- src/components/PhotoSwipe/PhotoSwipe.tsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/components/PhotoSwipe/PhotoSwipe.tsx b/src/components/PhotoSwipe/PhotoSwipe.tsx index e6f58e8a3..bc0138193 100644 --- a/src/components/PhotoSwipe/PhotoSwipe.tsx +++ b/src/components/PhotoSwipe/PhotoSwipe.tsx @@ -48,8 +48,6 @@ import { FreeFlowText } from 'components/RecoveryKeyModal'; import { Formik } from 'formik'; import * as Yup from 'yup'; import EnteSpinner from 'components/EnteSpinner'; -import { extensions } from 'file-type'; -import { FileExtension } from 'file-type/core'; interface Iprops { isOpen: boolean; @@ -300,10 +298,7 @@ function RenderFileName({ }) { const originalTitle = file?.metadata.title; const [isInEditMode, setIsInEditMode] = useState(false); - let [originalFileName, extension] = originalTitle?.split('.', 2); - if (extension && !extensions.has(extension as FileExtension)) { - originalFileName = originalTitle; - } + const [originalFileName, extension] = originalTitle?.split('.', 2); const [filename, setFilename] = useState(originalFileName); const openEditMode = () => setIsInEditMode(true); const closeEditMode = () => setIsInEditMode(false); From 1dc5ecb0615caae6fc72bdcb438152d9d1ee6020 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Tue, 9 Nov 2021 15:59:37 +0530 Subject: [PATCH 12/43] minor improvments --- src/components/PhotoSwipe/PhotoSwipe.tsx | 50 ++++++++++++------------ 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/src/components/PhotoSwipe/PhotoSwipe.tsx b/src/components/PhotoSwipe/PhotoSwipe.tsx index bc0138193..92a455e7a 100644 --- a/src/components/PhotoSwipe/PhotoSwipe.tsx +++ b/src/components/PhotoSwipe/PhotoSwipe.tsx @@ -229,8 +229,7 @@ const FileNameEditForm = ({ filename, saveEdits, discardEdits, extension }) => { + xs={extension ? 7 : 8}> { )} - - - {loading ? ( - - ) : ( - - )} - - - - - + + + {loading ? ( + + ) : ( + + )} + + + + + @@ -298,7 +293,10 @@ function RenderFileName({ }) { const originalTitle = file?.metadata.title; const [isInEditMode, setIsInEditMode] = useState(false); - const [originalFileName, extension] = originalTitle?.split('.', 2); + const [originalFileName, extension] = originalTitle?.split('.', 2) ?? [ + undefined, + undefined, + ]; const [filename, setFilename] = useState(originalFileName); const openEditMode = () => setIsInEditMode(true); const closeEditMode = () => setIsInEditMode(false); From b225b4ff353e7c6fcfe4bac4c07a18eafb7601b2 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Tue, 9 Nov 2021 16:06:12 +0530 Subject: [PATCH 13/43] simpley close date edit if saved without edit --- src/components/PhotoSwipe/PhotoSwipe.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/PhotoSwipe/PhotoSwipe.tsx b/src/components/PhotoSwipe/PhotoSwipe.tsx index e688b0afd..5b0d691fd 100644 --- a/src/components/PhotoSwipe/PhotoSwipe.tsx +++ b/src/components/PhotoSwipe/PhotoSwipe.tsx @@ -98,7 +98,8 @@ function RenderCreationTime({ try { if (isInEditMode && file) { const unixTimeInMicroSec = pickedTime.getTime() * 1000; - if (unixTimeInMicroSec === file.metadata.creationTime) { + if (unixTimeInMicroSec === file?.metadata.creationTime) { + closeEditMode(); return; } let updatedFile = await changeFileCreationTime( From 3c5e74f16d911a1056f8307bde40e947aaa96e8c Mon Sep 17 00:00:00 2001 From: Abhinav Date: Tue, 9 Nov 2021 16:34:44 +0530 Subject: [PATCH 14/43] update the files array so that returned files from the function is sorted as the caller expect sorted files and use spread operator so that deep copy of file is used to set state to avoid #212 problem --- src/services/fileService.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/services/fileService.ts b/src/services/fileService.ts index 09570391a..a78321d3c 100644 --- a/src/services/fileService.ts +++ b/src/services/fileService.ts @@ -147,8 +147,9 @@ export const syncFiles = async ( let files = await removeDeletedCollectionFiles(collections, localFiles); if (files.length !== localFiles.length) { await setLocalFiles(files); - setFiles(sortFiles(mergeMetadata(files))); } + files = sortFiles(mergeMetadata(files)); + setFiles([...files]); for (const collection of collections) { if (!getToken()) { continue; @@ -183,9 +184,10 @@ export const syncFiles = async ( `${collection.id}-time`, collection.updationTime ); - setFiles(sortFiles(mergeMetadata(files))); + files = sortFiles(mergeMetadata(files)); + setFiles([...files]); } - return mergeMetadata(files); + return files; }; export const getFiles = async ( From 15f12a6f8ebf851cd0de27428c01b0f8c259aa7e Mon Sep 17 00:00:00 2001 From: Abhinav Date: Tue, 9 Nov 2021 22:33:09 +0530 Subject: [PATCH 15/43] fix filename spliting --- src/components/PhotoSwipe/PhotoSwipe.tsx | 7 +++---- src/utils/file/index.ts | 10 ++++++++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/components/PhotoSwipe/PhotoSwipe.tsx b/src/components/PhotoSwipe/PhotoSwipe.tsx index 92a455e7a..96b2e5b13 100644 --- a/src/components/PhotoSwipe/PhotoSwipe.tsx +++ b/src/components/PhotoSwipe/PhotoSwipe.tsx @@ -26,6 +26,7 @@ import { changeFileName, downloadFile, formatDateTime, + splitFilenameAndExtension, updateExistingFilePubMetadata, } from 'utils/file'; import { Col, Form, FormCheck, FormControl } from 'react-bootstrap'; @@ -293,10 +294,8 @@ function RenderFileName({ }) { const originalTitle = file?.metadata.title; const [isInEditMode, setIsInEditMode] = useState(false); - const [originalFileName, extension] = originalTitle?.split('.', 2) ?? [ - undefined, - undefined, - ]; + const [originalFileName, extension] = + splitFilenameAndExtension(originalTitle); const [filename, setFilename] = useState(originalFileName); const openEditMode = () => setIsInEditMode(true); const closeEditMode = () => setIsInEditMode(false); diff --git a/src/utils/file/index.ts b/src/utils/file/index.ts index 221fda554..5ea88f252 100644 --- a/src/utils/file/index.ts +++ b/src/utils/file/index.ts @@ -240,6 +240,16 @@ export function fileExtensionWithDot(filename) { else return filename.substr(lastDotPosition); } +export function splitFilenameAndExtension(filename): [string, string] { + const lastDotPosition = filename.lastIndexOf('.'); + if (lastDotPosition === -1) return [filename, null]; + else + return [ + filename.substr(0, lastDotPosition), + filename.substr(lastDotPosition + 1), + ]; +} + export function generateStreamFromArrayBuffer(data: Uint8Array) { return new ReadableStream({ async start(controller: ReadableStreamDefaultController) { From 270c43521d51afbc1e29d10c502103e456d87031 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 10 Nov 2021 10:21:29 +0530 Subject: [PATCH 16/43] better solution to avoid saving merged metadata to local storage --- src/services/fileService.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/services/fileService.ts b/src/services/fileService.ts index a78321d3c..d5b5c9d52 100644 --- a/src/services/fileService.ts +++ b/src/services/fileService.ts @@ -147,9 +147,8 @@ export const syncFiles = async ( let files = await removeDeletedCollectionFiles(collections, localFiles); if (files.length !== localFiles.length) { await setLocalFiles(files); + setFiles([...sortFiles(mergeMetadata(files))]); } - files = sortFiles(mergeMetadata(files)); - setFiles([...files]); for (const collection of collections) { if (!getToken()) { continue; @@ -184,10 +183,9 @@ export const syncFiles = async ( `${collection.id}-time`, collection.updationTime ); - files = sortFiles(mergeMetadata(files)); - setFiles([...files]); + setFiles([...sortFiles(mergeMetadata(files))]); } - return files; + return sortFiles(mergeMetadata(files)); }; export const getFiles = async ( From 4b27ed4fd52fdfed90a1c3ea3435311ed8de53ff Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 10 Nov 2021 12:14:56 +0530 Subject: [PATCH 17/43] updated info object key from `type` to `fileFormat` , as sentry does not recognise `type` as valid property name for some reason --- src/services/upload/thumbnailService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/upload/thumbnailService.ts b/src/services/upload/thumbnailService.ts index f73d177d1..f4c8fd855 100644 --- a/src/services/upload/thumbnailService.ts +++ b/src/services/upload/thumbnailService.ts @@ -56,7 +56,7 @@ export async function generateThumbnail( } } catch (e) { logError(e, 'uploading static thumbnail', { - type: fileTypeInfo.exactType, + fileFormat: fileTypeInfo.exactType, }); thumbnail = Uint8Array.from(atob(BLACK_THUMBNAIL_BASE64), (c) => c.charCodeAt(0) From 87089eec438c09b1a692da57f6d5d2dee944027c Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 10 Nov 2021 13:46:16 +0530 Subject: [PATCH 18/43] adds logic to refresh state on repen --- src/components/FixCreationTime.tsx | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/components/FixCreationTime.tsx b/src/components/FixCreationTime.tsx index db1295d04..e961a599f 100644 --- a/src/components/FixCreationTime.tsx +++ b/src/components/FixCreationTime.tsx @@ -75,6 +75,28 @@ export default function FixCreationTime(props: Props) { init(); }, []); + const main = async () => { + const filesToBeUpdated = await getFilesPendingCreationTimeUpdate(); + if (fixState === FIX_STATE.NOT_STARTED && filesToBeUpdated.length > 0) { + props.show(); + } + if ( + (fixState === FIX_STATE.COMPLETED || fixState === FIX_STATE.NOOP) && + filesToBeUpdated.length > 0 + ) { + updateFixState(FIX_STATE.NOT_STARTED); + } + if (filesToBeUpdated.length === 0 && fixState !== FIX_STATE.NOOP) { + updateFixState(FIX_STATE.NOOP); + } + }; + + useEffect(() => { + if (props.isOpen && fixState !== FIX_STATE.RUNNING) { + main(); + } + }, [props.isOpen]); + const startFix = async () => { updateFixState(FIX_STATE.RUNNING); const filesToBeUpdated = await getFilesPendingCreationTimeUpdate(); From 3d3213e989d350ea3953e44e761fa1307c78fdd0 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 10 Nov 2021 13:47:21 +0530 Subject: [PATCH 19/43] replace exif only if creationTime different than exif --- src/services/updateCreationTimeWithExif.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/services/updateCreationTimeWithExif.ts b/src/services/updateCreationTimeWithExif.ts index f0c6b169d..ac9199b26 100644 --- a/src/services/updateCreationTimeWithExif.ts +++ b/src/services/updateCreationTimeWithExif.ts @@ -53,7 +53,10 @@ export async function updateCreationTimeWithExif( try { const fileURL = await downloadManager.getFile(file); const exifData = await getExifDataFromURL(fileURL); - if (exifData?.creationTime) { + if ( + exifData?.creationTime && + exifData?.creationTime !== file.metadata.creationTime + ) { let updatedFile = await changeFileCreationTime( file, exifData.creationTime From c47d24eb8c0e5018c717741ad77f0a72c696997b Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 10 Nov 2021 14:52:40 +0530 Subject: [PATCH 20/43] removed unneccessary getExifDataFromURL --- src/services/upload/exifService.ts | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/services/upload/exifService.ts b/src/services/upload/exifService.ts index 96ef1e899..004958b6f 100644 --- a/src/services/upload/exifService.ts +++ b/src/services/upload/exifService.ts @@ -30,23 +30,6 @@ export async function getExifData( return parsedEXIFData; } -export async function getExifDataFromURL(url: string): Promise { - try { - const exifData = await exifr.parse(url, EXIF_TAGS_NEEDED); - if (!exifData) { - return { location: NULL_LOCATION, creationTime: null }; - } - const parsedEXIFData = { - location: getEXIFLocation(exifData), - creationTime: getUNIXTime(exifData), - }; - return parsedEXIFData; - } catch (e) { - logError(e, 'error reading exif data'); - // ignore exif parsing errors - } -} - function getUNIXTime(exifData: any) { const dateTime: Date = exifData.DateTimeOriginal ?? exifData.CreateDate ?? exifData.ModifyDate; From 88f643192cf6bf553498453d7f1c7f657de2d31a Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 10 Nov 2021 14:53:23 +0530 Subject: [PATCH 21/43] fix getExifData breaking for some files --- src/services/updateCreationTimeWithExif.ts | 6 ++++-- src/utils/file/index.ts | 6 ++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/services/updateCreationTimeWithExif.ts b/src/services/updateCreationTimeWithExif.ts index ac9199b26..482718f72 100644 --- a/src/services/updateCreationTimeWithExif.ts +++ b/src/services/updateCreationTimeWithExif.ts @@ -1,6 +1,7 @@ import { SetProgressTracker } from 'components/FixLargeThumbnail'; import { changeFileCreationTime, + getFileFromURL, updateExistingFilePubMetadata, } from 'utils/file'; import { logError } from 'utils/sentry'; @@ -8,7 +9,7 @@ import localForage from 'utils/storage/localForage'; import downloadManager from './downloadManager'; import { getLocalFiles, File, updatePublicMagicMetadata } from './fileService'; import { getLocalTrash, getTrashedFiles } from './trashService'; -import { getExifDataFromURL } from './upload/exifService'; +import { getExifData } from './upload/exifService'; const CREATION_TIME_UPDATED_FILES_TABLE = 'creation-time-updated-file-table'; @@ -52,7 +53,8 @@ export async function updateCreationTimeWithExif( for (const [index, file] of filesToBeUpdated.entries()) { try { const fileURL = await downloadManager.getFile(file); - const exifData = await getExifDataFromURL(fileURL); + const fileObject = await getFileFromURL(fileURL); + const exifData = await getExifData(fileObject); if ( exifData?.creationTime && exifData?.creationTime !== file.metadata.creationTime diff --git a/src/utils/file/index.ts b/src/utils/file/index.ts index 5ea88f252..258984c94 100644 --- a/src/utils/file/index.ts +++ b/src/utils/file/index.ts @@ -438,3 +438,9 @@ export function updateExistingFilePubMetadata( existingFile.pubMagicMetadata = updatedFile.pubMagicMetadata; existingFile.metadata = mergeMetadata([existingFile])[0].metadata; } + +export async function getFileFromURL(fileURL: string) { + const fileBlob = await (await fetch(fileURL)).blob(); + const fileFile = new globalThis.File([fileBlob], 'temp'); + return fileFile; +} From 088d7e2156dff86951b89d8217d8a9567d52565e Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 10 Nov 2021 14:55:29 +0530 Subject: [PATCH 22/43] sync after fix creation time completion --- src/components/FixCreationTime.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/FixCreationTime.tsx b/src/components/FixCreationTime.tsx index e961a599f..962f72fa6 100644 --- a/src/components/FixCreationTime.tsx +++ b/src/components/FixCreationTime.tsx @@ -1,6 +1,6 @@ import constants from 'utils/strings/constants'; import MessageDialog from './MessageDialog'; -import React, { useEffect, useState } from 'react'; +import React, { useContext, useEffect, useState } from 'react'; import { ProgressBar, Button } from 'react-bootstrap'; import { ComfySpan } from './ExportInProgress'; import { getData, LS_KEYS, setData } from 'utils/storage/localStorage'; @@ -8,6 +8,7 @@ import { getFilesPendingCreationTimeUpdate, updateCreationTimeWithExif, } from 'services/updateCreationTimeWithExif'; +import { GalleryContext } from 'pages/gallery'; interface Props { isOpen: boolean; @@ -51,6 +52,7 @@ export default function FixCreationTime(props: Props) { current: 0, total: 0, }); + const galleryContext = useContext(GalleryContext); const updateFixState = (fixState: FIX_STATE) => { setFixState(fixState); @@ -109,6 +111,7 @@ export default function FixCreationTime(props: Props) { } else { updateFixState(FIX_STATE.COMPLETED_WITH_ERRORS); } + await galleryContext.syncWithRemote(); }; return ( From 04ab5a1651a339649586127952190580c7c7b001 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 10 Nov 2021 15:02:36 +0530 Subject: [PATCH 23/43] dont create inline function for startFix --- src/components/FixCreationTime.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/FixCreationTime.tsx b/src/components/FixCreationTime.tsx index 962f72fa6..0e7d1a8c9 100644 --- a/src/components/FixCreationTime.tsx +++ b/src/components/FixCreationTime.tsx @@ -196,7 +196,7 @@ export default function FixCreationTime(props: Props) { From b0aeb0fd3d58000b735e5b1f562cc713f298bac2 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 10 Nov 2021 16:41:34 +0530 Subject: [PATCH 24/43] hostapple-app-site-association file --- public/.well-known/apple-app-site-association | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 public/.well-known/apple-app-site-association diff --git a/public/.well-known/apple-app-site-association b/public/.well-known/apple-app-site-association new file mode 100644 index 000000000..8e87b1279 --- /dev/null +++ b/public/.well-known/apple-app-site-association @@ -0,0 +1,7 @@ +{ + "webcredentials": { + "apps": [ + "2BUSYC7FN9.io.ente.frame" + ] + } +} \ No newline at end of file From f1213c2f31661a32adfce2e27df471b5e7754b55 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Thu, 11 Nov 2021 13:30:05 +0530 Subject: [PATCH 25/43] Add digital assetlink for android --- public/.well-known/assetlinks.json | 35 ++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 public/.well-known/assetlinks.json diff --git a/public/.well-known/assetlinks.json b/public/.well-known/assetlinks.json new file mode 100644 index 000000000..5a178a45a --- /dev/null +++ b/public/.well-known/assetlinks.json @@ -0,0 +1,35 @@ +[ + { + "relation": [ + "delegate_permission/common.get_login_creds" + ], + "target": { + "namespace": "web", + "site": "https://web.ente.io" + } + }, + { + "relation": [ + "delegate_permission/common.get_login_creds" + ], + "target": { + "namespace": "android_app", + "package_name": "io.ente.photos.independent", + "sha256_cert_fingerprints": [ + "35:ED:56:81:B7:0B:B3:BD:35:D9:0D:85:6A:F5:69:4C:50:4D:EF:46:AA:D8:3F:77:7B:1C:67:5C:F4:51:35:0B" + ] + } + }, + { + "relation": [ + "delegate_permission/common.get_login_creds" + ], + "target": { + "namespace": "android_app", + "package_name": "io.ente.photos", + "sha256_cert_fingerprints": [ + "35:ED:56:81:B7:0B:B3:BD:35:D9:0D:85:6A:F5:69:4C:50:4D:EF:46:AA:D8:3F:77:7B:1C:67:5C:F4:51:35:0B" + ] + } + } +] \ No newline at end of file From 07591fe30dea4154ea2bb3248ec92cd148bd2d85 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Fri, 12 Nov 2021 10:39:36 +0530 Subject: [PATCH 26/43] update creation Time copies --- src/utils/strings/englishConstants.tsx | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/utils/strings/englishConstants.tsx b/src/utils/strings/englishConstants.tsx index 826d0776a..035742aa0 100644 --- a/src/utils/strings/englishConstants.tsx +++ b/src/utils/strings/englishConstants.tsx @@ -596,21 +596,18 @@ const englishConstants = { REPLACE_THUMBNAIL_COMPLETED_WITH_ERROR: () => ( <>could not compress some of your thumbnails, please retry ), - FIX_CREATION_TIME: 'fix creation time', - CREATION_TIME_UPDATED: `file's creation time updated`, + FIX_CREATION_TIME: 'fix time', + CREATION_TIME_UPDATED: `file time updated`, UPDATE_CREATION_TIME_NOT_STARTED: () => ( - <>update creation time with exif values + <>update file time with values from EXIF ), UPDATE_CREATION_TIME_COMPLETED: () => <>successfully updated all files, UPDATE_CREATION_TIME_NOOP: () => ( - <> - you have no files that whose CreationTime is not updated with exif - value - + <>you have no files that whose time is out of sync with exif ), UPDATE_CREATION_TIME_COMPLETED_WITH_ERROR: () => ( - <>updating creation time failed for some files ,try again + <>file time updation failed for some files, please retry ), FIX_CREATION_TIME_LATER: 'update later', FILE_NAME_CHARACTER_LIMIT: '100 characters max', From 9b333807a289d88d83018a74f811c85e122cba64 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Sat, 13 Nov 2021 00:23:05 +0530 Subject: [PATCH 27/43] Add support to show mnemonic recovery key --- package.json | 1 + src/components/RecoveryKeyModal.tsx | 6 ++++-- src/pages/recover/index.tsx | 10 ++++++++++ yarn.lock | 17 ++++++++++++++++- 4 files changed, 31 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 0f589b5a6..b2ce5421a 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "@typescript-eslint/eslint-plugin": "^4.25.0", "@typescript-eslint/parser": "^4.25.0", "axios": "^0.21.3", + "bip39": "^3.0.4", "bootstrap": "^4.5.2", "chrono-node": "^2.2.6", "comlink": "^4.3.0", diff --git a/src/components/RecoveryKeyModal.tsx b/src/components/RecoveryKeyModal.tsx index d4cd01682..6a4afffb9 100644 --- a/src/components/RecoveryKeyModal.tsx +++ b/src/components/RecoveryKeyModal.tsx @@ -5,7 +5,9 @@ import constants from 'utils/strings/constants'; import MessageDialog from './MessageDialog'; import EnteSpinner from './EnteSpinner'; import styled from 'styled-components'; - +const bip39 = require('bip39'); +// mobile client library only supports english. +bip39.setDefaultWordlist('english'); export const CodeBlock = styled.div<{ height: number }>` display: flex; align-items: center; @@ -42,7 +44,7 @@ function RecoveryKeyModal({ somethingWentWrong, ...props }: Props) { somethingWentWrong(); props.onHide(); } - setRecoveryKey(recoveryKey); + setRecoveryKey(bip39.entropyToMnemonic(recoveryKey)); }; main(); }, [props.show]); diff --git a/src/pages/recover/index.tsx b/src/pages/recover/index.tsx index 898841b08..fdfe7fbb8 100644 --- a/src/pages/recover/index.tsx +++ b/src/pages/recover/index.tsx @@ -21,6 +21,9 @@ import LogoImg from 'components/LogoImg'; import { logError } from 'utils/sentry'; import { getKey, SESSION_KEYS } from 'utils/storage/sessionStorage'; import { User } from 'services/userService'; +const bip39 = require('bip39'); +// mobile client library only supports english. +bip39.setDefaultWordlist('english'); export default function Recover() { const router = useRouter(); @@ -51,6 +54,13 @@ export default function Recover() { const recover = async (recoveryKey: string, setFieldError) => { try { + // check if user is entering mnemonic recovery key + if (recoveryKey.trim().indexOf(' ') > 0) { + if (recoveryKey.trim().split(' ').length !== 24) { + throw new Error('recovery code should have 24 words'); + } + recoveryKey = bip39.mnemonicToEntropy(recoveryKey); + } const cryptoWorker = await new CryptoWorker(); const masterKey: string = await cryptoWorker.decryptB64( keyAttributes.masterKeyEncryptedWithRecoveryKey, diff --git a/yarn.lock b/yarn.lock index 143b09bb9..580621f0a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1453,6 +1453,11 @@ resolved "https://registry.npmjs.org/@types/node/-/node-16.9.1.tgz" integrity sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g== +"@types/node@11.11.6": + version "11.11.6" + resolved "https://registry.yarnpkg.com/@types/node/-/node-11.11.6.tgz#df929d1bb2eee5afdda598a41930fe50b43eaa6a" + integrity sha512-Exw4yUWMBXM3X+8oqzJNRqZSwUAaS4+7NdvHqQuFi/d+synz++xmX3QIf+BFqneW8N31R8Ky+sikfZUXq07ggQ== + "@types/node@^14.6.4": version "14.17.15" resolved "https://registry.npmjs.org/@types/node/-/node-14.17.15.tgz" @@ -2085,6 +2090,16 @@ binary-extensions@^2.0.0: resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== +bip39@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/bip39/-/bip39-3.0.4.tgz#5b11fed966840b5e1b8539f0f54ab6392969b2a0" + integrity sha512-YZKQlb752TrUWqHWj7XAwCSjYEgGAk+/Aas3V7NyjQeZYsztO8JnQUaCWhcnL4T+jL8nvB8typ2jRPzTlgugNw== + dependencies: + "@types/node" "11.11.6" + create-hash "^1.1.0" + pbkdf2 "^3.0.9" + randombytes "^2.0.1" + bluebird@^3.5.5: version "3.7.2" resolved "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz" @@ -5016,7 +5031,7 @@ path-type@^4.0.0: resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -pbkdf2@^3.0.3: +pbkdf2@^3.0.3, pbkdf2@^3.0.9: version "3.1.2" resolved "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz" integrity sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA== From e8aa6141dd65b096f2d6351fce7fcfbfea5b9631 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Thu, 11 Nov 2021 17:57:42 +0530 Subject: [PATCH 28/43] bubble up error to caller from queue processor --- src/services/ffmpegService.ts | 15 ++++++++++----- src/services/upload/queueProcessor.ts | 16 ++++++++++------ src/utils/common/errorUtil.ts | 1 + 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/services/ffmpegService.ts b/src/services/ffmpegService.ts index 4cfcdf9be..2b4bf2970 100644 --- a/src/services/ffmpegService.ts +++ b/src/services/ffmpegService.ts @@ -33,12 +33,17 @@ class FFmpegService { const response = this.generateThumbnailProcessor.queueUpRequest( generateThumbnailHelper.bind(null, this.ffmpeg, file) ); - - const thumbnail = await response.promise; - if (!thumbnail) { - throw Error(CustomError.THUMBNAIL_GENERATION_FAILED); + try { + return await response.promise; + } catch (e) { + if (e.message === CustomError.REQUEST_CANCELLED) { + // ignore + return null; + } else { + logError(e, 'ffmpeg thumbnail generation failed'); + throw e; + } } - return thumbnail; } } diff --git a/src/services/upload/queueProcessor.ts b/src/services/upload/queueProcessor.ts index 470659f6b..6d9e86fc6 100644 --- a/src/services/upload/queueProcessor.ts +++ b/src/services/upload/queueProcessor.ts @@ -1,6 +1,9 @@ +import { CustomError } from 'utils/common/errorUtil'; + interface RequestQueueItem { request: (canceller?: RequestCanceller) => Promise; - callback: (response) => void; + successCallback: (response: any) => void; + failureCallback: (error: Error) => void; isCanceled: { status: boolean }; canceller: { exec: () => void }; } @@ -26,10 +29,11 @@ export default class QueueProcessor { }, }; - const promise = new Promise((resolve) => { + const promise = new Promise((resolve, reject) => { this.requestQueue.push({ request, - callback: resolve, + successCallback: resolve, + failureCallback: reject, isCanceled, canceller, }); @@ -53,15 +57,15 @@ export default class QueueProcessor { let response = null; if (queueItem.isCanceled.status) { - response = null; + queueItem.failureCallback(Error(CustomError.REQUEST_CANCELLED)); } else { try { response = await queueItem.request(queueItem.canceller); + queueItem.successCallback(response); } catch (e) { - response = null; + queueItem.failureCallback(e); } } - queueItem.callback(response); } } } diff --git a/src/utils/common/errorUtil.ts b/src/utils/common/errorUtil.ts index 664388966..626f7fe58 100644 --- a/src/utils/common/errorUtil.ts +++ b/src/utils/common/errorUtil.ts @@ -28,6 +28,7 @@ export enum CustomError { FAV_COLLECTION_MISSING = 'favorite collection missing', INVALID_COLLECTION_OPERATION = 'invalid collection operation', WAIT_TIME_EXCEEDED = 'thumbnail generation wait time exceeded', + REQUEST_CANCELLED = 'request canceled', } function parseUploadError(error: AxiosResponse) { From e1b683734b24864a32f60f50234f9c177d3513ed Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Mon, 15 Nov 2021 21:39:32 +0530 Subject: [PATCH 29/43] Handle mneomonic recovery code during 2FA recover --- src/pages/two-factor/recover/index.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/pages/two-factor/recover/index.tsx b/src/pages/two-factor/recover/index.tsx index ee0219cdd..30f599eeb 100644 --- a/src/pages/two-factor/recover/index.tsx +++ b/src/pages/two-factor/recover/index.tsx @@ -12,6 +12,9 @@ import { logError } from 'utils/sentry'; import { recoverTwoFactor, removeTwoFactor } from 'services/userService'; import { AppContext, FLASH_MESSAGE_TYPE } from 'pages/_app'; import { PAGES } from 'types'; +const bip39 = require('bip39'); +// mobile client library only supports english. +bip39.setDefaultWordlist('english'); export default function Recover() { const router = useRouter(); @@ -43,6 +46,13 @@ export default function Recover() { const recover = async (recoveryKey: string, setFieldError) => { try { + // check if user is entering mnemonic recovery key + if (recoveryKey.trim().indexOf(' ') > 0) { + if (recoveryKey.trim().split(' ').length !== 24) { + throw new Error('recovery code should have 24 words'); + } + recoveryKey = bip39.mnemonicToEntropy(recoveryKey); + } const cryptoWorker = await new CryptoWorker(); const twoFactorSecret: string = await cryptoWorker.decryptB64( encryptedTwoFactorSecret.encryptedData, From f94322ddefe9bda8019b3fa0bb287478a0457322 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Tue, 16 Nov 2021 11:40:46 +0530 Subject: [PATCH 30/43] log the exifparsing inside getExifData itself --- src/services/upload/exifService.ts | 15 +++++++++++++-- src/services/upload/metadataService.ts | 9 +-------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/services/upload/exifService.ts b/src/services/upload/exifService.ts index 004958b6f..8589f45ee 100644 --- a/src/services/upload/exifService.ts +++ b/src/services/upload/exifService.ts @@ -1,6 +1,8 @@ import exifr from 'exifr'; +import { logError } from 'utils/sentry'; import { NULL_LOCATION, Location } from './metadataService'; +import { FileTypeInfo } from './readFileService'; const EXIF_TAGS_NEEDED = [ 'DateTimeOriginal', @@ -17,9 +19,18 @@ interface ParsedEXIFData { } export async function getExifData( - receivedFile: globalThis.File + receivedFile: globalThis.File, + fileTypeInfo: FileTypeInfo ): Promise { - const exifData = await exifr.parse(receivedFile, EXIF_TAGS_NEEDED); + let exifData; + try { + exifData = await exifr.parse(receivedFile, EXIF_TAGS_NEEDED); + } catch (e) { + logError(e, 'file missing exif data ', { + fileType: fileTypeInfo.exactType, + }); + // ignore exif parsing errors + } if (!exifData) { return { location: NULL_LOCATION, creationTime: null }; } diff --git a/src/services/upload/metadataService.ts b/src/services/upload/metadataService.ts index fab027ba6..305b0b202 100644 --- a/src/services/upload/metadataService.ts +++ b/src/services/upload/metadataService.ts @@ -34,14 +34,7 @@ export async function extractMetadata( ) { let exifData = null; if (fileTypeInfo.fileType === FILE_TYPE.IMAGE) { - try { - exifData = await getExifData(receivedFile); - } catch (e) { - logError(e, 'file missing exif data ', { - fileType: fileTypeInfo.exactType, - }); - // ignore exif parsing errors - } + exifData = await getExifData(receivedFile, fileTypeInfo); } const extractedMetadata: MetadataObject = { From 80fda73a7986e86b9237a6a861e0cb9f83742295 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Tue, 16 Nov 2021 12:27:23 +0530 Subject: [PATCH 31/43] move fix CreationTime option to selectionOption bar --- src/components/FixCreationTime.tsx | 147 +++++++----------- src/components/Sidebar.tsx | 14 -- .../pages/gallery/SelectedFileOptions.tsx | 5 + src/pages/gallery/index.tsx | 29 +++- src/services/updateCreationTimeWithExif.ts | 33 +--- src/utils/strings/englishConstants.tsx | 4 +- 6 files changed, 93 insertions(+), 139 deletions(-) diff --git a/src/components/FixCreationTime.tsx b/src/components/FixCreationTime.tsx index 0e7d1a8c9..a67c7fd43 100644 --- a/src/components/FixCreationTime.tsx +++ b/src/components/FixCreationTime.tsx @@ -3,22 +3,22 @@ import MessageDialog from './MessageDialog'; import React, { useContext, useEffect, useState } from 'react'; import { ProgressBar, Button } from 'react-bootstrap'; import { ComfySpan } from './ExportInProgress'; -import { getData, LS_KEYS, setData } from 'utils/storage/localStorage'; -import { - getFilesPendingCreationTimeUpdate, - updateCreationTimeWithExif, -} from 'services/updateCreationTimeWithExif'; +import { LS_KEYS, setData } from 'utils/storage/localStorage'; +import { updateCreationTimeWithExif } from 'services/updateCreationTimeWithExif'; import { GalleryContext } from 'pages/gallery'; +import { File } from 'services/fileService'; +export interface FixCreationTimeAttributes { + files: File[]; +} interface Props { isOpen: boolean; show: () => void; hide: () => void; + attributes: FixCreationTimeAttributes; } export enum FIX_STATE { NOT_STARTED, - FIX_LATER, - NOOP, RUNNING, COMPLETED, COMPLETED_WITH_ERRORS, @@ -27,24 +27,16 @@ function Message(props: { fixState: FIX_STATE }) { let message = null; switch (props.fixState) { case FIX_STATE.NOT_STARTED: - case FIX_STATE.FIX_LATER: message = constants.UPDATE_CREATION_TIME_NOT_STARTED(); break; case FIX_STATE.COMPLETED: message = constants.UPDATE_CREATION_TIME_COMPLETED(); break; - case FIX_STATE.NOOP: - message = constants.UPDATE_CREATION_TIME_NOOP(); - break; case FIX_STATE.COMPLETED_WITH_ERRORS: message = constants.UPDATE_CREATION_TIME_COMPLETED_WITH_ERROR(); break; } - return message ? ( -
{message}
- ) : ( - <> - ); + return message ?
{message}
: <>; } export default function FixCreationTime(props: Props) { const [fixState, setFixState] = useState(FIX_STATE.NOT_STARTED); @@ -59,51 +51,20 @@ export default function FixCreationTime(props: Props) { setData(LS_KEYS.CREATION_TIME_FIX_STATE, { state: fixState }); }; - const init = () => { - let fixState = getData(LS_KEYS.CREATION_TIME_FIX_STATE)?.state; - if (!fixState || fixState === FIX_STATE.RUNNING) { - fixState = FIX_STATE.NOT_STARTED; - updateFixState(fixState); - } - if (fixState === FIX_STATE.COMPLETED) { - fixState = FIX_STATE.NOOP; - updateFixState(fixState); - } - setFixState(fixState); - return fixState; - }; - useEffect(() => { - init(); - }, []); - - const main = async () => { - const filesToBeUpdated = await getFilesPendingCreationTimeUpdate(); - if (fixState === FIX_STATE.NOT_STARTED && filesToBeUpdated.length > 0) { - props.show(); - } if ( - (fixState === FIX_STATE.COMPLETED || fixState === FIX_STATE.NOOP) && - filesToBeUpdated.length > 0 + !props.attributes && + props.isOpen && + fixState !== FIX_STATE.RUNNING ) { - updateFixState(FIX_STATE.NOT_STARTED); - } - if (filesToBeUpdated.length === 0 && fixState !== FIX_STATE.NOOP) { - updateFixState(FIX_STATE.NOOP); - } - }; - - useEffect(() => { - if (props.isOpen && fixState !== FIX_STATE.RUNNING) { - main(); + setFixState(FIX_STATE.NOT_STARTED); } }, [props.isOpen]); const startFix = async () => { updateFixState(FIX_STATE.RUNNING); - const filesToBeUpdated = await getFilesPendingCreationTimeUpdate(); const completedWithoutError = await updateCreationTimeWithExif( - filesToBeUpdated, + props.attributes.files, setProgressTracker ); if (!completedWithoutError) { @@ -113,6 +74,9 @@ export default function FixCreationTime(props: Props) { } await galleryContext.syncWithRemote(); }; + if (!props.attributes) { + return <>; + } return (
)} -
- {fixState === FIX_STATE.NOT_STARTED || - fixState === FIX_STATE.FIX_LATER ? ( - - ) : ( - - )} - {(fixState === FIX_STATE.NOT_STARTED || - fixState === FIX_STATE.FIX_LATER || - fixState === FIX_STATE.COMPLETED_WITH_ERRORS) && ( - <> -
- + {fixState !== FIX_STATE.RUNNING && ( +
+ {(fixState === FIX_STATE.NOT_STARTED || + fixState === FIX_STATE.COMPLETED_WITH_ERRORS) && ( - - )} -
+ )} + {fixState === FIX_STATE.COMPLETED && ( + + )} + {(fixState === FIX_STATE.NOT_STARTED || + fixState === FIX_STATE.COMPLETED_WITH_ERRORS) && ( + <> +
+ + + + )} +
+ )}
); diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index 024599002..8d7358048 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -37,7 +37,6 @@ import { TRASH_SECTION, } from 'components/pages/gallery/Collections'; import FixLargeThumbnails from './FixLargeThumbnail'; -import FixCreationTime from './FixCreationTime'; interface Props { collections: Collection[]; setDialogMessage: SetDialogMessage; @@ -56,7 +55,6 @@ export default function Sidebar(props: Props) { const [twoFactorModalView, setTwoFactorModalView] = useState(false); const [exportModalView, setExportModalView] = useState(false); const [fixLargeThumbsView, setFixLargeThumbsView] = useState(false); - const [fixCreationTimeView, setFixCreationTimeView] = useState(false); const galleryContext = useContext(GalleryContext); useEffect(() => { const main = async () => { @@ -294,18 +292,6 @@ export default function Sidebar(props: Props) { {constants.FIX_LARGE_THUMBNAILS} - <> - setFixCreationTimeView(false)} - show={() => setFixCreationTimeView(true)} - /> - setFixCreationTimeView(true)}> - {constants.FIX_CREATION_TIME} - - diff --git a/src/components/pages/gallery/SelectedFileOptions.tsx b/src/components/pages/gallery/SelectedFileOptions.tsx index 2c9276954..9fbced565 100644 --- a/src/components/pages/gallery/SelectedFileOptions.tsx +++ b/src/components/pages/gallery/SelectedFileOptions.tsx @@ -27,6 +27,7 @@ interface Props { setCollectionSelectorAttributes: SetCollectionSelectorAttributes; deleteFileHelper: (permanent?: boolean) => void; removeFromCollectionHelper: () => void; + fixTimeHelper: () => void; count: number; clearSelection: () => void; archiveFilesHelper: () => void; @@ -68,6 +69,7 @@ const SelectedFileOptions = ({ restoreToCollectionHelper, showCreateCollectionModal, removeFromCollectionHelper, + fixTimeHelper, setDialogMessage, setCollectionSelectorAttributes, deleteFileHelper, @@ -210,6 +212,9 @@ const SelectedFileOptions = ({ + + {constants.FIX_CREATION_TIME} + )} diff --git a/src/pages/gallery/index.tsx b/src/pages/gallery/index.tsx index e16145f72..d949791a1 100644 --- a/src/pages/gallery/index.tsx +++ b/src/pages/gallery/index.tsx @@ -93,6 +93,9 @@ import { Trash, } from 'services/trashService'; import DeleteBtn from 'components/DeleteBtn'; +import FixCreationTime, { + FixCreationTimeAttributes, +} from 'components/FixCreationTime'; export const DeadCenter = styled.div` flex: 1; @@ -204,7 +207,9 @@ export default function Gallery() { useState>(); const [activeCollection, setActiveCollection] = useState(undefined); const [trash, setTrash] = useState([]); - + const [fixCreationTimeView, setFixCreationTimeView] = useState(false); + const [fixCreationTimeAttributes, setFixCreationTimeAttributes] = + useState(null); useEffect(() => { const key = getKey(SESSION_KEYS.ENCRYPTION_KEY); if (!key) { @@ -243,13 +248,13 @@ export default function Gallery() { useEffect(() => setDialogView(true), [dialogMessage]); - useEffect(() => { - if (collectionSelectorAttributes) { - setCollectionSelectorView(true); - } - }, [collectionSelectorAttributes]); + useEffect( + () => setCollectionSelectorView(true), + [collectionSelectorAttributes] + ); useEffect(() => setCollectionNamerView(true), [collectionNamerAttributes]); + useEffect(() => setFixCreationTimeView(true), [fixCreationTimeAttributes]); useEffect(() => { if (typeof activeCollection === 'undefined') { @@ -523,6 +528,11 @@ export default function Gallery() { } }; + const fixTimeHelper = async () => { + const selectedFiles = getSelectedFiles(selected, files); + setFixCreationTimeAttributes({ files: selectedFiles }); + }; + return ( + setFixCreationTimeView(false)} + show={() => setFixCreationTimeView(true)} + attributes={fixCreationTimeAttributes} + /> ( - CREATION_TIME_UPDATED_FILES_TABLE - )) ?? [] - ); -} - export async function setCreationTimeUpdatedFiles(creationTimeUpdatedFiles) { return await localForage.setItem( CREATION_TIME_UPDATED_FILES_TABLE, @@ -28,17 +21,6 @@ export async function setCreationTimeUpdatedFiles(creationTimeUpdatedFiles) { ); } -export async function getFilesPendingCreationTimeUpdate() { - const files = await getLocalFiles(); - const trash = await getLocalTrash(); - const trashFiles = getTrashedFiles(trash); - const allFiles = [...files, ...trashFiles]; - const updateFiles = new Set(await getCreationTimeUpdatedFiles()); - - const pendingFiles = allFiles.filter((file) => !updateFiles.has(file.id)); - return pendingFiles; -} - export async function updateCreationTimeWithExif( filesToBeUpdated: File[], setProgressTracker: SetProgressTracker @@ -48,13 +30,14 @@ export async function updateCreationTimeWithExif( if (filesToBeUpdated.length === 0) { return completedWithError; } - const updatedFiles = await getCreationTimeUpdatedFiles(); setProgressTracker({ current: 0, total: filesToBeUpdated.length }); for (const [index, file] of filesToBeUpdated.entries()) { try { const fileURL = await downloadManager.getFile(file); const fileObject = await getFileFromURL(fileURL); - const exifData = await getExifData(fileObject); + const worker = await new CryptoWorker(); + const fileTypeInfo = await getFileType(worker, fileObject); + const exifData = await getExifData(fileObject, fileTypeInfo); if ( exifData?.creationTime && exifData?.creationTime !== file.metadata.creationTime @@ -69,11 +52,9 @@ export async function updateCreationTimeWithExif( updateExistingFilePubMetadata(file, updatedFile); } setProgressTracker({ - current: index, + current: index + 1, total: filesToBeUpdated.length, }); - updatedFiles.push(file.id); - setCreationTimeUpdatedFiles(updatedFiles); } catch (e) { logError(e, 'failed to updated a CreationTime With Exif'); completedWithError = true; diff --git a/src/utils/strings/englishConstants.tsx b/src/utils/strings/englishConstants.tsx index 035742aa0..09bd5aaf3 100644 --- a/src/utils/strings/englishConstants.tsx +++ b/src/utils/strings/englishConstants.tsx @@ -603,9 +603,7 @@ const englishConstants = { <>update file time with values from EXIF ), UPDATE_CREATION_TIME_COMPLETED: () => <>successfully updated all files, - UPDATE_CREATION_TIME_NOOP: () => ( - <>you have no files that whose time is out of sync with exif - ), + UPDATE_CREATION_TIME_COMPLETED_WITH_ERROR: () => ( <>file time updation failed for some files, please retry ), From 638dab44211c34d635da690da9d6fcaad98ac5f2 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Tue, 16 Nov 2021 13:08:15 +0530 Subject: [PATCH 32/43] fix setting fixState on modal open --- src/components/FixCreationTime.tsx | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/components/FixCreationTime.tsx b/src/components/FixCreationTime.tsx index a67c7fd43..6e613c8fe 100644 --- a/src/components/FixCreationTime.tsx +++ b/src/components/FixCreationTime.tsx @@ -3,7 +3,6 @@ import MessageDialog from './MessageDialog'; import React, { useContext, useEffect, useState } from 'react'; import { ProgressBar, Button } from 'react-bootstrap'; import { ComfySpan } from './ExportInProgress'; -import { LS_KEYS, setData } from 'utils/storage/localStorage'; import { updateCreationTimeWithExif } from 'services/updateCreationTimeWithExif'; import { GalleryContext } from 'pages/gallery'; import { File } from 'services/fileService'; @@ -46,14 +45,9 @@ export default function FixCreationTime(props: Props) { }); const galleryContext = useContext(GalleryContext); - const updateFixState = (fixState: FIX_STATE) => { - setFixState(fixState); - setData(LS_KEYS.CREATION_TIME_FIX_STATE, { state: fixState }); - }; - useEffect(() => { if ( - !props.attributes && + props.attributes && props.isOpen && fixState !== FIX_STATE.RUNNING ) { @@ -62,15 +56,15 @@ export default function FixCreationTime(props: Props) { }, [props.isOpen]); const startFix = async () => { - updateFixState(FIX_STATE.RUNNING); + setFixState(FIX_STATE.RUNNING); const completedWithoutError = await updateCreationTimeWithExif( props.attributes.files, setProgressTracker ); if (!completedWithoutError) { - updateFixState(FIX_STATE.COMPLETED); + setFixState(FIX_STATE.COMPLETED); } else { - updateFixState(FIX_STATE.COMPLETED_WITH_ERRORS); + setFixState(FIX_STATE.COMPLETED_WITH_ERRORS); } await galleryContext.syncWithRemote(); }; From 70b3e59fbff8a6a09b6eaf974897036a87fb2a2c Mon Sep 17 00:00:00 2001 From: Abhinav Date: Tue, 16 Nov 2021 17:55:19 +0530 Subject: [PATCH 33/43] add fix time icon --- src/components/icons/ClockIcon.tsx | 20 +++++++++++++++++++ .../pages/gallery/SelectedFileOptions.tsx | 9 ++++++--- 2 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 src/components/icons/ClockIcon.tsx diff --git a/src/components/icons/ClockIcon.tsx b/src/components/icons/ClockIcon.tsx new file mode 100644 index 000000000..468d165a0 --- /dev/null +++ b/src/components/icons/ClockIcon.tsx @@ -0,0 +1,20 @@ +import React from 'react'; + +export default function ClockIcon(props) { + return ( + + + + ); +} + +ClockIcon.defaultProps = { + height: 20, + width: 20, + viewBox: '0 0 24 24', +}; diff --git a/src/components/pages/gallery/SelectedFileOptions.tsx b/src/components/pages/gallery/SelectedFileOptions.tsx index 9fbced565..b2a23cb13 100644 --- a/src/components/pages/gallery/SelectedFileOptions.tsx +++ b/src/components/pages/gallery/SelectedFileOptions.tsx @@ -17,6 +17,7 @@ import { OverlayTrigger } from 'react-bootstrap'; import { Collection } from 'services/collectionService'; import RemoveIcon from 'components/icons/RemoveIcon'; import RestoreIcon from 'components/icons/RestoreIcon'; +import ClockIcon from 'components/icons/ClockIcon'; interface Props { addToCollectionHelper: (collection: Collection) => void; @@ -212,9 +213,11 @@ const SelectedFileOptions = ({ - - {constants.FIX_CREATION_TIME} - + + + + + )} From ff58e8900dd1838ce3779d4dd2c8ac2d823b28fb Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 17 Nov 2021 10:22:30 +0530 Subject: [PATCH 34/43] clear selection after trigerring fix creation --- src/pages/gallery/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/gallery/index.tsx b/src/pages/gallery/index.tsx index d949791a1..3c0934bb6 100644 --- a/src/pages/gallery/index.tsx +++ b/src/pages/gallery/index.tsx @@ -531,6 +531,7 @@ export default function Gallery() { const fixTimeHelper = async () => { const selectedFiles = getSelectedFiles(selected, files); setFixCreationTimeAttributes({ files: selectedFiles }); + clearSelection(); }; return ( From 29ecff60d6a788ba38d198cd5b442db3bcfb0302 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 17 Nov 2021 10:29:40 +0530 Subject: [PATCH 35/43] reorder selection bar icons --- .../pages/gallery/SelectedFileOptions.tsx | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/components/pages/gallery/SelectedFileOptions.tsx b/src/components/pages/gallery/SelectedFileOptions.tsx index b2a23cb13..1b034abae 100644 --- a/src/components/pages/gallery/SelectedFileOptions.tsx +++ b/src/components/pages/gallery/SelectedFileOptions.tsx @@ -171,6 +171,16 @@ const SelectedFileOptions = ({ ) : ( <> + + + + + + + + + + {activeCollection === ARCHIVE_SECTION && ( @@ -185,11 +195,7 @@ const SelectedFileOptions = ({ )} - - - - - + {activeCollection !== ALL_SECTION && activeCollection !== ARCHIVE_SECTION && !isFavoriteCollection && ( @@ -213,11 +219,6 @@ const SelectedFileOptions = ({ - - - - - )} From d15cef404da9e65c419149b098e9f703aed77724 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 17 Nov 2021 13:05:41 +0530 Subject: [PATCH 36/43] update strings --- src/components/FixCreationTime.tsx | 5 ++++- src/utils/strings/englishConstants.tsx | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/components/FixCreationTime.tsx b/src/components/FixCreationTime.tsx index 6e613c8fe..25ff329ad 100644 --- a/src/components/FixCreationTime.tsx +++ b/src/components/FixCreationTime.tsx @@ -77,7 +77,10 @@ export default function FixCreationTime(props: Props) { show={props.isOpen} onHide={props.hide} attributes={{ - title: constants.FIX_CREATION_TIME, + title: + fixState === FIX_STATE.RUNNING + ? constants.FIX_CREATION_TIME_IN_PROGRESS + : constants.FIX_CREATION_TIME, staticBackdrop: true, nonClosable: true, }}> diff --git a/src/utils/strings/englishConstants.tsx b/src/utils/strings/englishConstants.tsx index 09bd5aaf3..69aa8d8aa 100644 --- a/src/utils/strings/englishConstants.tsx +++ b/src/utils/strings/englishConstants.tsx @@ -597,17 +597,17 @@ const englishConstants = { <>could not compress some of your thumbnails, please retry ), FIX_CREATION_TIME: 'fix time', + FIX_CREATION_TIME_IN_PROGRESS: 'fixing time', CREATION_TIME_UPDATED: `file time updated`, UPDATE_CREATION_TIME_NOT_STARTED: () => ( - <>update file time with values from EXIF + <>do you want to fix time with the values found in EXIF ), UPDATE_CREATION_TIME_COMPLETED: () => <>successfully updated all files, UPDATE_CREATION_TIME_COMPLETED_WITH_ERROR: () => ( <>file time updation failed for some files, please retry ), - FIX_CREATION_TIME_LATER: 'update later', FILE_NAME_CHARACTER_LIMIT: '100 characters max', }; From 80aa50b8fba7ee603a3478e5e1cdc4b4fc6af01c Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 17 Nov 2021 13:11:33 +0530 Subject: [PATCH 37/43] only try to parse image exif --- src/services/updateCreationTimeWithExif.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/services/updateCreationTimeWithExif.ts b/src/services/updateCreationTimeWithExif.ts index e3664b789..0cbaec809 100644 --- a/src/services/updateCreationTimeWithExif.ts +++ b/src/services/updateCreationTimeWithExif.ts @@ -8,7 +8,7 @@ import { import { logError } from 'utils/sentry'; import localForage from 'utils/storage/localForage'; import downloadManager from './downloadManager'; -import { File, updatePublicMagicMetadata } from './fileService'; +import { File, FILE_TYPE, updatePublicMagicMetadata } from './fileService'; import { getExifData } from './upload/exifService'; import { getFileType } from './upload/readFileService'; @@ -37,6 +37,9 @@ export async function updateCreationTimeWithExif( const fileObject = await getFileFromURL(fileURL); const worker = await new CryptoWorker(); const fileTypeInfo = await getFileType(worker, fileObject); + if (file.metadata.fileType !== FILE_TYPE.IMAGE) { + continue; + } const exifData = await getExifData(fileObject, fileTypeInfo); if ( exifData?.creationTime && From f8de2f21c7202ccea0c39845a536a7c9798ec541 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 17 Nov 2021 13:16:29 +0530 Subject: [PATCH 38/43] move setProgressTracker to finally block for any case update --- src/services/updateCreationTimeWithExif.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/services/updateCreationTimeWithExif.ts b/src/services/updateCreationTimeWithExif.ts index 0cbaec809..d91b1d7eb 100644 --- a/src/services/updateCreationTimeWithExif.ts +++ b/src/services/updateCreationTimeWithExif.ts @@ -54,13 +54,14 @@ export async function updateCreationTimeWithExif( )[0]; updateExistingFilePubMetadata(file, updatedFile); } + } catch (e) { + logError(e, 'failed to updated a CreationTime With Exif'); + completedWithError = true; + } finally { setProgressTracker({ current: index + 1, total: filesToBeUpdated.length, }); - } catch (e) { - logError(e, 'failed to updated a CreationTime With Exif'); - completedWithError = true; } } } catch (e) { From 84617a32679f47574e632691071c6ba6dbb9fc05 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 17 Nov 2021 13:17:21 +0530 Subject: [PATCH 39/43] trigger popper open only if attributes not null --- src/pages/gallery/index.tsx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/pages/gallery/index.tsx b/src/pages/gallery/index.tsx index 3c0934bb6..554affbed 100644 --- a/src/pages/gallery/index.tsx +++ b/src/pages/gallery/index.tsx @@ -249,12 +249,18 @@ export default function Gallery() { useEffect(() => setDialogView(true), [dialogMessage]); useEffect( - () => setCollectionSelectorView(true), + () => collectionSelectorAttributes && setCollectionSelectorView(true), [collectionSelectorAttributes] ); - useEffect(() => setCollectionNamerView(true), [collectionNamerAttributes]); - useEffect(() => setFixCreationTimeView(true), [fixCreationTimeAttributes]); + useEffect( + () => collectionNamerAttributes && setCollectionNamerView(true), + [collectionNamerAttributes] + ); + useEffect( + () => fixCreationTimeAttributes && setFixCreationTimeView(true), + [fixCreationTimeAttributes] + ); useEffect(() => { if (typeof activeCollection === 'undefined') { From 0c23ace8fffd47e5a43f1b5e5125f0647aad931b Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 17 Nov 2021 13:29:46 +0530 Subject: [PATCH 40/43] remove redundant code --- src/services/updateCreationTimeWithExif.ts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/services/updateCreationTimeWithExif.ts b/src/services/updateCreationTimeWithExif.ts index d91b1d7eb..1bea28819 100644 --- a/src/services/updateCreationTimeWithExif.ts +++ b/src/services/updateCreationTimeWithExif.ts @@ -6,21 +6,11 @@ import { updateExistingFilePubMetadata, } from 'utils/file'; import { logError } from 'utils/sentry'; -import localForage from 'utils/storage/localForage'; import downloadManager from './downloadManager'; import { File, FILE_TYPE, updatePublicMagicMetadata } from './fileService'; import { getExifData } from './upload/exifService'; import { getFileType } from './upload/readFileService'; -const CREATION_TIME_UPDATED_FILES_TABLE = 'creation-time-updated-file-table'; - -export async function setCreationTimeUpdatedFiles(creationTimeUpdatedFiles) { - return await localForage.setItem( - CREATION_TIME_UPDATED_FILES_TABLE, - creationTimeUpdatedFiles - ); -} - export async function updateCreationTimeWithExif( filesToBeUpdated: File[], setProgressTracker: SetProgressTracker From bb9c82db524b9871e9d2f07afb652b0b2097fd4a Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 17 Nov 2021 13:36:23 +0530 Subject: [PATCH 41/43] remove unused LS_KEYS --- src/utils/storage/localStorage.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/utils/storage/localStorage.ts b/src/utils/storage/localStorage.ts index 16a9ea112..fddfe119e 100644 --- a/src/utils/storage/localStorage.ts +++ b/src/utils/storage/localStorage.ts @@ -13,7 +13,6 @@ export enum LS_KEYS { EXPORT = 'export', AnonymizeUserID = 'anonymizedUserID', THUMBNAIL_FIX_STATE = 'thumbnailFixState', - CREATION_TIME_FIX_STATE = 'creationTimeFixState', } export const setData = (key: LS_KEYS, value: object) => { From 8ad2d3f23fd1e20bb12616a3153705dbe7e24774 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 17 Nov 2021 13:51:21 +0530 Subject: [PATCH 42/43] skip non image file before downoading itself --- src/services/updateCreationTimeWithExif.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/services/updateCreationTimeWithExif.ts b/src/services/updateCreationTimeWithExif.ts index 1bea28819..efb72c01d 100644 --- a/src/services/updateCreationTimeWithExif.ts +++ b/src/services/updateCreationTimeWithExif.ts @@ -23,13 +23,13 @@ export async function updateCreationTimeWithExif( setProgressTracker({ current: 0, total: filesToBeUpdated.length }); for (const [index, file] of filesToBeUpdated.entries()) { try { + if (file.metadata.fileType !== FILE_TYPE.IMAGE) { + continue; + } const fileURL = await downloadManager.getFile(file); const fileObject = await getFileFromURL(fileURL); const worker = await new CryptoWorker(); const fileTypeInfo = await getFileType(worker, fileObject); - if (file.metadata.fileType !== FILE_TYPE.IMAGE) { - continue; - } const exifData = await getExifData(fileObject, fileTypeInfo); if ( exifData?.creationTime && From d62739e25975e7973c2c9b146f262948257b28be Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 17 Nov 2021 15:10:09 +0530 Subject: [PATCH 43/43] show fix creation for selected user --- .../pages/gallery/SelectedFileOptions.tsx | 22 ++++++++++++++----- src/services/userService.ts | 2 ++ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/components/pages/gallery/SelectedFileOptions.tsx b/src/components/pages/gallery/SelectedFileOptions.tsx index 1b034abae..f10bdeefa 100644 --- a/src/components/pages/gallery/SelectedFileOptions.tsx +++ b/src/components/pages/gallery/SelectedFileOptions.tsx @@ -1,5 +1,5 @@ import { SetDialogMessage } from 'components/MessageDialog'; -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { SetCollectionSelectorAttributes } from './CollectionSelector'; import styled from 'styled-components'; import Navbar from 'components/Navbar'; @@ -18,6 +18,8 @@ import { Collection } from 'services/collectionService'; import RemoveIcon from 'components/icons/RemoveIcon'; import RestoreIcon from 'components/icons/RestoreIcon'; import ClockIcon from 'components/icons/ClockIcon'; +import { getData, LS_KEYS } from 'utils/storage/localStorage'; +import { FIX_CREATION_TIME_USER_ID, User } from 'services/userService'; interface Props { addToCollectionHelper: (collection: Collection) => void; @@ -81,6 +83,12 @@ const SelectedFileOptions = ({ activeCollection, isFavoriteCollection, }: Props) => { + const [showFixCreationTime, setShowFixCreationTime] = useState(false); + useEffect(() => { + const user: User = getData(LS_KEYS.USER); + const showFixCreationTime = user?.id === FIX_CREATION_TIME_USER_ID; + setShowFixCreationTime(showFixCreationTime); + }, []); const addToCollection = () => setCollectionSelectorAttributes({ callback: addToCollectionHelper, @@ -171,11 +179,13 @@ const SelectedFileOptions = ({ ) : ( <> - - - - - + {showFixCreationTime && ( + + + + + + )} diff --git a/src/services/userService.ts b/src/services/userService.ts index c55a9722f..f904a1e93 100644 --- a/src/services/userService.ts +++ b/src/services/userService.ts @@ -28,6 +28,8 @@ const ENDPOINT = getEndpoint(); const HAS_SET_KEYS = 'hasSetKeys'; +export const FIX_CREATION_TIME_USER_ID = 341; + export interface User { id: number; name: string;