This commit is contained in:
Manav Rathi 2024-04-01 09:58:55 +05:30
parent 18698d35bb
commit 35f2a6944e
No known key found for this signature in database
5 changed files with 314 additions and 326 deletions

View file

@ -0,0 +1,314 @@
import { Row, Value } from "@ente/shared/components/Container";
import DialogBox from "@ente/shared/components/DialogBox/";
import { Button, LinearProgress } from "@mui/material";
import EnteDateTimePicker from "components/EnteDateTimePicker";
import { ComfySpan } from "components/ExportInProgress";
import { Formik } from "formik";
import { t } from "i18next";
import { GalleryContext } from "pages/gallery";
import { ChangeEvent, useContext, useEffect, useState } from "react";
import { Form } from "react-bootstrap";
import { updateCreationTimeWithExif } from "services/updateCreationTimeWithExif";
import { EnteFile } from "types/file";
export interface FixCreationTimeAttributes {
files: EnteFile[];
}
interface Props {
isOpen: boolean;
show: () => void;
hide: () => void;
attributes: FixCreationTimeAttributes;
}
enum FIX_STATE {
NOT_STARTED,
RUNNING,
COMPLETED,
COMPLETED_WITH_ERRORS,
}
enum FIX_OPTIONS {
DATE_TIME_ORIGINAL,
DATE_TIME_DIGITIZED,
METADATA_DATE,
CUSTOM_TIME,
}
interface formValues {
option: FIX_OPTIONS;
customTime: Date;
}
function Message({ fixState }: { fixState: FIX_STATE }) {
let message = null;
switch (fixState) {
case FIX_STATE.NOT_STARTED:
message = t("UPDATE_CREATION_TIME_NOT_STARTED");
break;
case FIX_STATE.COMPLETED:
message = t("UPDATE_CREATION_TIME_COMPLETED");
break;
case FIX_STATE.COMPLETED_WITH_ERRORS:
message = t("UPDATE_CREATION_TIME_COMPLETED_WITH_ERROR");
break;
}
return message ? <div>{message}</div> : <></>;
}
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 <></>;
}
const onSubmit = (values: formValues) => {
startFix(Number(values.option), new Date(values.customTime));
};
return (
<DialogBox
open={props.isOpen}
onClose={props.hide}
attributes={{
title:
fixState === FIX_STATE.RUNNING
? t("FIX_CREATION_TIME_IN_PROGRESS")
: t("FIX_CREATION_TIME"),
nonClosable: true,
}}
>
<div
style={{
marginBottom: "10px",
display: "flex",
flexDirection: "column",
...(fixState === FIX_STATE.RUNNING
? { alignItems: "center" }
: {}),
}}
>
<Message fixState={fixState} />
{fixState === FIX_STATE.RUNNING && (
<FixCreationTimeRunning progressTracker={progressTracker} />
)}
<Formik<formValues>
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) && (
<div style={{ marginTop: "10px" }}>
<FixCreationTimeOptions
handleChange={handleChange}
values={values}
/>
</div>
)}
<FixCreationTimeFooter
fixState={fixState}
startFix={handleSubmit}
hide={props.hide}
/>
</>
)}
</Formik>
</div>
</DialogBox>
);
}
const Option = ({
value,
selected,
onChange,
label,
}: {
value: FIX_OPTIONS;
selected: FIX_OPTIONS;
onChange: (e: string | ChangeEvent<any>) => void;
label: string;
}) => (
<Form.Check
name="group1"
style={{
margin: "5px 0",
color: value !== Number(selected) ? "#aaa" : "#fff",
}}
>
<Form.Check.Input
id={value.toString()}
type="radio"
value={value}
checked={value === Number(selected)}
onChange={onChange}
/>
<Form.Check.Label
style={{ cursor: "pointer" }}
htmlFor={value.toString()}
>
{label}
</Form.Check.Label>
</Form.Check>
);
function FixCreationTimeOptions({ handleChange, values }) {
return (
<Form noValidate>
<Row style={{ margin: "0" }}>
<Option
value={FIX_OPTIONS.DATE_TIME_ORIGINAL}
onChange={handleChange("option")}
label={t("DATE_TIME_ORIGINAL")}
selected={Number(values.option)}
/>
</Row>
<Row style={{ margin: "0" }}>
<Option
value={FIX_OPTIONS.DATE_TIME_DIGITIZED}
onChange={handleChange("option")}
label={t("DATE_TIME_DIGITIZED")}
selected={Number(values.option)}
/>
</Row>
<Row style={{ margin: "0" }}>
<Option
value={FIX_OPTIONS.METADATA_DATE}
onChange={handleChange("option")}
label={t("METADATA_DATE")}
selected={Number(values.option)}
/>
</Row>
<Row style={{ margin: "0" }}>
<Value width="50%">
<Option
value={FIX_OPTIONS.CUSTOM_TIME}
onChange={handleChange("option")}
label={t("CUSTOM_TIME")}
selected={Number(values.option)}
/>
</Value>
{Number(values.option) === FIX_OPTIONS.CUSTOM_TIME && (
<Value width="40%">
<EnteDateTimePicker
onSubmit={(x: Date) =>
handleChange("customTime")(x.toUTCString())
}
/>
</Value>
)}
</Row>
</Form>
);
}
const FixCreationTimeFooter = ({ fixState, startFix, ...props }) => {
return (
fixState !== FIX_STATE.RUNNING && (
<div
style={{
width: "100%",
display: "flex",
marginTop: "30px",
justifyContent: "space-around",
}}
>
{(fixState === FIX_STATE.NOT_STARTED ||
fixState === FIX_STATE.COMPLETED_WITH_ERRORS) && (
<Button
color="secondary"
size="large"
onClick={() => {
props.hide();
}}
>
{t("CANCEL")}
</Button>
)}
{fixState === FIX_STATE.COMPLETED && (
<Button color="primary" size="large" onClick={props.hide}>
{t("CLOSE")}
</Button>
)}
{(fixState === FIX_STATE.NOT_STARTED ||
fixState === FIX_STATE.COMPLETED_WITH_ERRORS) && (
<>
<div style={{ width: "30px" }} />
<Button color="accent" size="large" onClick={startFix}>
{t("FIX_CREATION_TIME")}
</Button>
</>
)}
</div>
)
);
};
const FixCreationTimeRunning = ({ progressTracker }) => {
const progress = Math.round(
(progressTracker.current * 100) / progressTracker.total,
);
return (
<>
<div style={{ marginBottom: "10px" }}>
<ComfySpan>
{" "}
{progressTracker.current} / {progressTracker.total}{" "}
</ComfySpan>{" "}
<span style={{ marginLeft: "10px" }}>
{" "}
{t("CREATION_TIME_UPDATED")}
</span>
</div>
<div
style={{
width: "100%",
marginTop: "10px",
marginBottom: "20px",
}}
>
<LinearProgress variant="determinate" value={progress} />
</div>
</>
);
};

