From fc114c7d92ea3c38acfa4491e73ddc0d7e7fa12b Mon Sep 17 00:00:00 2001 From: Manav Rathi Date: Wed, 13 Nov 2024 07:12:45 +0530 Subject: [PATCH] Inline fin --- .../src/components/Upload/UploadProgress.tsx | 481 +++++++++++++++++- .../Upload/UploadProgress/context.tsx | 42 -- .../Upload/UploadProgress/dialog.tsx | 331 ------------ .../Upload/UploadProgress/header.tsx | 118 ----- 4 files changed, 475 insertions(+), 497 deletions(-) delete mode 100644 web/apps/photos/src/components/Upload/UploadProgress/context.tsx delete mode 100644 web/apps/photos/src/components/Upload/UploadProgress/dialog.tsx delete mode 100644 web/apps/photos/src/components/Upload/UploadProgress/header.tsx diff --git a/web/apps/photos/src/components/Upload/UploadProgress.tsx b/web/apps/photos/src/components/Upload/UploadProgress.tsx index f84dd87541..67ac3a1a4a 100644 --- a/web/apps/photos/src/components/Upload/UploadProgress.tsx +++ b/web/apps/photos/src/components/Upload/UploadProgress.tsx @@ -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 = ({ ); }; +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>; +} + +const UploadProgressContext = createContext({ + 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 = () => ( @@ -97,3 +158,411 @@ const MinimizedUploadProgress: React.FC = () => ( ); + +function UploadProgressHeader() { + return ( + <> + + + + ); +} + +const UploadProgressTitleText = ({ expanded }) => { + return ( + + {t("FILE_UPLOAD")} + + ); +}; + +function UploadProgressSubtitleText() { + const { uploadPhase, uploadCounter } = useContext(UploadProgressContext); + + return ( + + {subtitleText(uploadPhase, uploadCounter)} + + ); +} + +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 ( + + + + + + + + + + {expanded ? : } + + + + + + + + + ); +}; + +const UploadProgressBar: React.FC = () => { + const { uploadPhase, percentComplete } = useContext(UploadProgressContext); + return ( + + {(uploadPhase == "readingMetadata" || + uploadPhase == "uploading") && ( + <> + + + + )} + + ); +}; + +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 ( + + + {(uploadPhase == "uploading" || uploadPhase == "done") && ( + + {uploadPhase === "uploading" && } + + + {uploadPhase == "done" && hasUnUploadedFiles && ( + + {t("FILE_NOT_UPLOADED_LIST")} + + )} + } + /> + + + + + + + )} + {uploadPhase == "done" && } + + ); +} + +const InProgressSection: React.FC = () => { + const { inProgressUploads, hasLivePhotos, uploadFileNames, uploadPhase } = + useContext(UploadProgressContext); + const fileList = inProgressUploads ?? []; + + const renderListItem = ({ localFileID, progress }) => { + return ( + + {uploadFileNames.get(localFileID)} + {uploadPhase == "uploading" && ( + <> + {" "} + {`-`} + {`${progress}%`} + + )} + + ); + }; + + const getItemTitle = ({ localFileID, progress }) => { + return `${uploadFileNames.get(localFileID)} - ${progress}%`; + }; + + const generateItemKey = ({ localFileID, progress }) => { + return `${localFileID}-${progress}`; + }; + + return ( + + }> + + + + {hasLivePhotos && ( + {t("LIVE_PHOTOS_DETECTED")} + )} + + + + ); +}; + +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) => ( + +))(({ 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) => ( + +); + +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 = ({ + uploadResult, + sectionTitle, + sectionInfo, +}) => { + const { finishedUploads, uploadFileNames } = useContext( + UploadProgressContext, + ); + const fileList = finishedUploads.get(uploadResult); + + if (!fileList?.length) { + return <>; + } + + const renderListItem = (fileID) => { + return ( + + {uploadFileNames.get(fileID)} + + ); + }; + + const getItemTitle = (fileID) => { + return uploadFileNames.get(fileID); + }; + + const generateItemKey = (fileID) => { + return fileID; + }; + + return ( + + }> + + + + {sectionInfo && {sectionInfo}} + + + + ); +}; + +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 = ({ title, count }) => ( + + {title} + + {"•"} + + + {count ?? 0} + + +); + +const DoneFooter: React.FC = () => { + const { uploadPhase, finishedUploads, retryFailed, onClose } = useContext( + UploadProgressContext, + ); + + return ( + + {uploadPhase == "done" && + (finishedUploads?.get(UPLOAD_RESULT.FAILED)?.length > 0 || + finishedUploads?.get(UPLOAD_RESULT.BLOCKED)?.length > 0 ? ( + + ) : ( + + ))} + + ); +}; diff --git a/web/apps/photos/src/components/Upload/UploadProgress/context.tsx b/web/apps/photos/src/components/Upload/UploadProgress/context.tsx deleted file mode 100644 index dce32b9a52..0000000000 --- a/web/apps/photos/src/components/Upload/UploadProgress/context.tsx +++ /dev/null @@ -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>; -} -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( - defaultUploadProgressContext, -); - -export default UploadProgressContext; diff --git a/web/apps/photos/src/components/Upload/UploadProgress/dialog.tsx b/web/apps/photos/src/components/Upload/UploadProgress/dialog.tsx deleted file mode 100644 index 314f0da050..0000000000 --- a/web/apps/photos/src/components/Upload/UploadProgress/dialog.tsx +++ /dev/null @@ -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 ( - - - {(uploadPhase == "uploading" || uploadPhase == "done") && ( - - {uploadPhase === "uploading" && } - - - {uploadPhase == "done" && hasUnUploadedFiles && ( - - {t("FILE_NOT_UPLOADED_LIST")} - - )} - } - /> - - - - - - - )} - {uploadPhase == "done" && } - - ); -} - -const InProgressSection: React.FC = () => { - const { inProgressUploads, hasLivePhotos, uploadFileNames, uploadPhase } = - useContext(UploadProgressContext); - const fileList = inProgressUploads ?? []; - - const renderListItem = ({ localFileID, progress }) => { - return ( - - {uploadFileNames.get(localFileID)} - {uploadPhase == "uploading" && ( - <> - {" "} - {`-`} - {`${progress}%`} - - )} - - ); - }; - - const getItemTitle = ({ localFileID, progress }) => { - return `${uploadFileNames.get(localFileID)} - ${progress}%`; - }; - - const generateItemKey = ({ localFileID, progress }) => { - return `${localFileID}-${progress}`; - }; - - return ( - - }> - - - - {hasLivePhotos && ( - {t("LIVE_PHOTOS_DETECTED")} - )} - - - - ); -}; - -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) => ( - -))(({ 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) => ( - -); - -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 = ({ - uploadResult, - sectionTitle, - sectionInfo, -}) => { - const { finishedUploads, uploadFileNames } = useContext( - UploadProgressContext, - ); - const fileList = finishedUploads.get(uploadResult); - - if (!fileList?.length) { - return <>; - } - - const renderListItem = (fileID) => { - return ( - - {uploadFileNames.get(fileID)} - - ); - }; - - const getItemTitle = (fileID) => { - return uploadFileNames.get(fileID); - }; - - const generateItemKey = (fileID) => { - return fileID; - }; - - return ( - - }> - - - - {sectionInfo && {sectionInfo}} - - - - ); -}; - -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 = ({ title, count }) => ( - - {title} - - {"•"} - - - {count ?? 0} - - -); - -const DoneFooter: React.FC = () => { - const { uploadPhase, finishedUploads, retryFailed, onClose } = useContext( - UploadProgressContext, - ); - - return ( - - {uploadPhase == "done" && - (finishedUploads?.get(UPLOAD_RESULT.FAILED)?.length > 0 || - finishedUploads?.get(UPLOAD_RESULT.BLOCKED)?.length > 0 ? ( - - ) : ( - - ))} - - ); -}; diff --git a/web/apps/photos/src/components/Upload/UploadProgress/header.tsx b/web/apps/photos/src/components/Upload/UploadProgress/header.tsx deleted file mode 100644 index d73b17e138..0000000000 --- a/web/apps/photos/src/components/Upload/UploadProgress/header.tsx +++ /dev/null @@ -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 ( - <> - - - - ); -} - -const UploadProgressTitleText = ({ expanded }) => { - return ( - - {t("FILE_UPLOAD")} - - ); -}; - -function UploadProgressSubtitleText() { - const { uploadPhase, uploadCounter } = useContext(UploadProgressContext); - - return ( - - {subtitleText(uploadPhase, uploadCounter)} - - ); -} - -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 ( - - - - - - - - - - {expanded ? : } - - - - - - - - - ); -}; - -const UploadProgressBar: React.FC = () => { - const { uploadPhase, percentComplete } = useContext(UploadProgressContext); - return ( - - {(uploadPhase == "readingMetadata" || - uploadPhase == "uploading") && ( - <> - - - - )} - - ); -};