diff --git a/web/apps/photos/package.json b/web/apps/photos/package.json index b881b9ac6b..c2b0d85da9 100644 --- a/web/apps/photos/package.json +++ b/web/apps/photos/package.json @@ -7,16 +7,13 @@ "@/base": "*", "@/media": "*", "@/new": "*", - "@date-io/date-fns": "^2.14.0", "@ente/eslint-config": "*", "@ente/shared": "*", - "@mui/x-date-pickers": "^5.0.0-alpha.6", "@stripe/stripe-js": "^1.13.2", "@xmldom/xmldom": "^0.8.10", "bip39": "^3.0.4", "bs58": "^5.0.0", "chrono-node": "^2.2.6", - "date-fns": "^2", "debounce": "^2.0.0", "exifr": "^7.1.3", "exifreader": "^4", diff --git a/web/apps/photos/src/components/EnteDateTimePicker.tsx b/web/apps/photos/src/components/EnteDateTimePicker.tsx deleted file mode 100644 index e53ed65b98..0000000000 --- a/web/apps/photos/src/components/EnteDateTimePicker.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import { useState } from "react"; - -import { - LocalizationProvider, - MobileDateTimePicker, -} from "@mui/x-date-pickers"; -import { AdapterDateFns } from "@mui/x-date-pickers/AdapterDateFns"; - -const MIN_EDITED_CREATION_TIME = new Date(1800, 0, 1); -const MAX_EDITED_CREATION_TIME = new Date(); - -interface Props { - initialValue?: Date; - disabled?: boolean; - label?: string; - onSubmit: (date: Date) => void; - onClose?: () => void; -} - -const EnteDateTimePicker = ({ - initialValue, - disabled, - onSubmit, - onClose, -}: Props) => { - const [open, setOpen] = useState(true); - const [value, setValue] = useState(initialValue ?? new Date()); - - const handleClose = () => { - setOpen(false); - onClose?.(); - }; - return ( - - setOpen(true)} - maxDateTime={MAX_EDITED_CREATION_TIME} - minDateTime={MIN_EDITED_CREATION_TIME} - disabled={disabled} - onAccept={onSubmit} - DialogProps={{ - sx: { - zIndex: "1502", - ".MuiPickersToolbar-penIconButton": { - display: "none", - }, - ".MuiDialog-paper": { width: "320px" }, - ".MuiClockPicker-root": { - position: "relative", - minHeight: "292px", - }, - ".PrivatePickersSlideTransition-root": { - minHeight: "200px", - }, - }, - }} - renderInput={() => <>} - /> - - ); -}; - -export default EnteDateTimePicker; diff --git a/web/apps/photos/src/components/FixCreationTime.tsx b/web/apps/photos/src/components/FixCreationTime.tsx index 42aac282bb..836da7924a 100644 --- a/web/apps/photos/src/components/FixCreationTime.tsx +++ b/web/apps/photos/src/components/FixCreationTime.tsx @@ -1,4 +1,11 @@ +import log from "@/base/log"; +import type { ParsedMetadataDate } from "@/media/file-metadata"; +import { FileType } from "@/media/file-type"; +import { PhotoDateTimePicker } from "@/new/photos/components/PhotoDateTimePicker"; +import downloadManager from "@/new/photos/services/download"; +import { extractExifDates } from "@/new/photos/services/exif"; import { EnteFile } from "@/new/photos/types/file"; +import { fileLogID } from "@/new/photos/utils/file"; import DialogBox from "@ente/shared/components/DialogBox/"; import { Button, @@ -14,41 +21,42 @@ import { useFormik } from "formik"; import { t } from "i18next"; import { GalleryContext } from "pages/gallery"; import React, { useContext, useEffect, useState } from "react"; -import { updateCreationTimeWithExif } from "services/fix-exif"; -import EnteDateTimePicker from "./EnteDateTimePicker"; +import { + changeFileCreationTime, + updateExistingFilePubMetadata, +} from "utils/file"; -export interface FixCreationTimeAttributes { - files: EnteFile[]; -} - -type Step = "running" | "completed" | "completed-with-errors"; +/** The current state of the fixing process. */ +type Status = "running" | "completed" | "completed-with-errors"; export type FixOption = | "date-time-original" | "date-time-digitized" | "metadata-date" - | "custom-time"; + | "custom"; interface FormValues { option: FixOption; - /** - * Date.toISOString() - * - * Formik doesn't have native support for JS dates, so we instead keep the - * corresponding date's ISO string representation as the form state. - */ - customTimeString: string; + /* Only valid when {@link option} is "custom-time". */ + customDate: ParsedMetadataDate | undefined; +} + +export interface FixCreationTimeAttributes { + files: EnteFile[]; } interface FixCreationTimeProps { isOpen: boolean; - show: () => void; hide: () => void; attributes: FixCreationTimeAttributes; } -const FixCreationTime: React.FC = (props) => { - const [step, setStep] = useState(); +const FixCreationTime: React.FC = ({ + isOpen, + hide, + attributes, +}) => { + const [status, setStatus] = useState(); const [progressTracker, setProgressTracker] = useState({ current: 0, total: 0, @@ -58,39 +66,36 @@ const FixCreationTime: React.FC = (props) => { useEffect(() => { // TODO (MR): Not sure why this is needed - if (props.attributes && props.isOpen && step !== "running") { - setStep(undefined); - } - }, [props.isOpen]); + if (attributes && isOpen && status !== "running") setStatus(undefined); + }, [isOpen]); const onSubmit = async (values: FormValues) => { - console.log({ values }); - setStep("running"); - const completedWithErrors = await updateCreationTimeWithExif( - props.attributes.files, + setStatus("running"); + const completedWithErrors = await updateFiles( + attributes.files, values.option, - new Date(values.customTimeString), + values.customDate, setProgressTracker, ); - setStep(completedWithErrors ? "completed-with-errors" : "completed"); + setStatus(completedWithErrors ? "completed-with-errors" : "completed"); await galleryContext.syncWithRemote(); }; const title = - step === "running" + status == "running" ? t("FIX_CREATION_TIME_IN_PROGRESS") : t("FIX_CREATION_TIME"); - const message = messageForStep(step); + const message = messageForStatus(status); - if (!props.attributes) { + if (!attributes) { return <>; } return (
= (props) => { marginBottom: "10px", display: "flex", flexDirection: "column", - ...(step === "running" ? { alignItems: "center" } : {}), + ...(status == "running" ? { alignItems: "center" } : {}), }} > {message &&
{message}
} - - {step === "running" && ( - - )} - - + {status === "running" && } +
); @@ -115,7 +116,7 @@ const FixCreationTime: React.FC = (props) => { export default FixCreationTime; -const messageForStep = (step?: Step) => { +const messageForStatus = (step?: Status) => { switch (step) { case undefined: return undefined; @@ -128,113 +129,7 @@ const messageForStep = (step?: Step) => { } }; -interface OptionsFormProps { - step?: Step; - onSubmit: (values: FormValues) => void | Promise; - hide: () => void; -} - -const OptionsForm: React.FC = ({ step, onSubmit, hide }) => { - const { values, handleChange, handleSubmit } = useFormik({ - initialValues: { - option: "date-time-original", - customTimeString: new Date().toISOString(), - }, - validateOnBlur: false, - onSubmit, - }); - - return ( - <> - {(step === undefined || step === "completed-with-errors") && ( -
-
- - - {t("UPDATE_CREATION_TIME_NOT_STARTED")} - - - - } - label={t("DATE_TIME_ORIGINAL")} - /> - } - label={t("DATE_TIME_DIGITIZED")} - /> - } - label={t("METADATA_DATE")} - /> - } - label={t("CUSTOM_TIME")} - /> - - {values.option === "custom-time" && ( - - handleChange("customTimeString")( - d.toISOString(), - ) - } - /> - )} - -
- )} -