View file

@ -1,50 +0,0 @@
import { Button } from "@mui/material";
import { t } from "i18next";
import { FIX_STATE } from ".";
export default function FixCreationTimeFooter({
fixState,
startFix,
...props
}) {
return (
fixState !== FIX_STATE.RUNNING && (
<div
style={{
width: "100%",
display: "flex",
marginTop: "30px",
justifyContent: "space-around",
}}
>
{(fixState === FIX_STATE.NOT_STARTED ||
fixState === FIX_STATE.COMPLETED_WITH_ERRORS) && (
<Button
color="secondary"
size="large"
onClick={() => {
props.hide();
}}
>
{t("CANCEL")}
</Button>
)}
{fixState === FIX_STATE.COMPLETED && (
<Button color="primary" size="large" onClick={props.hide}>
{t("CLOSE")}
</Button>
)}
{(fixState === FIX_STATE.NOT_STARTED ||
fixState === FIX_STATE.COMPLETED_WITH_ERRORS) && (
<>
<div style={{ width: "30px" }} />
<Button color="accent" size="large" onClick={startFix}>
{t("FIX_CREATION_TIME")}
</Button>
</>
)}
</div>
)
);
}

View file

@ -1,154 +0,0 @@
import DialogBox from "@ente/shared/components/DialogBox/";
import { Formik } from "formik";
import { GalleryContext } from "pages/gallery";
import { useContext, useEffect, useState } from "react";
import { updateCreationTimeWithExif } from "services/updateCreationTimeWithExif";
import { EnteFile } from "types/file";
import FixCreationTimeFooter from "./footer";
import FixCreationTimeRunning from "./running";
import { t } from "i18next";
import FixCreationTimeOptions from "./options";
export interface FixCreationTimeAttributes {
files: EnteFile[];
}
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,
METADATA_DATE,
CUSTOM_TIME,
}
interface formValues {
option: FIX_OPTIONS;
customTime: Date;
}
function Message({ fixState }: { fixState: FIX_STATE }) {
let message = null;
switch (fixState) {
case FIX_STATE.NOT_STARTED:
message = t("UPDATE_CREATION_TIME_NOT_STARTED");
break;
case FIX_STATE.COMPLETED:
message = t("UPDATE_CREATION_TIME_COMPLETED");
break;
case FIX_STATE.COMPLETED_WITH_ERRORS:
message = t("UPDATE_CREATION_TIME_COMPLETED_WITH_ERROR");
break;
}
return message ? <div>{message}</div> : <></>;
}
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 <></>;
}
const onSubmit = (values: formValues) => {
startFix(Number(values.option), new Date(values.customTime));
};
return (
<DialogBox
open={props.isOpen}
onClose={props.hide}
attributes={{
title:
fixState === FIX_STATE.RUNNING
? t("FIX_CREATION_TIME_IN_PROGRESS")
: t("FIX_CREATION_TIME"),
nonClosable: true,
}}
>
<div
style={{
marginBottom: "10px",
display: "flex",
flexDirection: "column",
...(fixState === FIX_STATE.RUNNING
? { alignItems: "center" }
: {}),
}}
>
<Message fixState={fixState} />
{fixState === FIX_STATE.RUNNING && (
<FixCreationTimeRunning progressTracker={progressTracker} />
)}
<Formik<formValues>
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) && (
<div style={{ marginTop: "10px" }}>
<FixCreationTimeOptions
handleChange={handleChange}
values={values}
/>
</div>
)}
<FixCreationTimeFooter
fixState={fixState}
startFix={handleSubmit}
hide={props.hide}
/>
</>
)}
</Formik>
</div>
</DialogBox>
);
}

