diff --git a/web/apps/photos/src/components/FixLargeThumbnail.tsx b/web/apps/photos/src/components/FixLargeThumbnail.tsx deleted file mode 100644 index e62fafda3..000000000 --- a/web/apps/photos/src/components/FixLargeThumbnail.tsx +++ /dev/null @@ -1,235 +0,0 @@ -import DialogBox from "@ente/shared/components/DialogBox/"; -import { logError } from "@ente/shared/sentry"; -import { LS_KEYS, getData, setData } from "@ente/shared/storage/localStorage"; -import { t } from "i18next"; -import React, { useEffect, useState } from "react"; -import { Button, ProgressBar } from "react-bootstrap"; -import { - getLargeThumbnailFiles, - replaceThumbnail, -} from "services/migrateThumbnailService"; -import { ComfySpan } from "./ExportInProgress"; - -export type SetProgressTracker = React.Dispatch< - React.SetStateAction<{ - current: number; - total: number; - }> ->; -interface Props { - isOpen: boolean; - show: () => void; - hide: () => void; -} -export enum FIX_STATE { - NOT_STARTED, - FIX_LATER, - NOOP, - RUNNING, - COMPLETED, - COMPLETED_WITH_ERRORS, -} -function Message({ fixState }: { fixState: FIX_STATE }) { - let message = null; - switch (fixState) { - case FIX_STATE.NOT_STARTED: - case FIX_STATE.FIX_LATER: - message = t("REPLACE_THUMBNAIL_NOT_STARTED"); - break; - case FIX_STATE.COMPLETED: - message = t("REPLACE_THUMBNAIL_COMPLETED"); - break; - case FIX_STATE.NOOP: - message = t("REPLACE_THUMBNAIL_NOOP"); - break; - case FIX_STATE.COMPLETED_WITH_ERRORS: - message = t("REPLACE_THUMBNAIL_COMPLETED_WITH_ERROR"); - break; - } - return message ? ( -
{message}
- ) : ( - <> - ); -} -export default function FixLargeThumbnails(props: Props) { - const [fixState, setFixState] = useState(FIX_STATE.NOT_STARTED); - const [progressTracker, setProgressTracker] = useState({ - current: 0, - total: 0, - }); - const [largeThumbnailFiles, setLargeThumbnailFiles] = useState( - [], - ); - - const init = (): FIX_STATE => { - let fixState = getData(LS_KEYS.THUMBNAIL_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; - }; - - const fetchLargeThumbnail = async () => { - const largeThumbnailFiles = (await getLargeThumbnailFiles()) ?? []; - setLargeThumbnailFiles(largeThumbnailFiles); - return largeThumbnailFiles; - }; - - const main = async () => { - const largeThumbnailFiles = await fetchLargeThumbnail(); - if ( - fixState === FIX_STATE.NOT_STARTED && - largeThumbnailFiles.length > 0 - ) { - props.show(); - } - if ( - (fixState === FIX_STATE.COMPLETED || fixState === FIX_STATE.NOOP) && - largeThumbnailFiles.length > 0 - ) { - updateFixState(FIX_STATE.NOT_STARTED); - logError(Error(), "large thumbnail files left after migration"); - } - if (largeThumbnailFiles.length === 0 && fixState !== FIX_STATE.NOOP) { - updateFixState(FIX_STATE.NOOP); - } - }; - useEffect(() => { - if (props.isOpen && fixState !== FIX_STATE.RUNNING) { - main(); - } - }, [props.isOpen]); - - useEffect(() => { - const fixState = init(); - if (fixState === FIX_STATE.NOT_STARTED) { - main(); - } - }, []); - const startFix = async (newlyFetchedLargeThumbnailFiles?: number[]) => { - updateFixState(FIX_STATE.RUNNING); - const completedWithError = await replaceThumbnail( - setProgressTracker, - new Set( - newlyFetchedLargeThumbnailFiles ?? largeThumbnailFiles ?? [], - ), - ); - if (typeof completedWithError !== "undefined") { - updateFixState( - completedWithError - ? FIX_STATE.COMPLETED_WITH_ERRORS - : FIX_STATE.COMPLETED, - ); - } - await fetchLargeThumbnail(); - }; - - const updateFixState = (fixState: FIX_STATE) => { - setFixState(fixState); - setData(LS_KEYS.THUMBNAIL_FIX_STATE, { state: fixState }); - }; - return ( - -
- - - {fixState === FIX_STATE.RUNNING && ( - <> -
- - {" "} - {progressTracker.current} /{" "} - {progressTracker.total}{" "} - {" "} - - {" "} - {t("THUMBNAIL_REPLACED")} - -
-
- -
- - )} -
- {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/web/apps/photos/src/components/Sidebar/UtilitySection.tsx b/web/apps/photos/src/components/Sidebar/UtilitySection.tsx index b297f36ee..9160fe4a2 100644 --- a/web/apps/photos/src/components/Sidebar/UtilitySection.tsx +++ b/web/apps/photos/src/components/Sidebar/UtilitySection.tsx @@ -1,15 +1,13 @@ -import { t } from "i18next"; -import { useContext, useState } from "react"; - -// import FixLargeThumbnails from 'components/FixLargeThumbnail'; import RecoveryKey from "@ente/shared/components/RecoveryKey"; import { ACCOUNTS_PAGES, PHOTOS_PAGES as PAGES, } from "@ente/shared/constants/pages"; import TwoFactorModal from "components/TwoFactor/Modal"; +import { t } from "i18next"; import { useRouter } from "next/router"; import { AppContext } from "pages/_app"; +import { useContext, useState } from "react"; // import mlIDbStorage from 'utils/storage/mlIDbStorage'; import { configurePasskeyRecovery, diff --git a/web/apps/photos/src/services/migrateThumbnailService.ts b/web/apps/photos/src/services/migrateThumbnailService.ts deleted file mode 100644 index ad83f4e4b..000000000 --- a/web/apps/photos/src/services/migrateThumbnailService.ts +++ /dev/null @@ -1,147 +0,0 @@ -import ComlinkCryptoWorker from "@ente/shared/crypto"; -import { DedicatedCryptoWorker } from "@ente/shared/crypto/internal/crypto.worker"; -import HTTPService from "@ente/shared/network/HTTPService"; -import { getEndpoint } from "@ente/shared/network/api"; -import { logError } from "@ente/shared/sentry"; -import { getToken } from "@ente/shared/storage/localStorage/helpers"; -import { Remote } from "comlink"; -import { SetProgressTracker } from "components/FixLargeThumbnail"; -import downloadManager from "services/download"; -import { getLocalFiles } from "services/fileService"; -import { getFileType } from "services/typeDetectionService"; -import { generateThumbnail } from "services/upload/thumbnailService"; -import uploadHttpClient from "services/upload/uploadHttpClient"; -import { S3FileAttributes } from "types/file"; -import { UploadURL } from "types/upload"; -import { getLocalTrashedFiles } from "./trashService"; - -const ENDPOINT = getEndpoint(); -const REPLACE_THUMBNAIL_THRESHOLD = 500 * 1024; // 500KB -export async function getLargeThumbnailFiles() { - try { - const token = getToken(); - if (!token) { - return; - } - const resp = await HTTPService.get( - `${ENDPOINT}/files/large-thumbnails`, - { - threshold: REPLACE_THUMBNAIL_THRESHOLD, - }, - { - "X-Auth-Token": token, - }, - ); - return resp.data.largeThumbnailFiles as number[]; - } catch (e) { - logError(e, "failed to get large thumbnail files"); - throw e; - } -} -export async function replaceThumbnail( - setProgressTracker: SetProgressTracker, - largeThumbnailFileIDs: Set, -) { - let completedWithError = false; - try { - const cryptoWorker = await ComlinkCryptoWorker.getInstance(); - const files = await getLocalFiles(); - const trashFiles = await getLocalTrashedFiles(); - const largeThumbnailFiles = [...files, ...trashFiles].filter((file) => - largeThumbnailFileIDs.has(file.id), - ); - if (largeThumbnailFileIDs.size !== largeThumbnailFiles.length) { - logError(Error(), "all large thumbnail files not found locally"); - } - if (largeThumbnailFiles.length === 0) { - return completedWithError; - } - setProgressTracker({ current: 0, total: largeThumbnailFiles.length }); - const uploadURLs: UploadURL[] = []; - await uploadHttpClient.fetchUploadURLs( - largeThumbnailFiles.length, - uploadURLs, - ); - for (const [idx, file] of largeThumbnailFiles.entries()) { - try { - setProgressTracker({ - current: idx, - total: largeThumbnailFiles.length, - }); - const originalThumbnail = - await downloadManager.getThumbnail(file); - const dummyImageFile = new File( - [originalThumbnail], - file.metadata.title, - ); - const fileTypeInfo = await getFileType(dummyImageFile); - const { thumbnail: newThumbnail } = await generateThumbnail( - dummyImageFile, - fileTypeInfo, - ); - const newUploadedThumbnail = await uploadThumbnail( - cryptoWorker, - file.key, - newThumbnail, - uploadURLs.pop(), - ); - await updateThumbnail(file.id, newUploadedThumbnail); - } catch (e) { - logError(e, "failed to replace a thumbnail"); - completedWithError = true; - } - } - } catch (e) { - logError(e, "replace Thumbnail function failed"); - completedWithError = true; - } - return completedWithError; -} - -export async function uploadThumbnail( - worker: Remote, - fileKey: string, - updatedThumbnail: Uint8Array, - uploadURL: UploadURL, -): Promise { - const { file: encryptedThumbnail } = await worker.encryptThumbnail( - updatedThumbnail, - fileKey, - ); - const thumbnailObjectKey = await uploadHttpClient.putFile( - uploadURL, - encryptedThumbnail.encryptedData, - () => {}, - ); - - return { - objectKey: thumbnailObjectKey, - decryptionHeader: encryptedThumbnail.decryptionHeader, - }; -} - -export async function updateThumbnail( - fileID: number, - newThumbnail: S3FileAttributes, -) { - try { - const token = getToken(); - if (!token) { - return; - } - await HTTPService.put( - `${ENDPOINT}/files/thumbnail`, - { - fileID: fileID, - thumbnail: newThumbnail, - }, - null, - { - "X-Auth-Token": token, - }, - ); - } catch (e) { - logError(e, "failed to update thumbnail"); - throw e; - } -} diff --git a/web/apps/photos/src/services/updateCreationTimeWithExif.ts b/web/apps/photos/src/services/updateCreationTimeWithExif.ts index 4cc4cc3d6..387188660 100644 --- a/web/apps/photos/src/services/updateCreationTimeWithExif.ts +++ b/web/apps/photos/src/services/updateCreationTimeWithExif.ts @@ -1,6 +1,5 @@ import { logError } from "@ente/shared/sentry"; import { FIX_OPTIONS } from "components/FixCreationTime"; -import { SetProgressTracker } from "components/FixLargeThumbnail"; import { EnteFile } from "types/file"; import { changeFileCreationTime, @@ -21,6 +20,13 @@ const EXIF_TIME_TAGS = [ "MetadataDate", ]; +export type SetProgressTracker = React.Dispatch< + React.SetStateAction<{ + current: number; + total: number; + }> +>; + export async function updateCreationTimeWithExif( filesToBeUpdated: EnteFile[], fixOption: FIX_OPTIONS,