From 5095bde562b8405930d36036bedef1e123a3e8d2 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 3 Nov 2021 16:01:47 +0530 Subject: [PATCH 01/68] 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 02/68] 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 03/68] 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 87089eec438c09b1a692da57f6d5d2dee944027c Mon Sep 17 00:00:00 2001 From: Abhinav Date: Wed, 10 Nov 2021 13:46:16 +0530 Subject: [PATCH 04/68] 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 05/68] 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 06/68] 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 07/68] 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 08/68] 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 09/68] 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 07591fe30dea4154ea2bb3248ec92cd148bd2d85 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Fri, 12 Nov 2021 10:39:36 +0530 Subject: [PATCH 10/68] 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 11/68] 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 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 12/68] 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 13/68] 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 14/68] 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 15/68] 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 16/68] 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 17/68] 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 18/68] 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 19/68] 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 20/68] 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 21/68] 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 22/68] 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 23/68] 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 24/68] 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 25/68] 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 26/68] 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; From 2b7e5f207489eb32a43d22e7c7cacb5e22174eb7 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Thu, 18 Nov 2021 10:12:01 +0530 Subject: [PATCH 27/68] one more place with same issue --- 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 f4c8fd855..dd79222f3 100644 --- a/src/services/upload/thumbnailService.ts +++ b/src/services/upload/thumbnailService.ts @@ -44,7 +44,7 @@ export async function generateThumbnail( ); } catch (e) { logError(e, 'failed to generate thumbnail using ffmpeg', { - type: fileTypeInfo.exactType, + fileFormat: fileTypeInfo.exactType, }); canvas = await generateVideoThumbnail(file); } From 91cbbfc3ebba076eb540f0694c019217c59823b6 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Thu, 18 Nov 2021 22:20:53 +0530 Subject: [PATCH 28/68] refactor date picker into seperate component --- src/components/EnteDateTimePicker.tsx | 37 ++++++++++++++++++++++++ src/components/PhotoSwipe/PhotoSwipe.tsx | 34 ++++------------------ 2 files changed, 43 insertions(+), 28 deletions(-) create mode 100644 src/components/EnteDateTimePicker.tsx diff --git a/src/components/EnteDateTimePicker.tsx b/src/components/EnteDateTimePicker.tsx new file mode 100644 index 000000000..de4d37f71 --- /dev/null +++ b/src/components/EnteDateTimePicker.tsx @@ -0,0 +1,37 @@ +import React from 'react'; +import { + MIN_EDITED_CREATION_TIME, + MAX_EDITED_CREATION_TIME, + ALL_TIME, +} from 'services/fileService'; + +import DatePicker from 'react-datepicker'; +import 'react-datepicker/dist/react-datepicker.css'; + +const isSameDay = (first, second) => + first.getFullYear() === second.getFullYear() && + first.getMonth() === second.getMonth() && + first.getDate() === second.getDate(); + +const EnteDateTimePicker = ({ isInEditMode, pickedTime, handleChange }) => ( + +); + +export default EnteDateTimePicker; diff --git a/src/components/PhotoSwipe/PhotoSwipe.tsx b/src/components/PhotoSwipe/PhotoSwipe.tsx index a7f90d77a..58fd6852b 100644 --- a/src/components/PhotoSwipe/PhotoSwipe.tsx +++ b/src/components/PhotoSwipe/PhotoSwipe.tsx @@ -8,11 +8,8 @@ import { removeFromFavorites, } from 'services/collectionService'; import { - ALL_TIME, File, MAX_EDITED_FILE_NAME_LENGTH, - MAX_EDITED_CREATION_TIME, - MIN_EDITED_CREATION_TIME, updatePublicMagicMetadata, } from 'services/fileService'; import constants from 'utils/strings/constants'; @@ -41,14 +38,13 @@ import { } from 'components/Container'; import { logError } from 'utils/sentry'; -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'; import { Formik } from 'formik'; import * as Yup from 'yup'; import EnteSpinner from 'components/EnteSpinner'; +import EnteDateTimePicker from 'components/EnteDateTimePicker'; interface Iprops { isOpen: boolean; @@ -87,11 +83,6 @@ const renderInfoItem = (label: string, value: string | JSX.Element) => ( ); -const isSameDay = (first, second) => - first.getFullYear() === second.getFullYear() && - first.getMonth() === second.getMonth() && - first.getDate() === second.getDate(); - function RenderCreationTime({ file, scheduleUpdate, @@ -145,24 +136,11 @@ function RenderCreationTime({ {isInEditMode ? ( - + ) : ( formatDateTime(pickedTime) )} From b2162c4398e77ae528e4b8f1edcfb2262b907816 Mon Sep 17 00:00:00 2001 From: Abhinav Date: Thu, 18 Nov 2021 22:44:50 +0530 Subject: [PATCH 29/68] add options for fix creation time --- src/components/FixCreationTime.tsx | 172 --------------------- src/components/FixCreationTime/footer.tsx | 51 ++++++ src/components/FixCreationTime/index.tsx | 122 +++++++++++++++ src/components/FixCreationTime/options.tsx | 107 +++++++++++++ src/components/FixCreationTime/running.tsx | 35 +++++ src/services/updateCreationTimeWithExif.ts | 33 ++-- src/services/upload/exifService.ts | 44 ++++-- src/utils/strings/englishConstants.tsx | 6 +- 8 files changed, 375 insertions(+), 195 deletions(-) delete mode 100644 src/components/FixCreationTime.tsx create mode 100644 src/components/FixCreationTime/footer.tsx create mode 100644 src/components/FixCreationTime/index.tsx create mode 100644 src/components/FixCreationTime/options.tsx create mode 100644 src/components/FixCreationTime/running.tsx diff --git a/src/components/FixCreationTime.tsx b/src/components/FixCreationTime.tsx deleted file mode 100644 index 25ff329ad..000000000 --- a/src/components/FixCreationTime.tsx +++ /dev/null @@ -1,172 +0,0 @@ -import constants from 'utils/strings/constants'; -import MessageDialog from './MessageDialog'; -import React, { useContext, useEffect, useState } from 'react'; -import { ProgressBar, Button } from 'react-bootstrap'; -import { ComfySpan } from './ExportInProgress'; -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, - RUNNING, - COMPLETED, - COMPLETED_WITH_ERRORS, -} -function Message(props: { fixState: FIX_STATE }) { - let message = null; - switch (props.fixState) { - case FIX_STATE.NOT_STARTED: - message = constants.UPDATE_CREATION_TIME_NOT_STARTED(); - break; - case FIX_STATE.COMPLETED: - message = constants.UPDATE_CREATION_TIME_COMPLETED(); - 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 galleryContext = useContext(GalleryContext); - - useEffect(() => { - if ( - props.attributes && - props.isOpen && - fixState !== FIX_STATE.RUNNING - ) { - setFixState(FIX_STATE.NOT_STARTED); - } - }, [props.isOpen]); - - const startFix = async () => { - setFixState(FIX_STATE.RUNNING); - const completedWithoutError = await updateCreationTimeWithExif( - props.attributes.files, - setProgressTracker - ); - if (!completedWithoutError) { - setFixState(FIX_STATE.COMPLETED); - } else { - setFixState(FIX_STATE.COMPLETED_WITH_ERRORS); - } - await galleryContext.syncWithRemote(); - }; - if (!props.attributes) { - return <>; - } - - return ( - -
- - - {fixState === FIX_STATE.RUNNING && ( - <> -
- - {' '} - {progressTracker.current} /{' '} - {progressTracker.total}{' '} - {' '} - - {' '} - {constants.CREATION_TIME_UPDATED} - -
-
- -
- - )} - {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/FixCreationTime/footer.tsx b/src/components/FixCreationTime/footer.tsx new file mode 100644 index 000000000..ba2f50da9 --- /dev/null +++ b/src/components/FixCreationTime/footer.tsx @@ -0,0 +1,51 @@ +import React from 'react'; +import { Button } from 'react-bootstrap'; +import { FIX_STATE } from '.'; +import constants from 'utils/strings/constants'; + +export function FixCreationTimeFooter({ fixState, startFix, ...props }) { + return ( + 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/FixCreationTime/index.tsx b/src/components/FixCreationTime/index.tsx new file mode 100644 index 000000000..5346068df --- /dev/null +++ b/src/components/FixCreationTime/index.tsx @@ -0,0 +1,122 @@ +import constants from 'utils/strings/constants'; +import MessageDialog from '../MessageDialog'; +import React, { useContext, useEffect, useState } from 'react'; +import { updateCreationTimeWithExif } from 'services/updateCreationTimeWithExif'; +import { GalleryContext } from 'pages/gallery'; +import { File } from 'services/fileService'; +import { FixCreationTimeRunning } from './running'; +import { FixCreationTimeOptions } from './options'; +import { FixCreationTimeFooter } from './footer'; +export interface FixCreationTimeAttributes { + files: File[]; +} + +interface Props { + isOpen: boolean; + show: () => void; + hide: () => void; + attributes: FixCreationTimeAttributes; +} +export enum FIX_STATE { + NOT_STARTED, + RUNNING, + COMPLETED, + COMPLETED_WITH_ERRORS, +} + +export enum FIX_OPTIONS { + DATE_TIME_ORIGINAL, + DATE_TIME_DIGITIZED, + CUSTOM_TIME, +} +function Message(props: { fixState: FIX_STATE }) { + let message = null; + switch (props.fixState) { + case FIX_STATE.NOT_STARTED: + message = constants.UPDATE_CREATION_TIME_NOT_STARTED(); + break; + case FIX_STATE.COMPLETED: + message = constants.UPDATE_CREATION_TIME_COMPLETED(); + 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 galleryContext = useContext(GalleryContext); + + useEffect(() => { + if ( + props.attributes && + props.isOpen && + fixState !== FIX_STATE.RUNNING + ) { + setFixState(FIX_STATE.NOT_STARTED); + } + }, [props.isOpen]); + + const startFix = async (option: FIX_OPTIONS, customTime: Date) => { + setFixState(FIX_STATE.RUNNING); + const completedWithoutError = await updateCreationTimeWithExif( + props.attributes.files, + option, + customTime, + setProgressTracker + ); + if (!completedWithoutError) { + setFixState(FIX_STATE.COMPLETED); + } else { + setFixState(FIX_STATE.COMPLETED_WITH_ERRORS); + } + await galleryContext.syncWithRemote(); + }; + if (!props.attributes) { + return <>; + } + + return ( + +
+ + + {fixState === FIX_STATE.RUNNING && ( + + )} + {(fixState === FIX_STATE.NOT_STARTED || + fixState === FIX_STATE.COMPLETED_WITH_ERRORS) && ( +
+ +
+ )} + +
+
+ ); +} diff --git a/src/components/FixCreationTime/options.tsx b/src/components/FixCreationTime/options.tsx new file mode 100644 index 000000000..2e3b86af2 --- /dev/null +++ b/src/components/FixCreationTime/options.tsx @@ -0,0 +1,107 @@ +import { Formik } from 'formik'; +import React, { ChangeEvent } from 'react'; +import { FIX_OPTIONS } from '.'; +import { Form } from 'react-bootstrap'; +import EnteDateTimePicker from 'components/EnteDateTimePicker'; +import { Row, Value } from 'components/Container'; +import constants from 'utils/strings/constants'; + +interface formValues { + option: FIX_OPTIONS; + customTime: Date; +} + +const Option = ({ + value, + selected, + onChange, + label, +}: { + value: FIX_OPTIONS; + selected: FIX_OPTIONS; + onChange: (e: string | ChangeEvent) => void; + label: string; +}) => ( + + + + {label} + + +); + +export function FixCreationTimeOptions({ handleSubmit }) { + const onSubmit = (values: formValues) => { + console.log(values); + handleSubmit(values.option, new Date(values.customTime)); + }; + + return ( + + initialValues={{ + option: FIX_OPTIONS.DATE_TIME_ORIGINAL, + customTime: new Date(), + }} + validateOnBlur={false} + onSubmit={onSubmit}> + {({ values, handleChange, handleSubmit }) => ( +
+ + + + + + + + + + {Number(values.option) === FIX_OPTIONS.CUSTOM_TIME && ( + + + handleChange('customTime')( + x.toUTCString() + ) + } + /> + + )} + +
+ )} + + ); +} diff --git a/src/components/FixCreationTime/running.tsx b/src/components/FixCreationTime/running.tsx new file mode 100644 index 000000000..fdf3ed999 --- /dev/null +++ b/src/components/FixCreationTime/running.tsx @@ -0,0 +1,35 @@ +import constants from 'utils/strings/constants'; +import { ComfySpan } from 'components/ExportInProgress'; +import React from 'react'; +import { ProgressBar } from 'react-bootstrap'; + +export function FixCreationTimeRunning({ progressTracker }) { + return ( + <> +
+ + {' '} + {progressTracker.current} / {progressTracker.total}{' '} + {' '} + + {' '} + {constants.CREATION_TIME_UPDATED} + +
+
+ +
+ + ); +} diff --git a/src/services/updateCreationTimeWithExif.ts b/src/services/updateCreationTimeWithExif.ts index efb72c01d..1c4a199d9 100644 --- a/src/services/updateCreationTimeWithExif.ts +++ b/src/services/updateCreationTimeWithExif.ts @@ -1,3 +1,4 @@ +import { FIX_OPTIONS } from 'components/FixCreationTime'; import { SetProgressTracker } from 'components/FixLargeThumbnail'; import CryptoWorker from 'utils/crypto'; import { @@ -8,11 +9,13 @@ import { import { logError } from 'utils/sentry'; import downloadManager from './downloadManager'; import { File, FILE_TYPE, updatePublicMagicMetadata } from './fileService'; -import { getExifData } from './upload/exifService'; +import { getRawExif, getUNIXTime } from './upload/exifService'; import { getFileType } from './upload/readFileService'; export async function updateCreationTimeWithExif( filesToBeUpdated: File[], + fixOption: FIX_OPTIONS, + customTime: Date, setProgressTracker: SetProgressTracker ) { let completedWithError = false; @@ -26,18 +29,30 @@ export async function updateCreationTimeWithExif( 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); - const exifData = await getExifData(fileObject, fileTypeInfo); + let correctCreationTime: number; + if (fixOption === FIX_OPTIONS.CUSTOM_TIME) { + correctCreationTime = getUNIXTime(customTime); + } else { + const fileURL = await downloadManager.getFile(file); + const fileObject = await getFileFromURL(fileURL); + const worker = await new CryptoWorker(); + const fileTypeInfo = await getFileType(worker, fileObject); + const exifData = await getRawExif(fileObject, fileTypeInfo); + if (fixOption === FIX_OPTIONS.DATE_TIME_ORIGINAL) { + correctCreationTime = getUNIXTime( + exifData.DateTimeOriginal + ); + } else { + correctCreationTime = getUNIXTime(exifData.CreateDate); + } + } if ( - exifData?.creationTime && - exifData?.creationTime !== file.metadata.creationTime + correctCreationTime && + correctCreationTime !== file.metadata.creationTime ) { let updatedFile = await changeFileCreationTime( file, - exifData.creationTime + correctCreationTime ); updatedFile = ( await updatePublicMagicMetadata([updatedFile]) diff --git a/src/services/upload/exifService.ts b/src/services/upload/exifService.ts index 8589f45ee..f3e3de842 100644 --- a/src/services/upload/exifService.ts +++ b/src/services/upload/exifService.ts @@ -13,6 +13,15 @@ const EXIF_TAGS_NEEDED = [ 'GPSLatitudeRef', 'GPSLongitudeRef', ]; +interface Exif { + DateTimeOriginal?: Date; + CreateDate?: Date; + ModifyDate?: Date; + GPSLatitude?: number; + GPSLongitude?: number; + GPSLatitudeRef?: number; + GPSLongitudeRef?: number; +} interface ParsedEXIFData { location: Location; creationTime: number; @@ -22,7 +31,26 @@ export async function getExifData( receivedFile: globalThis.File, fileTypeInfo: FileTypeInfo ): Promise { - let exifData; + const exifData = await getRawExif(receivedFile, fileTypeInfo); + if (!exifData) { + return { location: NULL_LOCATION, creationTime: null }; + } + const parsedEXIFData = { + location: getEXIFLocation(exifData), + creationTime: getUNIXTime( + exifData.DateTimeOriginal ?? + exifData.CreateDate ?? + exifData.ModifyDate + ), + }; + return parsedEXIFData; +} + +export async function getRawExif( + receivedFile: File, + fileTypeInfo: FileTypeInfo +) { + let exifData: Exif; try { exifData = await exifr.parse(receivedFile, EXIF_TAGS_NEEDED); } catch (e) { @@ -31,20 +59,10 @@ export async function getExifData( }); // ignore exif parsing errors } - if (!exifData) { - return { location: NULL_LOCATION, creationTime: null }; - } - const parsedEXIFData = { - location: getEXIFLocation(exifData), - creationTime: getUNIXTime(exifData), - }; - return parsedEXIFData; + return exifData; } -function getUNIXTime(exifData: any) { - const dateTime: Date = - exifData.DateTimeOriginal ?? exifData.CreateDate ?? exifData.ModifyDate; - +export function getUNIXTime(dateTime: Date) { if (!dateTime) { return null; } diff --git a/src/utils/strings/englishConstants.tsx b/src/utils/strings/englishConstants.tsx index 69aa8d8aa..3a9212e9f 100644 --- a/src/utils/strings/englishConstants.tsx +++ b/src/utils/strings/englishConstants.tsx @@ -601,7 +601,7 @@ const englishConstants = { CREATION_TIME_UPDATED: `file time updated`, UPDATE_CREATION_TIME_NOT_STARTED: () => ( - <>do you want to fix time with the values found in EXIF + <>select the option you want to use to fix creation time ), UPDATE_CREATION_TIME_COMPLETED: () => <>successfully updated all files, @@ -609,6 +609,10 @@ const englishConstants = { <>file time updation failed for some files, please retry ), FILE_NAME_CHARACTER_LIMIT: '100 characters max', + + CREATION_DATE_TIME: 'creation date time ', + DATE_TIME_DIGITIZED: 'date time digitized', + CUSTOM_TIME: 'custom time', }; export default englishConstants; From aca752f00eda8bc8309f2335820eac9fa4b7ed7c Mon Sep 17 00:00:00 2001 From: Abhinav Date: Fri, 19 Nov 2021 00:06:45 +0530 Subject: [PATCH 30/68] refactor component --- src/components/FixCreationTime/footer.tsx | 6 +- src/components/FixCreationTime/index.tsx | 61 +++++++++--- src/components/FixCreationTime/options.tsx | 103 ++++++++------------- src/components/FixCreationTime/running.tsx | 2 +- 4 files changed, 92 insertions(+), 80 deletions(-) diff --git a/src/components/FixCreationTime/footer.tsx b/src/components/FixCreationTime/footer.tsx index ba2f50da9..133e9a92f 100644 --- a/src/components/FixCreationTime/footer.tsx +++ b/src/components/FixCreationTime/footer.tsx @@ -3,7 +3,11 @@ import { Button } from 'react-bootstrap'; import { FIX_STATE } from '.'; import constants from 'utils/strings/constants'; -export function FixCreationTimeFooter({ fixState, startFix, ...props }) { +export default function FixCreationTimeFooter({ + fixState, + startFix, + ...props +}) { return ( fixState !== FIX_STATE.RUNNING && (
{ if ( props.attributes && @@ -81,6 +88,11 @@ export default function FixCreationTime(props: Props) { return <>; } + const onSubmit = (values: formValues) => { + console.log(values); + startFix(Number(values.option), new Date(values.customTime)); + }; + return ( {fixState === FIX_STATE.RUNNING && ( )} - {(fixState === FIX_STATE.NOT_STARTED || - fixState === FIX_STATE.COMPLETED_WITH_ERRORS) && ( -
- -
- )} - + + initialValues={{ + option: FIX_OPTIONS.DATE_TIME_ORIGINAL, + customTime: new Date(), + }} + validateOnBlur={false} + onSubmit={onSubmit}> + {({ values, handleChange, handleSubmit }) => ( + <> + {(fixState === FIX_STATE.NOT_STARTED || + fixState === + FIX_STATE.COMPLETED_WITH_ERRORS) && ( +
+ +
+ )} + + + )} +
); diff --git a/src/components/FixCreationTime/options.tsx b/src/components/FixCreationTime/options.tsx index 2e3b86af2..79d7433f2 100644 --- a/src/components/FixCreationTime/options.tsx +++ b/src/components/FixCreationTime/options.tsx @@ -1,4 +1,3 @@ -import { Formik } from 'formik'; import React, { ChangeEvent } from 'react'; import { FIX_OPTIONS } from '.'; import { Form } from 'react-bootstrap'; @@ -6,11 +5,6 @@ import EnteDateTimePicker from 'components/EnteDateTimePicker'; import { Row, Value } from 'components/Container'; import constants from 'utils/strings/constants'; -interface formValues { - option: FIX_OPTIONS; - customTime: Date; -} - const Option = ({ value, selected, @@ -43,65 +37,48 @@ const Option = ({ ); -export function FixCreationTimeOptions({ handleSubmit }) { - const onSubmit = (values: formValues) => { - console.log(values); - handleSubmit(values.option, new Date(values.customTime)); - }; - +export default function FixCreationTimeOptions({ handleChange, values }) { return ( - - initialValues={{ - option: FIX_OPTIONS.DATE_TIME_ORIGINAL, - customTime: new Date(), - }} - validateOnBlur={false} - onSubmit={onSubmit}> - {({ values, handleChange, handleSubmit }) => ( -
- -