View file

@ -1,90 +0,0 @@
import { Row, Value } from "@ente/shared/components/Container";
import EnteDateTimePicker from "components/EnteDateTimePicker";
import { t } from "i18next";
import { ChangeEvent } from "react";
import { Form } from "react-bootstrap";
import { FIX_OPTIONS } from ".";
const Option = ({
value,
selected,
onChange,
label,
}: {
value: FIX_OPTIONS;
selected: FIX_OPTIONS;
onChange: (e: string | ChangeEvent<any>) => void;
label: string;
}) => (
<Form.Check
name="group1"
style={{
margin: "5px 0",
color: value !== Number(selected) ? "#aaa" : "#fff",
}}
>
<Form.Check.Input
id={value.toString()}
type="radio"
value={value}
checked={value === Number(selected)}
onChange={onChange}
/>
<Form.Check.Label
style={{ cursor: "pointer" }}
htmlFor={value.toString()}
>
{label}
</Form.Check.Label>
</Form.Check>
);
export default function FixCreationTimeOptions({ handleChange, values }) {
return (
<Form noValidate>
<Row style={{ margin: "0" }}>
<Option
value={FIX_OPTIONS.DATE_TIME_ORIGINAL}
onChange={handleChange("option")}
label={t("DATE_TIME_ORIGINAL")}
selected={Number(values.option)}
/>
</Row>
<Row style={{ margin: "0" }}>
<Option
value={FIX_OPTIONS.DATE_TIME_DIGITIZED}
onChange={handleChange("option")}
label={t("DATE_TIME_DIGITIZED")}
selected={Number(values.option)}
/>
</Row>
<Row style={{ margin: "0" }}>
<Option
value={FIX_OPTIONS.METADATA_DATE}
onChange={handleChange("option")}
label={t("METADATA_DATE")}
selected={Number(values.option)}
/>
</Row>
<Row style={{ margin: "0" }}>
<Value width="50%">
<Option
value={FIX_OPTIONS.CUSTOM_TIME}
onChange={handleChange("option")}
label={t("CUSTOM_TIME")}
selected={Number(values.option)}
/>
</Value>
{Number(values.option) === FIX_OPTIONS.CUSTOM_TIME && (
<Value width="40%">
<EnteDateTimePicker
onSubmit={(x: Date) =>
handleChange("customTime")(x.toUTCString())
}
/>
</Value>
)}
</Row>
</Form>
);
}

View file

@ -1,32 +0,0 @@
import { LinearProgress } from "@mui/material";
import { ComfySpan } from "components/ExportInProgress";
import { t } from "i18next";
export default function FixCreationTimeRunning({ progressTracker }) {
const progress = Math.round(
(progressTracker.current * 100) / progressTracker.total,
);
return (
<>
<div style={{ marginBottom: "10px" }}>
<ComfySpan>
{" "}
{progressTracker.current} / {progressTracker.total}{" "}
</ComfySpan>{" "}
<span style={{ marginLeft: "10px" }}>
{" "}
{t("CREATION_TIME_UPDATED")}
</span>
</div>
<div
style={{
width: "100%",
marginTop: "10px",
marginBottom: "20px",
}}
>
<LinearProgress variant="determinate" value={progress} />
</div>
</>
);
}