reorganize UploadProgress
This commit is contained in:
parent
0ab97288c5
commit
4c54d2902b
38
src/components/UploadProgress/InProgressSection.tsx
Normal file
38
src/components/UploadProgress/InProgressSection.tsx
Normal file
|
@ -0,0 +1,38 @@
|
|||
import React from 'react';
|
||||
import FileList from 'components/FileList';
|
||||
import { Accordion, AccordionDetails, AccordionSummary } from '@mui/material';
|
||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||
import { SectionInfo, InProgressItemContainer } from './styledComponents';
|
||||
import { FileProgresses } from '.';
|
||||
|
||||
export interface InProgressProps {
|
||||
filenames: Map<number, string>;
|
||||
sectionTitle: string;
|
||||
fileProgressStatuses: FileProgresses[];
|
||||
sectionInfo?: any;
|
||||
}
|
||||
export const InProgressSection = (props: InProgressProps) => {
|
||||
const fileList = props.fileProgressStatuses ?? [];
|
||||
|
||||
return (
|
||||
<Accordion>
|
||||
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
|
||||
{props.sectionTitle}
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
{props.sectionInfo && (
|
||||
<SectionInfo>{props.sectionInfo}</SectionInfo>
|
||||
)}
|
||||
<FileList
|
||||
fileList={fileList.map(({ fileID, progress }) => (
|
||||
<InProgressItemContainer key={fileID}>
|
||||
<span>{props.filenames.get(fileID)}</span>
|
||||
<span className="separator">{`-`}</span>
|
||||
<span>{`${progress}%`}</span>
|
||||
</InProgressItemContainer>
|
||||
))}
|
||||
/>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
);
|
||||
};
|
21
src/components/UploadProgress/Minimized.tsx
Normal file
21
src/components/UploadProgress/Minimized.tsx
Normal file
|
@ -0,0 +1,21 @@
|
|||
import { Snackbar, Paper } from '@mui/material';
|
||||
import React from 'react';
|
||||
import { UploadProgressHeader } from './header';
|
||||
export function MinimizedUploadProgress(props) {
|
||||
return (
|
||||
<Snackbar
|
||||
action={<></>}
|
||||
open={true}
|
||||
anchorOrigin={{
|
||||
horizontal: 'right',
|
||||
vertical: 'bottom',
|
||||
}}>
|
||||
<Paper
|
||||
sx={{
|
||||
width: '360px',
|
||||
}}>
|
||||
<UploadProgressHeader {...props} />
|
||||
</Paper>
|
||||
</Snackbar>
|
||||
);
|
||||
}
|
44
src/components/UploadProgress/ResultSection.tsx
Normal file
44
src/components/UploadProgress/ResultSection.tsx
Normal file
|
@ -0,0 +1,44 @@
|
|||
import React from 'react';
|
||||
import FileList from 'components/FileList';
|
||||
import {
|
||||
Accordion,
|
||||
AccordionDetails,
|
||||
AccordionSummary,
|
||||
Typography,
|
||||
} from '@mui/material';
|
||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||
import { SectionInfo, ResultItemContainer } from './styledComponents';
|
||||
import { FileUploadResults } from 'constants/upload';
|
||||
|
||||
export interface ResultSectionProps {
|
||||
filenames: Map<number, string>;
|
||||
fileUploadResultMap: Map<FileUploadResults, number[]>;
|
||||
fileUploadResult: FileUploadResults;
|
||||
sectionTitle: any;
|
||||
sectionInfo?: any;
|
||||
}
|
||||
export const ResultSection = (props: ResultSectionProps) => {
|
||||
const fileList = props.fileUploadResultMap?.get(props.fileUploadResult);
|
||||
if (!fileList?.length) {
|
||||
return <></>;
|
||||
}
|
||||
return (
|
||||
<Accordion>
|
||||
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
|
||||
<Typography> {props.sectionTitle}</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
{props.sectionInfo && (
|
||||
<SectionInfo>{props.sectionInfo}</SectionInfo>
|
||||
)}
|
||||
<FileList
|
||||
fileList={fileList.map((fileID) => (
|
||||
<ResultItemContainer key={fileID}>
|
||||
{props.filenames.get(fileID)}
|
||||
</ResultItemContainer>
|
||||
))}
|
||||
/>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
);
|
||||
};
|
121
src/components/UploadProgress/dialog.tsx
Normal file
121
src/components/UploadProgress/dialog.tsx
Normal file
|
@ -0,0 +1,121 @@
|
|||
import { Dialog, DialogContent } from '@mui/material';
|
||||
import constants from 'utils/strings/constants';
|
||||
import { UPLOAD_STAGES, FileUploadResults } from 'constants/upload';
|
||||
import React from 'react';
|
||||
import { UploadProgressFooter } from './footer';
|
||||
import { UploadProgressHeader } from './header';
|
||||
import { InProgressSection } from './inProgressSection';
|
||||
import { ResultSection } from './resultSection';
|
||||
import { NotUploadSectionHeader } from './styledComponents';
|
||||
import { DESKTOP_APP_DOWNLOAD_URL } from 'utils/common';
|
||||
export function UploadProgressDialog({
|
||||
handleHideModal,
|
||||
setExpanded,
|
||||
expanded,
|
||||
fileProgressStatuses,
|
||||
sectionInfo,
|
||||
fileUploadResultMap,
|
||||
filesNotUploaded,
|
||||
...props
|
||||
}) {
|
||||
return (
|
||||
<Dialog
|
||||
maxWidth="xs"
|
||||
fullWidth
|
||||
open={props.show}
|
||||
onClose={handleHideModal}>
|
||||
<UploadProgressHeader
|
||||
uploadStage={props.uploadStage}
|
||||
setExpanded={setExpanded}
|
||||
expanded={expanded}
|
||||
handleHideModal={handleHideModal}
|
||||
fileCounter={props.fileCounter}
|
||||
now={props.now}
|
||||
/>
|
||||
<DialogContent
|
||||
sx={{
|
||||
px: 0,
|
||||
}}>
|
||||
{props.uploadStage === UPLOAD_STAGES.UPLOADING && (
|
||||
<InProgressSection
|
||||
filenames={props.filenames}
|
||||
fileProgressStatuses={fileProgressStatuses}
|
||||
sectionTitle={constants.INPROGRESS_UPLOADS}
|
||||
sectionInfo={sectionInfo}
|
||||
/>
|
||||
)}
|
||||
|
||||
<ResultSection
|
||||
filenames={props.filenames}
|
||||
fileUploadResultMap={fileUploadResultMap}
|
||||
fileUploadResult={FileUploadResults.UPLOADED}
|
||||
sectionTitle={constants.SUCCESSFUL_UPLOADS}
|
||||
/>
|
||||
|
||||
{props.uploadStage === UPLOAD_STAGES.FINISH &&
|
||||
filesNotUploaded && (
|
||||
<NotUploadSectionHeader>
|
||||
{constants.FILE_NOT_UPLOADED_LIST}
|
||||
</NotUploadSectionHeader>
|
||||
)}
|
||||
|
||||
<ResultSection
|
||||
filenames={props.filenames}
|
||||
fileUploadResultMap={fileUploadResultMap}
|
||||
fileUploadResult={FileUploadResults.BLOCKED}
|
||||
sectionTitle={constants.BLOCKED_UPLOADS}
|
||||
sectionInfo={constants.ETAGS_BLOCKED(
|
||||
DESKTOP_APP_DOWNLOAD_URL
|
||||
)}
|
||||
/>
|
||||
<ResultSection
|
||||
filenames={props.filenames}
|
||||
fileUploadResultMap={fileUploadResultMap}
|
||||
fileUploadResult={FileUploadResults.FAILED}
|
||||
sectionTitle={constants.FAILED_UPLOADS}
|
||||
/>
|
||||
<ResultSection
|
||||
filenames={props.filenames}
|
||||
fileUploadResultMap={fileUploadResultMap}
|
||||
fileUploadResult={FileUploadResults.ALREADY_UPLOADED}
|
||||
sectionTitle={constants.SKIPPED_FILES}
|
||||
sectionInfo={constants.SKIPPED_INFO}
|
||||
/>
|
||||
<ResultSection
|
||||
filenames={props.filenames}
|
||||
fileUploadResultMap={fileUploadResultMap}
|
||||
fileUploadResult={
|
||||
FileUploadResults.LARGER_THAN_AVAILABLE_STORAGE
|
||||
}
|
||||
sectionTitle={
|
||||
constants.LARGER_THAN_AVAILABLE_STORAGE_UPLOADS
|
||||
}
|
||||
sectionInfo={constants.LARGER_THAN_AVAILABLE_STORAGE_INFO}
|
||||
/>
|
||||
<ResultSection
|
||||
filenames={props.filenames}
|
||||
fileUploadResultMap={fileUploadResultMap}
|
||||
fileUploadResult={FileUploadResults.UNSUPPORTED}
|
||||
sectionTitle={constants.UNSUPPORTED_FILES}
|
||||
sectionInfo={constants.UNSUPPORTED_INFO}
|
||||
/>
|
||||
<ResultSection
|
||||
filenames={props.filenames}
|
||||
fileUploadResultMap={fileUploadResultMap}
|
||||
fileUploadResult={FileUploadResults.TOO_LARGE}
|
||||
sectionTitle={constants.TOO_LARGE_UPLOADS}
|
||||
sectionInfo={constants.TOO_LARGE_INFO}
|
||||
/>
|
||||
</DialogContent>
|
||||
|
||||
{props.uploadStage === UPLOAD_STAGES.FINISH && (
|
||||
<UploadProgressFooter
|
||||
uploadStage={props.uploadStage}
|
||||
retryFailed={props.retryFailed}
|
||||
closeModal={props.cancelUploads}
|
||||
fileUploadResultMap={fileUploadResultMap}
|
||||
/>
|
||||
)}
|
||||
</Dialog>
|
||||
);
|
||||
}
|
28
src/components/UploadProgress/footer.tsx
Normal file
28
src/components/UploadProgress/footer.tsx
Normal file
|
@ -0,0 +1,28 @@
|
|||
import { Button, DialogActions } from '@mui/material';
|
||||
import { UPLOAD_STAGES, FileUploadResults } from 'constants/upload';
|
||||
import React from 'react';
|
||||
import constants from 'utils/strings/constants';
|
||||
export function UploadProgressFooter({
|
||||
uploadStage,
|
||||
fileUploadResultMap,
|
||||
retryFailed,
|
||||
closeModal,
|
||||
}) {
|
||||
return (
|
||||
<DialogActions>
|
||||
{uploadStage === UPLOAD_STAGES.FINISH &&
|
||||
(fileUploadResultMap?.get(FileUploadResults.FAILED)?.length >
|
||||
0 ||
|
||||
fileUploadResultMap?.get(FileUploadResults.BLOCKED)?.length >
|
||||
0 ? (
|
||||
<Button variant="contained" fullWidth onClick={retryFailed}>
|
||||
{constants.RETRY_FAILED}
|
||||
</Button>
|
||||
) : (
|
||||
<Button variant="contained" fullWidth onClick={closeModal}>
|
||||
{constants.CLOSE}
|
||||
</Button>
|
||||
))}
|
||||
</DialogActions>
|
||||
);
|
||||
}
|
25
src/components/UploadProgress/header.tsx
Normal file
25
src/components/UploadProgress/header.tsx
Normal file
|
@ -0,0 +1,25 @@
|
|||
import React from 'react';
|
||||
import { UploadProgressBar } from './progressBar';
|
||||
import { UploadProgressTitle } from './title';
|
||||
|
||||
export function UploadProgressHeader({
|
||||
uploadStage,
|
||||
setExpanded,
|
||||
expanded,
|
||||
handleHideModal,
|
||||
fileCounter,
|
||||
now,
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<UploadProgressTitle
|
||||
uploadStage={uploadStage}
|
||||
setExpanded={setExpanded}
|
||||
expanded={expanded}
|
||||
handleHideModal={handleHideModal}
|
||||
fileCounter={fileCounter}
|
||||
/>
|
||||
<UploadProgressBar now={now} uploadStage={uploadStage} />
|
||||
</>
|
||||
);
|
||||
}
|
38
src/components/UploadProgress/inProgressSection.tsx
Normal file
38
src/components/UploadProgress/inProgressSection.tsx
Normal file
|
@ -0,0 +1,38 @@
|
|||
import React from 'react';
|
||||
import FileList from 'components/FileList';
|
||||
import { Accordion, AccordionDetails, AccordionSummary } from '@mui/material';
|
||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||
import { SectionInfo, InProgressItemContainer } from './styledComponents';
|
||||
import { FileProgresses } from '.';
|
||||
|
||||
export interface InProgressProps {
|
||||
filenames: Map<number, string>;
|
||||
sectionTitle: string;
|
||||
fileProgressStatuses: FileProgresses[];
|
||||
sectionInfo?: any;
|
||||
}
|
||||
export const InProgressSection = (props: InProgressProps) => {
|
||||
const fileList = props.fileProgressStatuses ?? [];
|
||||
|
||||
return (
|
||||
<Accordion>
|
||||
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
|
||||
{props.sectionTitle}
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
{props.sectionInfo && (
|
||||
<SectionInfo>{props.sectionInfo}</SectionInfo>
|
||||
)}
|
||||
<FileList
|
||||
fileList={fileList.map(({ fileID, progress }) => (
|
||||
<InProgressItemContainer key={fileID}>
|
||||
<span>{props.filenames.get(fileID)}</span>
|
||||
<span className="separator">{`-`}</span>
|
||||
<span>{`${progress}%`}</span>
|
||||
</InProgressItemContainer>
|
||||
))}
|
||||
/>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
);
|
||||
};
|
102
src/components/UploadProgress/index.tsx
Normal file
102
src/components/UploadProgress/index.tsx
Normal file
|
@ -0,0 +1,102 @@
|
|||
import { UploadProgressDialog } from './dialog';
|
||||
import { MinimizedUploadProgress } from './minimized';
|
||||
import React, { useContext, useState } from 'react';
|
||||
|
||||
import constants from 'utils/strings/constants';
|
||||
import { FileUploadResults, UPLOAD_STAGES } from 'constants/upload';
|
||||
import { AppContext } from 'pages/_app';
|
||||
|
||||
interface Props {
|
||||
fileCounter;
|
||||
uploadStage;
|
||||
now;
|
||||
closeModal;
|
||||
retryFailed;
|
||||
fileProgress: Map<number, number>;
|
||||
filenames: Map<number, string>;
|
||||
show;
|
||||
uploadResult: Map<number, FileUploadResults>;
|
||||
hasLivePhotos: boolean;
|
||||
cancelUploads: () => void;
|
||||
}
|
||||
export interface FileProgresses {
|
||||
fileID: number;
|
||||
progress: number;
|
||||
}
|
||||
|
||||
export default function UploadProgress(props: Props) {
|
||||
const appContext = useContext(AppContext);
|
||||
const [expanded, setExpanded] = useState(true);
|
||||
const fileProgressStatuses = [] as FileProgresses[];
|
||||
const fileUploadResultMap = new Map<FileUploadResults, number[]>();
|
||||
let filesNotUploaded = false;
|
||||
let sectionInfo = null;
|
||||
if (props.fileProgress) {
|
||||
for (const [localID, progress] of props.fileProgress) {
|
||||
fileProgressStatuses.push({
|
||||
fileID: localID,
|
||||
progress,
|
||||
});
|
||||
}
|
||||
}
|
||||
if (props.uploadResult) {
|
||||
for (const [localID, progress] of props.uploadResult) {
|
||||
if (!fileUploadResultMap.has(progress)) {
|
||||
fileUploadResultMap.set(progress, []);
|
||||
}
|
||||
if (progress !== FileUploadResults.UPLOADED) {
|
||||
filesNotUploaded = true;
|
||||
}
|
||||
const fileList = fileUploadResultMap.get(progress);
|
||||
|
||||
fileUploadResultMap.set(progress, [...fileList, localID]);
|
||||
}
|
||||
}
|
||||
if (props.hasLivePhotos) {
|
||||
sectionInfo = constants.LIVE_PHOTOS_DETECTED();
|
||||
}
|
||||
|
||||
function handleHideModal() {
|
||||
if (props.uploadStage !== UPLOAD_STAGES.FINISH) {
|
||||
appContext.setDialogMessage({
|
||||
title: constants.STOP_UPLOADS_HEADER,
|
||||
content: constants.STOP_ALL_UPLOADS_MESSAGE,
|
||||
proceed: {
|
||||
text: constants.YES_STOP_UPLOADS,
|
||||
variant: 'danger',
|
||||
action: props.cancelUploads,
|
||||
},
|
||||
close: {
|
||||
text: constants.NO,
|
||||
variant: 'secondary',
|
||||
action: () => {},
|
||||
},
|
||||
});
|
||||
} else {
|
||||
props.closeModal();
|
||||
}
|
||||
}
|
||||
return (
|
||||
<>
|
||||
{expanded ? (
|
||||
<UploadProgressDialog
|
||||
handleHideModal={handleHideModal}
|
||||
setExpanded={setExpanded}
|
||||
expanded={expanded}
|
||||
fileProgressStatuses={fileProgressStatuses}
|
||||
sectionInfo={sectionInfo}
|
||||
fileUploadResultMap={fileUploadResultMap}
|
||||
filesNotUploaded={filesNotUploaded}
|
||||
{...props}
|
||||
/>
|
||||
) : (
|
||||
<MinimizedUploadProgress
|
||||
setExpanded={setExpanded}
|
||||
expanded={expanded}
|
||||
handleHideModal={handleHideModal}
|
||||
{...props}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
21
src/components/UploadProgress/minimized.tsx
Normal file
21
src/components/UploadProgress/minimized.tsx
Normal file
|
@ -0,0 +1,21 @@
|
|||
import { Snackbar, Paper } from '@mui/material';
|
||||
import React from 'react';
|
||||
import { UploadProgressHeader } from './header';
|
||||
export function MinimizedUploadProgress(props) {
|
||||
return (
|
||||
<Snackbar
|
||||
action={<></>}
|
||||
open={true}
|
||||
anchorOrigin={{
|
||||
horizontal: 'right',
|
||||
vertical: 'bottom',
|
||||
}}>
|
||||
<Paper
|
||||
sx={{
|
||||
width: '360px',
|
||||
}}>
|
||||
<UploadProgressHeader {...props} />
|
||||
</Paper>
|
||||
</Snackbar>
|
||||
);
|
||||
}
|
23
src/components/UploadProgress/progressBar.tsx
Normal file
23
src/components/UploadProgress/progressBar.tsx
Normal file
|
@ -0,0 +1,23 @@
|
|||
import React from 'react';
|
||||
import { LinearProgress, Divider } from '@mui/material';
|
||||
import { UPLOAD_STAGES } from 'constants/upload';
|
||||
|
||||
export function UploadProgressBar({ uploadStage, now }) {
|
||||
return (
|
||||
<>
|
||||
{(uploadStage === UPLOAD_STAGES.READING_GOOGLE_METADATA_FILES ||
|
||||
uploadStage === UPLOAD_STAGES.EXTRACTING_METADATA ||
|
||||
uploadStage === UPLOAD_STAGES.UPLOADING) && (
|
||||
<LinearProgress
|
||||
sx={{
|
||||
height: '2px',
|
||||
backgroundColor: 'transparent',
|
||||
}}
|
||||
variant="determinate"
|
||||
value={now}
|
||||
/>
|
||||
)}
|
||||
<Divider />
|
||||
</>
|
||||
);
|
||||
}
|
44
src/components/UploadProgress/resultSection.tsx
Normal file
44
src/components/UploadProgress/resultSection.tsx
Normal file
|
@ -0,0 +1,44 @@
|
|||
import React from 'react';
|
||||
import FileList from 'components/FileList';
|
||||
import {
|
||||
Accordion,
|
||||
AccordionDetails,
|
||||
AccordionSummary,
|
||||
Typography,
|
||||
} from '@mui/material';
|
||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||
import { SectionInfo, ResultItemContainer } from './styledComponents';
|
||||
import { FileUploadResults } from 'constants/upload';
|
||||
|
||||
export interface ResultSectionProps {
|
||||
filenames: Map<number, string>;
|
||||
fileUploadResultMap: Map<FileUploadResults, number[]>;
|
||||
fileUploadResult: FileUploadResults;
|
||||
sectionTitle: any;
|
||||
sectionInfo?: any;
|
||||
}
|
||||
export const ResultSection = (props: ResultSectionProps) => {
|
||||
const fileList = props.fileUploadResultMap?.get(props.fileUploadResult);
|
||||
if (!fileList?.length) {
|
||||
return <></>;
|
||||
}
|
||||
return (
|
||||
<Accordion>
|
||||
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
|
||||
<Typography> {props.sectionTitle}</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
{props.sectionInfo && (
|
||||
<SectionInfo>{props.sectionInfo}</SectionInfo>
|
||||
)}
|
||||
<FileList
|
||||
fileList={fileList.map((fileID) => (
|
||||
<ResultItemContainer key={fileID}>
|
||||
{props.filenames.get(fileID)}
|
||||
</ResultItemContainer>
|
||||
))}
|
||||
/>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
);
|
||||
};
|
62
src/components/UploadProgress/styledComponents.tsx
Normal file
62
src/components/UploadProgress/styledComponents.tsx
Normal file
|
@ -0,0 +1,62 @@
|
|||
import {
|
||||
getVariantColor,
|
||||
ButtonVariant,
|
||||
} from 'components/pages/gallery/LinkButton';
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const SectionTitle = styled.div`
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
color: #eee;
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
export const Section = styled.div`
|
||||
margin: 20px 0;
|
||||
padding: 0 20px;
|
||||
`;
|
||||
export const SectionInfo = styled.div`
|
||||
margin: 4px 0;
|
||||
padding-left: 15px;
|
||||
`;
|
||||
|
||||
export const SectionContent = styled.div`
|
||||
padding-right: 35px;
|
||||
`;
|
||||
|
||||
export const NotUploadSectionHeader = styled.div`
|
||||
margin-top: 30px;
|
||||
text-align: center;
|
||||
color: ${getVariantColor(ButtonVariant.warning)};
|
||||
border-bottom: 1px solid ${getVariantColor(ButtonVariant.warning)};
|
||||
margin: 0 20px;
|
||||
`;
|
||||
|
||||
export const InProgressItemContainer = styled.div`
|
||||
display: inline-block;
|
||||
& > span {
|
||||
display: inline-block;
|
||||
}
|
||||
& > span:first-of-type {
|
||||
position: relative;
|
||||
top: 5px;
|
||||
max-width: 287px;
|
||||
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: 334px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
`;
|
76
src/components/UploadProgress/title.tsx
Normal file
76
src/components/UploadProgress/title.tsx
Normal file
|
@ -0,0 +1,76 @@
|
|||
import React from 'react';
|
||||
import Close from '@mui/icons-material/Close';
|
||||
import {
|
||||
DialogTitle,
|
||||
Box,
|
||||
Typography,
|
||||
IconButton,
|
||||
styled,
|
||||
Stack,
|
||||
} from '@mui/material';
|
||||
import { SpaceBetweenFlex } from 'components/Container';
|
||||
import { UPLOAD_STAGES } from 'constants/upload';
|
||||
import constants from 'utils/strings/constants';
|
||||
import { MaximizeIcon } from 'components/icons/Maximize';
|
||||
import { MinimizeIcon } from 'components/icons/Minimize';
|
||||
|
||||
const IconButtonWithBG = styled(IconButton)(({ theme }) => ({
|
||||
backgroundColor: theme.palette.secondary.main,
|
||||
}));
|
||||
|
||||
const UploadProgressTitleText = (expanded) => (
|
||||
<Typography
|
||||
{...(expanded ? { variant: 'title' } : {})}
|
||||
css={
|
||||
!expanded &&
|
||||
`
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
line-height: 36px;
|
||||
`
|
||||
}>
|
||||
{constants.FILE_UPLOAD}
|
||||
</Typography>
|
||||
);
|
||||
|
||||
function UploadProgressSubtitleText(props) {
|
||||
return (
|
||||
<Typography color="text.secondary">
|
||||
{props.uploadStage === UPLOAD_STAGES.UPLOADING
|
||||
? constants.UPLOAD_STAGE_MESSAGE[props.uploadStage](
|
||||
props.fileCounter
|
||||
)
|
||||
: constants.UPLOAD_STAGE_MESSAGE[props.uploadStage]}
|
||||
</Typography>
|
||||
);
|
||||
}
|
||||
|
||||
export function UploadProgressTitle({
|
||||
setExpanded,
|
||||
expanded,
|
||||
handleHideModal,
|
||||
...props
|
||||
}) {
|
||||
const toggleExpanded = () => setExpanded((expanded) => !expanded);
|
||||
|
||||
return (
|
||||
<DialogTitle>
|
||||
<SpaceBetweenFlex>
|
||||
<Box>
|
||||
<UploadProgressTitleText expanded={expanded} />
|
||||
<UploadProgressSubtitleText {...props} />
|
||||
</Box>
|
||||
<Box>
|
||||
<Stack direction={'row'} spacing={1}>
|
||||
<IconButtonWithBG onClick={toggleExpanded}>
|
||||
{expanded ? <MinimizeIcon /> : <MaximizeIcon />}
|
||||
</IconButtonWithBG>
|
||||
<IconButtonWithBG onClick={handleHideModal}>
|
||||
<Close />
|
||||
</IconButtonWithBG>
|
||||
</Stack>
|
||||
</Box>
|
||||
</SpaceBetweenFlex>
|
||||
</DialogTitle>
|
||||
);
|
||||
}
|
13
src/components/icons/Maximize.tsx
Normal file
13
src/components/icons/Maximize.tsx
Normal file
|
@ -0,0 +1,13 @@
|
|||
import React from 'react';
|
||||
import OpenInFullIcon from '@mui/icons-material/OpenInFull';
|
||||
|
||||
export function MaximizeIcon() {
|
||||
return (
|
||||
<OpenInFullIcon
|
||||
sx={{
|
||||
padding: '4px',
|
||||
transform: 'rotate(90deg)',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
13
src/components/icons/Minimize.tsx
Normal file
13
src/components/icons/Minimize.tsx
Normal file
|
@ -0,0 +1,13 @@
|
|||
import React from 'react';
|
||||
import CloseFullscreenIcon from '@mui/icons-material/CloseFullscreen';
|
||||
|
||||
export function MinimizeIcon() {
|
||||
return (
|
||||
<CloseFullscreenIcon
|
||||
sx={{
|
||||
padding: '4px',
|
||||
transform: 'rotate(90deg)',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -3,7 +3,7 @@ import React, { useContext, useEffect, useRef, useState } from 'react';
|
|||
|
||||
import { syncCollections, createAlbum } from 'services/collectionService';
|
||||
import constants from 'utils/strings/constants';
|
||||
import UploadProgress from './UploadProgress';
|
||||
import UploadProgress from '../../UploadProgress';
|
||||
|
||||
import UploadStrategyChoiceModal from './UploadStrategyChoiceModal';
|
||||
import { SetCollectionNamerAttributes } from '../../Collections/CollectionNamer';
|
||||
|
|
|
@ -1,416 +0,0 @@
|
|||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import React, { useContext, useState } from 'react';
|
||||
|
||||
import styled from 'styled-components';
|
||||
import { DESKTOP_APP_DOWNLOAD_URL } from 'utils/common';
|
||||
import constants from 'utils/strings/constants';
|
||||
import { ButtonVariant, getVariantColor } from './LinkButton';
|
||||
import { FileUploadResults, UPLOAD_STAGES } from 'constants/upload';
|
||||
import FileList from 'components/FileList';
|
||||
import { AppContext } from 'pages/_app';
|
||||
import {
|
||||
Accordion,
|
||||
AccordionDetails,
|
||||
AccordionSummary,
|
||||
Box,
|
||||
Button,
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
Divider,
|
||||
IconButton,
|
||||
LinearProgress,
|
||||
Typography,
|
||||
} from '@mui/material';
|
||||
import { FlexWrapper, SpaceBetweenFlex } from 'components/Container';
|
||||
import Close from '@mui/icons-material/Close';
|
||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||
import OpenInFullIcon from '@mui/icons-material/OpenInFull';
|
||||
import CloseFullscreenIcon from '@mui/icons-material/CloseFullscreen';
|
||||
interface Props {
|
||||
fileCounter;
|
||||
uploadStage;
|
||||
now;
|
||||
closeModal;
|
||||
retryFailed;
|
||||
fileProgress: Map<number, number>;
|
||||
filenames: Map<number, string>;
|
||||
show;
|
||||
uploadResult: Map<number, FileUploadResults>;
|
||||
hasLivePhotos: boolean;
|
||||
cancelUploads: () => void;
|
||||
}
|
||||
interface FileProgresses {
|
||||
fileID: number;
|
||||
progress: number;
|
||||
}
|
||||
|
||||
const SectionTitle = styled.div`
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
color: #eee;
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
const Section = styled.div`
|
||||
margin: 20px 0;
|
||||
padding: 0 20px;
|
||||
`;
|
||||
const SectionInfo = styled.div`
|
||||
margin: 4px 0;
|
||||
padding-left: 15px;
|
||||
`;
|
||||
|
||||
const SectionContent = styled.div`
|
||||
padding-right: 35px;
|
||||
`;
|
||||
|
||||
const NotUploadSectionHeader = styled.div`
|
||||
margin-top: 30px;
|
||||
text-align: center;
|
||||
color: ${getVariantColor(ButtonVariant.warning)};
|
||||
border-bottom: 1px solid ${getVariantColor(ButtonVariant.warning)};
|
||||
margin: 0 20px;
|
||||
`;
|
||||
|
||||
const InProgressItemContainer = styled.div`
|
||||
display: inline-block;
|
||||
& > span {
|
||||
display: inline-block;
|
||||
}
|
||||
& > span:first-of-type {
|
||||
position: relative;
|
||||
top: 5px;
|
||||
max-width: 287px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
& > .separator {
|
||||
margin: 0 5px;
|
||||
}
|
||||
`;
|
||||
|
||||
const ResultItemContainer = styled.div`
|
||||
position: relative;
|
||||
top: 5px;
|
||||
display: inline-block;
|
||||
max-width: 334px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
`;
|
||||
|
||||
interface ResultSectionProps {
|
||||
filenames: Map<number, string>;
|
||||
fileUploadResultMap: Map<FileUploadResults, number[]>;
|
||||
fileUploadResult: FileUploadResults;
|
||||
sectionTitle: any;
|
||||
sectionInfo?: any;
|
||||
}
|
||||
const ResultSection = (props: ResultSectionProps) => {
|
||||
const fileList = props.fileUploadResultMap?.get(props.fileUploadResult);
|
||||
if (!fileList?.length) {
|
||||
return <></>;
|
||||
}
|
||||
return (
|
||||
<Accordion>
|
||||
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
|
||||
<Typography> {props.sectionTitle}</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
{props.sectionInfo && (
|
||||
<SectionInfo>{props.sectionInfo}</SectionInfo>
|
||||
)}
|
||||
<FileList
|
||||
fileList={fileList.map((fileID) => (
|
||||
<ResultItemContainer key={fileID}>
|
||||
{props.filenames.get(fileID)}
|
||||
</ResultItemContainer>
|
||||
))}
|
||||
/>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
);
|
||||
};
|
||||
|
||||
interface InProgressProps {
|
||||
filenames: Map<number, string>;
|
||||
sectionTitle: string;
|
||||
fileProgressStatuses: FileProgresses[];
|
||||
sectionInfo?: any;
|
||||
}
|
||||
const InProgressSection = (props: InProgressProps) => {
|
||||
const fileList = props.fileProgressStatuses ?? [];
|
||||
|
||||
return (
|
||||
<Accordion>
|
||||
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
|
||||
{props.sectionTitle}
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
{props.sectionInfo && (
|
||||
<SectionInfo>{props.sectionInfo}</SectionInfo>
|
||||
)}
|
||||
<FileList
|
||||
fileList={fileList.map(({ fileID, progress }) => (
|
||||
<InProgressItemContainer key={fileID}>
|
||||
<span>{props.filenames.get(fileID)}</span>
|
||||
<span className="separator">{`-`}</span>
|
||||
<span>{`${progress}%`}</span>
|
||||
</InProgressItemContainer>
|
||||
))}
|
||||
/>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
);
|
||||
};
|
||||
|
||||
export default function UploadProgress(props: Props) {
|
||||
const appContext = useContext(AppContext);
|
||||
const [expanded, setExpanded] = useState(true);
|
||||
const fileProgressStatuses = [] as FileProgresses[];
|
||||
const fileUploadResultMap = new Map<FileUploadResults, number[]>();
|
||||
let filesNotUploaded = false;
|
||||
let sectionInfo = null;
|
||||
if (props.fileProgress) {
|
||||
for (const [localID, progress] of props.fileProgress) {
|
||||
fileProgressStatuses.push({
|
||||
fileID: localID,
|
||||
progress,
|
||||
});
|
||||
}
|
||||
}
|
||||
if (props.uploadResult) {
|
||||
for (const [localID, progress] of props.uploadResult) {
|
||||
if (!fileUploadResultMap.has(progress)) {
|
||||
fileUploadResultMap.set(progress, []);
|
||||
}
|
||||
if (progress !== FileUploadResults.UPLOADED) {
|
||||
filesNotUploaded = true;
|
||||
}
|
||||
const fileList = fileUploadResultMap.get(progress);
|
||||
|
||||
fileUploadResultMap.set(progress, [...fileList, localID]);
|
||||
}
|
||||
}
|
||||
if (props.hasLivePhotos) {
|
||||
sectionInfo = constants.LIVE_PHOTOS_DETECTED();
|
||||
}
|
||||
|
||||
function handleHideModal() {
|
||||
if (props.uploadStage !== UPLOAD_STAGES.FINISH) {
|
||||
appContext.setDialogMessage({
|
||||
title: constants.STOP_UPLOADS_HEADER,
|
||||
content: constants.STOP_ALL_UPLOADS_MESSAGE,
|
||||
proceed: {
|
||||
text: constants.YES_STOP_UPLOADS,
|
||||
variant: 'danger',
|
||||
action: props.cancelUploads,
|
||||
},
|
||||
close: {
|
||||
text: constants.NO,
|
||||
variant: 'secondary',
|
||||
action: () => {},
|
||||
},
|
||||
});
|
||||
} else {
|
||||
props.closeModal();
|
||||
}
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<Dialog
|
||||
maxWidth="xs"
|
||||
fullWidth
|
||||
open={props.show}
|
||||
sx={
|
||||
!expanded && {
|
||||
'& .MuiDialog-container': {
|
||||
alignItems: 'flex-end',
|
||||
justifyContent: 'flex-end',
|
||||
},
|
||||
}
|
||||
}
|
||||
onClose={handleHideModal}>
|
||||
<DialogTitle>
|
||||
<SpaceBetweenFlex>
|
||||
<Box>
|
||||
<Typography variant="h5">
|
||||
{constants.FILE_UPLOAD}
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="subtitle1"
|
||||
color="text.secondary">
|
||||
{props.uploadStage === UPLOAD_STAGES.UPLOADING
|
||||
? constants.UPLOAD_STAGE_MESSAGE[
|
||||
props.uploadStage
|
||||
](props.fileCounter)
|
||||
: constants.UPLOAD_STAGE_MESSAGE[
|
||||
props.uploadStage
|
||||
]}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box>
|
||||
<FlexWrapper>
|
||||
<IconButton
|
||||
onClick={() => setExpanded((e) => !e)}
|
||||
sx={{
|
||||
m: 0.5,
|
||||
backgroundColor: (theme) =>
|
||||
theme.palette.secondary.main,
|
||||
}}>
|
||||
{expanded ? (
|
||||
<CloseFullscreenIcon
|
||||
sx={{
|
||||
padding: '4px',
|
||||
transform: 'rotate(90deg)',
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<OpenInFullIcon
|
||||
sx={{
|
||||
padding: '4px',
|
||||
transform: 'rotate(90deg)',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</IconButton>
|
||||
<IconButton
|
||||
onClick={handleHideModal}
|
||||
sx={{
|
||||
m: 0.5,
|
||||
backgroundColor: (theme) =>
|
||||
theme.palette.secondary.main,
|
||||
}}>
|
||||
<Close />
|
||||
</IconButton>
|
||||
</FlexWrapper>
|
||||
</Box>
|
||||
</SpaceBetweenFlex>
|
||||
</DialogTitle>
|
||||
{(props.uploadStage ===
|
||||
UPLOAD_STAGES.READING_GOOGLE_METADATA_FILES ||
|
||||
props.uploadStage === UPLOAD_STAGES.EXTRACTING_METADATA ||
|
||||
props.uploadStage === UPLOAD_STAGES.UPLOADING) && (
|
||||
<LinearProgress
|
||||
sx={{
|
||||
height: '2px',
|
||||
backgroundColor: 'transparent',
|
||||
}}
|
||||
variant="determinate"
|
||||
value={props.now}
|
||||
/>
|
||||
)}
|
||||
<Divider />
|
||||
{expanded && (
|
||||
<DialogContent
|
||||
sx={{
|
||||
px: 0,
|
||||
'& .paper-root': { borderRadius: 0 },
|
||||
}}>
|
||||
{props.uploadStage === UPLOAD_STAGES.UPLOADING && (
|
||||
<InProgressSection
|
||||
filenames={props.filenames}
|
||||
fileProgressStatuses={fileProgressStatuses}
|
||||
sectionTitle={constants.INPROGRESS_UPLOADS}
|
||||
sectionInfo={sectionInfo}
|
||||
/>
|
||||
)}
|
||||
|
||||
<ResultSection
|
||||
filenames={props.filenames}
|
||||
fileUploadResultMap={fileUploadResultMap}
|
||||
fileUploadResult={FileUploadResults.UPLOADED}
|
||||
sectionTitle={constants.SUCCESSFUL_UPLOADS}
|
||||
/>
|
||||
|
||||
{props.uploadStage === UPLOAD_STAGES.FINISH &&
|
||||
filesNotUploaded && (
|
||||
<NotUploadSectionHeader>
|
||||
{constants.FILE_NOT_UPLOADED_LIST}
|
||||
</NotUploadSectionHeader>
|
||||
)}
|
||||
|
||||
<ResultSection
|
||||
filenames={props.filenames}
|
||||
fileUploadResultMap={fileUploadResultMap}
|
||||
fileUploadResult={FileUploadResults.BLOCKED}
|
||||
sectionTitle={constants.BLOCKED_UPLOADS}
|
||||
sectionInfo={constants.ETAGS_BLOCKED(
|
||||
DESKTOP_APP_DOWNLOAD_URL
|
||||
)}
|
||||
/>
|
||||
<ResultSection
|
||||
filenames={props.filenames}
|
||||
fileUploadResultMap={fileUploadResultMap}
|
||||
fileUploadResult={FileUploadResults.FAILED}
|
||||
sectionTitle={constants.FAILED_UPLOADS}
|
||||
/>
|
||||
<ResultSection
|
||||
filenames={props.filenames}
|
||||
fileUploadResultMap={fileUploadResultMap}
|
||||
fileUploadResult={
|
||||
FileUploadResults.ALREADY_UPLOADED
|
||||
}
|
||||
sectionTitle={constants.SKIPPED_FILES}
|
||||
sectionInfo={constants.SKIPPED_INFO}
|
||||
/>
|
||||
<ResultSection
|
||||
filenames={props.filenames}
|
||||
fileUploadResultMap={fileUploadResultMap}
|
||||
fileUploadResult={
|
||||
FileUploadResults.LARGER_THAN_AVAILABLE_STORAGE
|
||||
}
|
||||
sectionTitle={
|
||||
constants.LARGER_THAN_AVAILABLE_STORAGE_UPLOADS
|
||||
}
|
||||
sectionInfo={
|
||||
constants.LARGER_THAN_AVAILABLE_STORAGE_INFO
|
||||
}
|
||||
/>
|
||||
<ResultSection
|
||||
filenames={props.filenames}
|
||||
fileUploadResultMap={fileUploadResultMap}
|
||||
fileUploadResult={FileUploadResults.UNSUPPORTED}
|
||||
sectionTitle={constants.UNSUPPORTED_FILES}
|
||||
sectionInfo={constants.UNSUPPORTED_INFO}
|
||||
/>
|
||||
<ResultSection
|
||||
filenames={props.filenames}
|
||||
fileUploadResultMap={fileUploadResultMap}
|
||||
fileUploadResult={FileUploadResults.TOO_LARGE}
|
||||
sectionTitle={constants.TOO_LARGE_UPLOADS}
|
||||
sectionInfo={constants.TOO_LARGE_INFO}
|
||||
/>
|
||||
</DialogContent>
|
||||
)}
|
||||
{props.uploadStage === UPLOAD_STAGES.FINISH && (
|
||||
<DialogActions>
|
||||
{props.uploadStage === UPLOAD_STAGES.FINISH &&
|
||||
(fileUploadResultMap?.get(FileUploadResults.FAILED)
|
||||
?.length > 0 ||
|
||||
fileUploadResultMap?.get(FileUploadResults.BLOCKED)
|
||||
?.length > 0 ? (
|
||||
<Button
|
||||
variant="contained"
|
||||
fullWidth
|
||||
onClick={props.retryFailed}>
|
||||
{constants.RETRY_FAILED}
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
variant="contained"
|
||||
fullWidth
|
||||
onClick={props.closeModal}>
|
||||
{constants.CLOSE}
|
||||
</Button>
|
||||
))}
|
||||
</DialogActions>
|
||||
)}
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
}
|
Loading…
Reference in a new issue