Inline fin

This commit is contained in:
Manav Rathi
2024-11-13 07:12:45 +05:30
parent 3e94ea8f81
commit fc114c7d92
4 changed files with 475 additions and 497 deletions

View File

@@ -1,17 +1,48 @@
import { type UploadPhase } from "@/new/photos/services/upload/types";
import { FilledIconButton } from "@/new/photos/components/mui";
import {
UPLOAD_RESULT,
type UploadPhase,
} from "@/new/photos/services/upload/types";
import { useAppContext } from "@/new/photos/types/context";
import { Paper, Snackbar } from "@mui/material";
import {
SpaceBetweenFlex,
VerticallyCenteredFlex,
} from "@ente/shared/components/Container";
import Close from "@mui/icons-material/Close";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import UnfoldLessIcon from "@mui/icons-material/UnfoldLess";
import UnfoldMoreIcon from "@mui/icons-material/UnfoldMore";
import {
Accordion,
AccordionDetails,
AccordionSummary,
Box,
Button,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
Divider,
LinearProgress,
Paper,
Snackbar,
Stack,
styled,
Typography,
type AccordionProps,
type DialogProps,
type TypographyProps,
} from "@mui/material";
import ItemList from "components/ItemList";
import { t } from "i18next";
import { useEffect, useState } from "react";
import React, { createContext, useContext, useEffect, useState } from "react";
import { Trans } from "react-i18next";
import type {
InProgressUpload,
SegregatedFinishedUploads,
UploadCounter,
UploadFileNames,
} from "services/upload/uploadManager";
import UploadProgressContext from "./UploadProgress/context";
import { UploadProgressDialog } from "./UploadProgress/dialog";
import { UploadProgressHeader } from "./UploadProgress/header";
interface UploadProgressProps {
open: boolean;
@@ -90,6 +121,36 @@ export const UploadProgress: React.FC<UploadProgressProps> = ({
);
};
interface UploadProgressContextT {
open: boolean;
onClose: () => void;
uploadCounter: UploadCounter;
uploadPhase: UploadPhase;
percentComplete: number;
retryFailed: () => void;
inProgressUploads: InProgressUpload[];
uploadFileNames: UploadFileNames;
finishedUploads: SegregatedFinishedUploads;
hasLivePhotos: boolean;
expanded: boolean;
setExpanded: React.Dispatch<React.SetStateAction<boolean>>;
}
const UploadProgressContext = createContext<UploadProgressContextT>({
open: null,
onClose: () => null,
uploadCounter: null,
uploadPhase: undefined,
percentComplete: null,
retryFailed: () => null,
inProgressUploads: null,
uploadFileNames: null,
finishedUploads: null,
hasLivePhotos: null,
expanded: null,
setExpanded: () => null,
});
const MinimizedUploadProgress: React.FC = () => (
<Snackbar open anchorOrigin={{ horizontal: "right", vertical: "bottom" }}>
<Paper sx={{ width: "min(360px, 100svw)" }}>
@@ -97,3 +158,411 @@ const MinimizedUploadProgress: React.FC = () => (
</Paper>
</Snackbar>
);
function UploadProgressHeader() {
return (
<>
<UploadProgressTitle />
<UploadProgressBar />
</>
);
}
const UploadProgressTitleText = ({ expanded }) => {
return (
<Typography variant={expanded ? "h2" : "h3"}>
{t("FILE_UPLOAD")}
</Typography>
);
};
function UploadProgressSubtitleText() {
const { uploadPhase, uploadCounter } = useContext(UploadProgressContext);
return (
<Typography
variant="body"
fontWeight={"normal"}
color="text.muted"
marginTop={"4px"}
>
{subtitleText(uploadPhase, uploadCounter)}
</Typography>
);
}
const subtitleText = (
uploadPhase: UploadPhase,
uploadCounter: UploadCounter,
) => {
switch (uploadPhase) {
case "preparing":
return t("UPLOAD_STAGE_MESSAGE.0");
case "readingMetadata":
return t("UPLOAD_STAGE_MESSAGE.1");
case "uploading":
return t("UPLOAD_STAGE_MESSAGE.3", { uploadCounter });
case "cancelling":
return t("UPLOAD_STAGE_MESSAGE.4");
case "done":
return t("UPLOAD_STAGE_MESSAGE.5");
}
};
const UploadProgressTitle: React.FC = () => {
const { setExpanded, onClose, expanded } = useContext(
UploadProgressContext,
);
const toggleExpanded = () => setExpanded((expanded) => !expanded);
return (
<DialogTitle>
<SpaceBetweenFlex>
<Box>
<UploadProgressTitleText expanded={expanded} />
<UploadProgressSubtitleText />
</Box>
<Box>
<Stack direction={"row"} spacing={1}>
<FilledIconButton onClick={toggleExpanded}>
{expanded ? <UnfoldLessIcon /> : <UnfoldMoreIcon />}
</FilledIconButton>
<FilledIconButton onClick={onClose}>
<Close />
</FilledIconButton>
</Stack>
</Box>
</SpaceBetweenFlex>
</DialogTitle>
);
};
const UploadProgressBar: React.FC = () => {
const { uploadPhase, percentComplete } = useContext(UploadProgressContext);
return (
<Box>
{(uploadPhase == "readingMetadata" ||
uploadPhase == "uploading") && (
<>
<LinearProgress
sx={{
height: "2px",
backgroundColor: "transparent",
}}
variant="determinate"
value={percentComplete}
/>
<Divider />
</>
)}
</Box>
);
};
function UploadProgressDialog() {
const { open, onClose, uploadPhase, finishedUploads } = useContext(
UploadProgressContext,
);
const [hasUnUploadedFiles, setHasUnUploadedFiles] = useState(false);
useEffect(() => {
if (
finishedUploads.get(UPLOAD_RESULT.ALREADY_UPLOADED)?.length > 0 ||
finishedUploads.get(UPLOAD_RESULT.BLOCKED)?.length > 0 ||
finishedUploads.get(UPLOAD_RESULT.FAILED)?.length > 0 ||
finishedUploads.get(UPLOAD_RESULT.LARGER_THAN_AVAILABLE_STORAGE)
?.length > 0 ||
finishedUploads.get(UPLOAD_RESULT.TOO_LARGE)?.length > 0 ||
finishedUploads.get(UPLOAD_RESULT.UNSUPPORTED)?.length > 0
) {
setHasUnUploadedFiles(true);
} else {
setHasUnUploadedFiles(false);
}
}, [finishedUploads]);
const handleClose: DialogProps["onClose"] = (_, reason) => {
if (reason != "backdropClick") onClose();
};
return (
<Dialog open={open} onClose={handleClose} maxWidth="xs" fullWidth>
<UploadProgressHeader />
{(uploadPhase == "uploading" || uploadPhase == "done") && (
<DialogContent sx={{ "&&&": { px: 0 } }}>
{uploadPhase === "uploading" && <InProgressSection />}
<ResultSection
uploadResult={UPLOAD_RESULT.UPLOADED}
sectionTitle={t("SUCCESSFUL_UPLOADS")}
/>
<ResultSection
uploadResult={
UPLOAD_RESULT.UPLOADED_WITH_STATIC_THUMBNAIL
}
sectionTitle={t("THUMBNAIL_GENERATION_FAILED_UPLOADS")}
sectionInfo={t("THUMBNAIL_GENERATION_FAILED_INFO")}
/>
{uploadPhase == "done" && hasUnUploadedFiles && (
<NotUploadSectionHeader>
{t("FILE_NOT_UPLOADED_LIST")}
</NotUploadSectionHeader>
)}
<ResultSection
uploadResult={UPLOAD_RESULT.BLOCKED}
sectionTitle={t("BLOCKED_UPLOADS")}
sectionInfo={<Trans i18nKey={"ETAGS_BLOCKED"} />}
/>
<ResultSection
uploadResult={UPLOAD_RESULT.FAILED}
sectionTitle={t("FAILED_UPLOADS")}
sectionInfo={
uploadPhase == "done"
? undefined
: t("failed_uploads_hint")
}
/>
<ResultSection
uploadResult={UPLOAD_RESULT.ALREADY_UPLOADED}
sectionTitle={t("SKIPPED_FILES")}
sectionInfo={t("SKIPPED_INFO")}
/>
<ResultSection
uploadResult={
UPLOAD_RESULT.LARGER_THAN_AVAILABLE_STORAGE
}
sectionTitle={t(
"LARGER_THAN_AVAILABLE_STORAGE_UPLOADS",
)}
sectionInfo={t("LARGER_THAN_AVAILABLE_STORAGE_INFO")}
/>
<ResultSection
uploadResult={UPLOAD_RESULT.UNSUPPORTED}
sectionTitle={t("UNSUPPORTED_FILES")}
sectionInfo={t("UNSUPPORTED_INFO")}
/>
<ResultSection
uploadResult={UPLOAD_RESULT.TOO_LARGE}
sectionTitle={t("TOO_LARGE_UPLOADS")}
sectionInfo={t("TOO_LARGE_INFO")}
/>
</DialogContent>
)}
{uploadPhase == "done" && <DoneFooter />}
</Dialog>
);
}
const InProgressSection: React.FC = () => {
const { inProgressUploads, hasLivePhotos, uploadFileNames, uploadPhase } =
useContext(UploadProgressContext);
const fileList = inProgressUploads ?? [];
const renderListItem = ({ localFileID, progress }) => {
return (
<InProgressItemContainer key={localFileID}>
<span>{uploadFileNames.get(localFileID)}</span>
{uploadPhase == "uploading" && (
<>
{" "}
<span className="separator">{`-`}</span>
<span>{`${progress}%`}</span>
</>
)}
</InProgressItemContainer>
);
};
const getItemTitle = ({ localFileID, progress }) => {
return `${uploadFileNames.get(localFileID)} - ${progress}%`;
};
const generateItemKey = ({ localFileID, progress }) => {
return `${localFileID}-${progress}`;
};
return (
<UploadProgressSection>
<UploadProgressSectionTitle expandIcon={<ExpandMoreIcon />}>
<TitleText
title={t("INPROGRESS_UPLOADS")}
count={inProgressUploads?.length}
/>
</UploadProgressSectionTitle>
<UploadProgressSectionContent>
{hasLivePhotos && (
<SectionInfo>{t("LIVE_PHOTOS_DETECTED")}</SectionInfo>
)}
<ItemList
items={fileList}
generateItemKey={generateItemKey}
getItemTitle={getItemTitle}
renderListItem={renderListItem}
maxHeight={160}
itemSize={35}
/>
</UploadProgressSectionContent>
</UploadProgressSection>
);
};
const InProgressItemContainer = styled("div")`
display: inline-block;
& > span {
display: inline-block;
}
& > span:first-of-type {
position: relative;
top: 5px;
max-width: 340px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
& > .separator {
margin: 0 5px;
}
`;
const UploadProgressSection = styled((props: AccordionProps) => (
<Accordion disableGutters elevation={0} square {...props} />
))(({ theme }) => ({
borderTop: `1px solid ${theme.palette.divider}`,
"&:last-child": {
borderBottom: `1px solid ${theme.palette.divider}`,
},
"&:before": {
display: "none",
},
}));
const UploadProgressSectionTitle = styled(AccordionSummary)(() => ({
backgroundColor: "rgba(255, 255, 255, .05)",
}));
const UploadProgressSectionContent = styled(AccordionDetails)(({ theme }) => ({
padding: theme.spacing(2),
}));
const SectionInfo = (props: TypographyProps) => (
<Typography
color={"text.muted"}
variant="small"
{...props}
sx={{ mb: 1 }}
/>
);
const NotUploadSectionHeader = styled("div")(
({ theme }) => `
text-align: center;
color: ${theme.colors.danger.A700};
border-bottom: 1px solid ${theme.colors.danger.A700};
margin:${theme.spacing(3, 2, 1)}
`,
);
interface ResultSectionProps {
uploadResult: UPLOAD_RESULT;
sectionTitle: string;
sectionInfo?: React.ReactNode;
}
const ResultSection: React.FC<ResultSectionProps> = ({
uploadResult,
sectionTitle,
sectionInfo,
}) => {
const { finishedUploads, uploadFileNames } = useContext(
UploadProgressContext,
);
const fileList = finishedUploads.get(uploadResult);
if (!fileList?.length) {
return <></>;
}
const renderListItem = (fileID) => {
return (
<ResultItemContainer key={fileID}>
{uploadFileNames.get(fileID)}
</ResultItemContainer>
);
};
const getItemTitle = (fileID) => {
return uploadFileNames.get(fileID);
};
const generateItemKey = (fileID) => {
return fileID;
};
return (
<UploadProgressSection>
<UploadProgressSectionTitle expandIcon={<ExpandMoreIcon />}>
<TitleText title={sectionTitle} count={fileList?.length} />
</UploadProgressSectionTitle>
<UploadProgressSectionContent>
{sectionInfo && <SectionInfo>{sectionInfo}</SectionInfo>}
<ItemList
items={fileList}
generateItemKey={generateItemKey}
getItemTitle={getItemTitle}
renderListItem={renderListItem}
maxHeight={160}
itemSize={35}
/>
</UploadProgressSectionContent>
</UploadProgressSection>
);
};
const ResultItemContainer = styled("div")`
position: relative;
top: 5px;
display: inline-block;
max-width: 394px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
`;
interface TitleTextProps {
title: string;
count: number | undefined;
}
const TitleText: React.FC<TitleTextProps> = ({ title, count }) => (
<VerticallyCenteredFlex gap={"4px"}>
<Typography>{title}</Typography>
<Typography variant="small" color="text.faint">
{"•"}
</Typography>
<Typography variant="small" color="text.faint">
{count ?? 0}
</Typography>
</VerticallyCenteredFlex>
);
const DoneFooter: React.FC = () => {
const { uploadPhase, finishedUploads, retryFailed, onClose } = useContext(
UploadProgressContext,
);
return (
<DialogActions>
{uploadPhase == "done" &&
(finishedUploads?.get(UPLOAD_RESULT.FAILED)?.length > 0 ||
finishedUploads?.get(UPLOAD_RESULT.BLOCKED)?.length > 0 ? (
<Button variant="contained" fullWidth onClick={retryFailed}>
{t("RETRY_FAILED")}
</Button>
) : (
<Button variant="contained" fullWidth onClick={onClose}>
{t("close")}
</Button>
))}
</DialogActions>
);
};

View File

@@ -1,42 +0,0 @@
import { type UploadPhase } from "@/new/photos/services/upload/types";
import { createContext } from "react";
import type {
InProgressUpload,
SegregatedFinishedUploads,
UploadCounter,
UploadFileNames,
} from "services/upload/uploadManager";
interface UploadProgressContextType {
open: boolean;
onClose: () => void;
uploadCounter: UploadCounter;
uploadPhase: UploadPhase;
percentComplete: number;
retryFailed: () => void;
inProgressUploads: InProgressUpload[];
uploadFileNames: UploadFileNames;
finishedUploads: SegregatedFinishedUploads;
hasLivePhotos: boolean;
expanded: boolean;
setExpanded: React.Dispatch<React.SetStateAction<boolean>>;
}
const defaultUploadProgressContext: UploadProgressContextType = {
open: null,
onClose: () => null,
uploadCounter: null,
uploadPhase: undefined,
percentComplete: null,
retryFailed: () => null,
inProgressUploads: null,
uploadFileNames: null,
finishedUploads: null,
hasLivePhotos: null,
expanded: null,
setExpanded: () => null,
};
const UploadProgressContext = createContext<UploadProgressContextType>(
defaultUploadProgressContext,
);
export default UploadProgressContext;

View File

@@ -1,331 +0,0 @@
import { UPLOAD_RESULT } from "@/new/photos/services/upload/types";
import { VerticallyCenteredFlex } from "@ente/shared/components/Container";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import {
Accordion,
AccordionDetails,
AccordionSummary,
Button,
Dialog,
DialogActions,
DialogContent,
styled,
Typography,
type AccordionProps,
type DialogProps,
type TypographyProps,
} from "@mui/material";
import ItemList from "components/ItemList";
import { t } from "i18next";
import React, { useContext, useEffect, useState } from "react";
import { Trans } from "react-i18next";
import UploadProgressContext from "./context";
import { UploadProgressHeader } from "./header";
export function UploadProgressDialog() {
const { open, onClose, uploadPhase, finishedUploads } = useContext(
UploadProgressContext,
);
const [hasUnUploadedFiles, setHasUnUploadedFiles] = useState(false);
useEffect(() => {
if (
finishedUploads.get(UPLOAD_RESULT.ALREADY_UPLOADED)?.length > 0 ||
finishedUploads.get(UPLOAD_RESULT.BLOCKED)?.length > 0 ||
finishedUploads.get(UPLOAD_RESULT.FAILED)?.length > 0 ||
finishedUploads.get(UPLOAD_RESULT.LARGER_THAN_AVAILABLE_STORAGE)
?.length > 0 ||
finishedUploads.get(UPLOAD_RESULT.TOO_LARGE)?.length > 0 ||
finishedUploads.get(UPLOAD_RESULT.UNSUPPORTED)?.length > 0
) {
setHasUnUploadedFiles(true);
} else {
setHasUnUploadedFiles(false);
}
}, [finishedUploads]);
const handleClose: DialogProps["onClose"] = (_, reason) => {
if (reason != "backdropClick") onClose();
};
return (
<Dialog open={open} onClose={handleClose} maxWidth="xs" fullWidth>
<UploadProgressHeader />
{(uploadPhase == "uploading" || uploadPhase == "done") && (
<DialogContent sx={{ "&&&": { px: 0 } }}>
{uploadPhase === "uploading" && <InProgressSection />}
<ResultSection
uploadResult={UPLOAD_RESULT.UPLOADED}
sectionTitle={t("SUCCESSFUL_UPLOADS")}
/>
<ResultSection
uploadResult={
UPLOAD_RESULT.UPLOADED_WITH_STATIC_THUMBNAIL
}
sectionTitle={t("THUMBNAIL_GENERATION_FAILED_UPLOADS")}
sectionInfo={t("THUMBNAIL_GENERATION_FAILED_INFO")}
/>
{uploadPhase == "done" && hasUnUploadedFiles && (
<NotUploadSectionHeader>
{t("FILE_NOT_UPLOADED_LIST")}
</NotUploadSectionHeader>
)}
<ResultSection
uploadResult={UPLOAD_RESULT.BLOCKED}
sectionTitle={t("BLOCKED_UPLOADS")}
sectionInfo={<Trans i18nKey={"ETAGS_BLOCKED"} />}
/>
<ResultSection
uploadResult={UPLOAD_RESULT.FAILED}
sectionTitle={t("FAILED_UPLOADS")}
sectionInfo={
uploadPhase == "done"
? undefined
: t("failed_uploads_hint")
}
/>
<ResultSection
uploadResult={UPLOAD_RESULT.ALREADY_UPLOADED}
sectionTitle={t("SKIPPED_FILES")}
sectionInfo={t("SKIPPED_INFO")}
/>
<ResultSection
uploadResult={
UPLOAD_RESULT.LARGER_THAN_AVAILABLE_STORAGE
}
sectionTitle={t(
"LARGER_THAN_AVAILABLE_STORAGE_UPLOADS",
)}
sectionInfo={t("LARGER_THAN_AVAILABLE_STORAGE_INFO")}
/>
<ResultSection
uploadResult={UPLOAD_RESULT.UNSUPPORTED}
sectionTitle={t("UNSUPPORTED_FILES")}
sectionInfo={t("UNSUPPORTED_INFO")}
/>
<ResultSection
uploadResult={UPLOAD_RESULT.TOO_LARGE}
sectionTitle={t("TOO_LARGE_UPLOADS")}
sectionInfo={t("TOO_LARGE_INFO")}
/>
</DialogContent>
)}
{uploadPhase == "done" && <DoneFooter />}
</Dialog>
);
}
const InProgressSection: React.FC = () => {
const { inProgressUploads, hasLivePhotos, uploadFileNames, uploadPhase } =
useContext(UploadProgressContext);
const fileList = inProgressUploads ?? [];
const renderListItem = ({ localFileID, progress }) => {
return (
<InProgressItemContainer key={localFileID}>
<span>{uploadFileNames.get(localFileID)}</span>
{uploadPhase == "uploading" && (
<>
{" "}
<span className="separator">{`-`}</span>
<span>{`${progress}%`}</span>
</>
)}
</InProgressItemContainer>
);
};
const getItemTitle = ({ localFileID, progress }) => {
return `${uploadFileNames.get(localFileID)} - ${progress}%`;
};
const generateItemKey = ({ localFileID, progress }) => {
return `${localFileID}-${progress}`;
};
return (
<UploadProgressSection>
<UploadProgressSectionTitle expandIcon={<ExpandMoreIcon />}>
<TitleText
title={t("INPROGRESS_UPLOADS")}
count={inProgressUploads?.length}
/>
</UploadProgressSectionTitle>
<UploadProgressSectionContent>
{hasLivePhotos && (
<SectionInfo>{t("LIVE_PHOTOS_DETECTED")}</SectionInfo>
)}
<ItemList
items={fileList}
generateItemKey={generateItemKey}
getItemTitle={getItemTitle}
renderListItem={renderListItem}
maxHeight={160}
itemSize={35}
/>
</UploadProgressSectionContent>
</UploadProgressSection>
);
};
const InProgressItemContainer = styled("div")`
display: inline-block;
& > span {
display: inline-block;
}
& > span:first-of-type {
position: relative;
top: 5px;
max-width: 340px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
& > .separator {
margin: 0 5px;
}
`;
const UploadProgressSection = styled((props: AccordionProps) => (
<Accordion disableGutters elevation={0} square {...props} />
))(({ theme }) => ({
borderTop: `1px solid ${theme.palette.divider}`,
"&:last-child": {
borderBottom: `1px solid ${theme.palette.divider}`,
},
"&:before": {
display: "none",
},
}));
const UploadProgressSectionTitle = styled(AccordionSummary)(() => ({
backgroundColor: "rgba(255, 255, 255, .05)",
}));
const UploadProgressSectionContent = styled(AccordionDetails)(({ theme }) => ({
padding: theme.spacing(2),
}));
const SectionInfo = (props: TypographyProps) => (
<Typography
color={"text.muted"}
variant="small"
{...props}
sx={{ mb: 1 }}
/>
);
const NotUploadSectionHeader = styled("div")(
({ theme }) => `
text-align: center;
color: ${theme.colors.danger.A700};
border-bottom: 1px solid ${theme.colors.danger.A700};
margin:${theme.spacing(3, 2, 1)}
`,
);
interface ResultSectionProps {
uploadResult: UPLOAD_RESULT;
sectionTitle: string;
sectionInfo?: React.ReactNode;
}
const ResultSection: React.FC<ResultSectionProps> = ({
uploadResult,
sectionTitle,
sectionInfo,
}) => {
const { finishedUploads, uploadFileNames } = useContext(
UploadProgressContext,
);
const fileList = finishedUploads.get(uploadResult);
if (!fileList?.length) {
return <></>;
}
const renderListItem = (fileID) => {
return (
<ResultItemContainer key={fileID}>
{uploadFileNames.get(fileID)}
</ResultItemContainer>
);
};
const getItemTitle = (fileID) => {
return uploadFileNames.get(fileID);
};
const generateItemKey = (fileID) => {
return fileID;
};
return (
<UploadProgressSection>
<UploadProgressSectionTitle expandIcon={<ExpandMoreIcon />}>
<TitleText title={sectionTitle} count={fileList?.length} />
</UploadProgressSectionTitle>
<UploadProgressSectionContent>
{sectionInfo && <SectionInfo>{sectionInfo}</SectionInfo>}
<ItemList
items={fileList}
generateItemKey={generateItemKey}
getItemTitle={getItemTitle}
renderListItem={renderListItem}
maxHeight={160}
itemSize={35}
/>
</UploadProgressSectionContent>
</UploadProgressSection>
);
};
const ResultItemContainer = styled("div")`
position: relative;
top: 5px;
display: inline-block;
max-width: 394px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
`;
interface TitleTextProps {
title: string;
count: number | undefined;
}
const TitleText: React.FC<TitleTextProps> = ({ title, count }) => (
<VerticallyCenteredFlex gap={"4px"}>
<Typography>{title}</Typography>
<Typography variant="small" color="text.faint">
{"•"}
</Typography>
<Typography variant="small" color="text.faint">
{count ?? 0}
</Typography>
</VerticallyCenteredFlex>
);
const DoneFooter: React.FC = () => {
const { uploadPhase, finishedUploads, retryFailed, onClose } = useContext(
UploadProgressContext,
);
return (
<DialogActions>
{uploadPhase == "done" &&
(finishedUploads?.get(UPLOAD_RESULT.FAILED)?.length > 0 ||
finishedUploads?.get(UPLOAD_RESULT.BLOCKED)?.length > 0 ? (
<Button variant="contained" fullWidth onClick={retryFailed}>
{t("RETRY_FAILED")}
</Button>
) : (
<Button variant="contained" fullWidth onClick={onClose}>
{t("close")}
</Button>
))}
</DialogActions>
);
};

View File

@@ -1,118 +0,0 @@
import { FilledIconButton } from "@/new/photos/components/mui";
import { type UploadPhase } from "@/new/photos/services/upload/types";
import { SpaceBetweenFlex } from "@ente/shared/components/Container";
import Close from "@mui/icons-material/Close";
import UnfoldLessIcon from "@mui/icons-material/UnfoldLess";
import UnfoldMoreIcon from "@mui/icons-material/UnfoldMore";
import {
Box,
DialogTitle,
Divider,
LinearProgress,
Stack,
Typography,
} from "@mui/material";
import { t } from "i18next";
import React, { useContext } from "react";
import type { UploadCounter } from "services/upload/uploadManager";
import UploadProgressContext from "./context";
export function UploadProgressHeader() {
return (
<>
<UploadProgressTitle />
<UploadProgressBar />
</>
);
}
const UploadProgressTitleText = ({ expanded }) => {
return (
<Typography variant={expanded ? "h2" : "h3"}>
{t("FILE_UPLOAD")}
</Typography>
);
};
function UploadProgressSubtitleText() {
const { uploadPhase, uploadCounter } = useContext(UploadProgressContext);
return (
<Typography
variant="body"
fontWeight={"normal"}
color="text.muted"
marginTop={"4px"}
>
{subtitleText(uploadPhase, uploadCounter)}
</Typography>
);
}
const subtitleText = (
uploadPhase: UploadPhase,
uploadCounter: UploadCounter,
) => {
switch (uploadPhase) {
case "preparing":
return t("UPLOAD_STAGE_MESSAGE.0");
case "readingMetadata":
return t("UPLOAD_STAGE_MESSAGE.1");
case "uploading":
return t("UPLOAD_STAGE_MESSAGE.3", { uploadCounter });
case "cancelling":
return t("UPLOAD_STAGE_MESSAGE.4");
case "done":
return t("UPLOAD_STAGE_MESSAGE.5");
}
};
const UploadProgressTitle: React.FC = () => {
const { setExpanded, onClose, expanded } = useContext(
UploadProgressContext,
);
const toggleExpanded = () => setExpanded((expanded) => !expanded);
return (
<DialogTitle>
<SpaceBetweenFlex>
<Box>
<UploadProgressTitleText expanded={expanded} />
<UploadProgressSubtitleText />
</Box>
<Box>
<Stack direction={"row"} spacing={1}>
<FilledIconButton onClick={toggleExpanded}>
{expanded ? <UnfoldLessIcon /> : <UnfoldMoreIcon />}
</FilledIconButton>
<FilledIconButton onClick={onClose}>
<Close />
</FilledIconButton>
</Stack>
</Box>
</SpaceBetweenFlex>
</DialogTitle>
);
};
const UploadProgressBar: React.FC = () => {
const { uploadPhase, percentComplete } = useContext(UploadProgressContext);
return (
<Box>
{(uploadPhase == "readingMetadata" ||
uploadPhase == "uploading") && (
<>
<LinearProgress
sx={{
height: "2px",
backgroundColor: "transparent",
}}
variant="determinate"
value={percentComplete}
/>
<Divider />
</>
)}
</Box>
);
};