This commit is contained in:
Manav Rathi
2024-11-13 06:43:21 +05:30
parent 604c4462fe
commit 0794f570a8
9 changed files with 296 additions and 313 deletions

View File

@@ -1,14 +1,26 @@
import { UPLOAD_RESULT } from "@/new/photos/services/upload/types";
import { Dialog, DialogContent, type DialogProps } from "@mui/material";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import {
Button,
Dialog,
DialogActions,
DialogContent,
styled,
type DialogProps,
} from "@mui/material";
import { CaptionedText } from "components/CaptionedText";
import ItemList from "components/ItemList";
import { t } from "i18next";
import { useContext, useEffect, useState } from "react";
import React, { useContext, useEffect, useState } from "react";
import { Trans } from "react-i18next";
import UploadProgressContext from "./context";
import { UploadProgressFooter } from "./footer";
import { UploadProgressHeader } from "./header";
import { InProgressSection } from "./inProgressSection";
import { ResultSection } from "./resultSection";
import { NotUploadSectionHeader } from "./styledComponents";
import {
SectionInfo,
UploadProgressSection,
UploadProgressSectionContent,
UploadProgressSectionTitle,
} from "./section";
export function UploadProgressDialog() {
const { open, onClose, uploadPhase, finishedUploads } = useContext(
@@ -103,3 +115,172 @@ export function UploadProgressDialog() {
</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 />}>
<CaptionedText
mainText={t("INPROGRESS_UPLOADS")}
subText={String(inProgressUploads?.length ?? 0)}
/>
</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 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)}
`,
);
const UploadProgressFooter: 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>
);
};
interface ResultSectionProps {
uploadResult: UPLOAD_RESULT;
sectionTitle: any;
sectionInfo?: any;
}
const ResultSection = (props: ResultSectionProps) => {
const { finishedUploads, uploadFileNames } = useContext(
UploadProgressContext,
);
const fileList = finishedUploads.get(props.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 />}>
<CaptionedText
mainText={props.sectionTitle}
subText={String(fileList?.length ?? 0)}
/>
</UploadProgressSectionTitle>
<UploadProgressSectionContent>
{props.sectionInfo && (
<SectionInfo>{props.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;
`;

View File

@@ -1,27 +0,0 @@
import { UPLOAD_RESULT } from "@/new/photos/services/upload/types";
import { Button, DialogActions } from "@mui/material";
import { t } from "i18next";
import { useContext } from "react";
import UploadProgressContext from "./context";
export function UploadProgressFooter() {
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,5 +1,21 @@
import { UploadProgressBar } from "./progressBar";
import { UploadProgressTitle } from "./title";
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 (
@@ -9,3 +25,94 @@ export function UploadProgressHeader() {
</>
);
}
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>
);
};

View File

@@ -1,67 +0,0 @@
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import ItemList from "components/ItemList";
import { t } from "i18next";
import { useContext } from "react";
import UploadProgressContext from "./context";
import {
SectionInfo,
UploadProgressSection,
UploadProgressSectionContent,
UploadProgressSectionTitle,
} from "./section";
import { InProgressItemContainer } from "./styledComponents";
import { CaptionedText } from "components/CaptionedText";
export const InProgressSection = () => {
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 />}>
<CaptionedText
mainText={t("INPROGRESS_UPLOADS")}
subText={String(inProgressUploads?.length ?? 0)}
/>
</UploadProgressSectionTitle>
<UploadProgressSectionContent>
{hasLivePhotos && (
<SectionInfo>{t("LIVE_PHOTOS_DETECTED")}</SectionInfo>
)}
<ItemList
items={fileList}
generateItemKey={generateItemKey}
getItemTitle={getItemTitle}
renderListItem={renderListItem}
maxHeight={160}
itemSize={35}
/>
</UploadProgressSectionContent>
</UploadProgressSection>
);
};

View File

@@ -1,25 +0,0 @@
import { Box, Divider, LinearProgress } from "@mui/material";
import { useContext } from "react";
import UploadProgressContext from "./context";
export function UploadProgressBar() {
const { uploadPhase, percentComplete } = useContext(UploadProgressContext);
return (
<Box>
{(uploadPhase == "readingMetadata" ||
uploadPhase == "uploading") && (
<>
<LinearProgress
sx={{
height: "2px",
backgroundColor: "transparent",
}}
variant="determinate"
value={percentComplete}
/>
<Divider />
</>
)}
</Box>
);
}

View File

@@ -1,69 +0,0 @@
import { UPLOAD_RESULT } from "@/new/photos/services/upload/types";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import { CaptionedText } from "components/CaptionedText";
import ItemList from "components/ItemList";
import { useContext } from "react";
import UploadProgressContext from "./context";
import {
SectionInfo,
UploadProgressSection,
UploadProgressSectionContent,
UploadProgressSectionTitle,
} from "./section";
import { ResultItemContainer } from "./styledComponents";
export interface ResultSectionProps {
uploadResult: UPLOAD_RESULT;
sectionTitle: any;
sectionInfo?: any;
}
export const ResultSection = (props: ResultSectionProps) => {
const { finishedUploads, uploadFileNames } = useContext(
UploadProgressContext,
);
const fileList = finishedUploads.get(props.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 />}>
<CaptionedText
mainText={props.sectionTitle}
subText={String(fileList?.length ?? 0)}
/>
</UploadProgressSectionTitle>
<UploadProgressSectionContent>
{props.sectionInfo && (
<SectionInfo>{props.sectionInfo}</SectionInfo>
)}
<ItemList
items={fileList}
generateItemKey={generateItemKey}
getItemTitle={getItemTitle}
renderListItem={renderListItem}
maxHeight={160}
itemSize={35}
/>
</UploadProgressSectionContent>
</UploadProgressSection>
);
};

View File

@@ -1,37 +0,0 @@
import { styled } from "@mui/material";
export 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)}
`,
);
export 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;
}
`;
export const ResultItemContainer = styled("div")`
position: relative;
top: 5px;
display: inline-block;
max-width: 394px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
`;

View File

@@ -1,80 +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, Stack, Typography } from "@mui/material";
import { t } from "i18next";
import { useContext } from "react";
import type { UploadCounter } from "services/upload/uploadManager";
import UploadProgressContext from "./context";
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");
}
};
export function UploadProgressTitle() {
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>
);
}