add upload progress context
This commit is contained in:
parent
b51afc7efe
commit
ae3a68378f
|
@ -1,7 +1,7 @@
|
||||||
import { DialogContent } from '@mui/material';
|
import { DialogContent } from '@mui/material';
|
||||||
import constants from 'utils/strings/constants';
|
import constants from 'utils/strings/constants';
|
||||||
import { UPLOAD_STAGES, FileUploadResults } from 'constants/upload';
|
import { UPLOAD_STAGES, UPLOAD_RESULT } from 'constants/upload';
|
||||||
import React from 'react';
|
import React, { useContext, useMemo } from 'react';
|
||||||
import { UploadProgressFooter } from './footer';
|
import { UploadProgressFooter } from './footer';
|
||||||
import { UploadProgressHeader } from './header';
|
import { UploadProgressHeader } from './header';
|
||||||
import { InProgressSection } from './inProgressSection';
|
import { InProgressSection } from './inProgressSection';
|
||||||
|
@ -9,49 +9,46 @@ import { ResultSection } from './resultSection';
|
||||||
import { NotUploadSectionHeader } from './styledComponents';
|
import { NotUploadSectionHeader } from './styledComponents';
|
||||||
import { getOSSpecificDesktopAppDownloadLink } from 'utils/common';
|
import { getOSSpecificDesktopAppDownloadLink } from 'utils/common';
|
||||||
import DialogBoxBase from 'components/DialogBox/base';
|
import DialogBoxBase from 'components/DialogBox/base';
|
||||||
export function UploadProgressDialog({
|
import UploadProgressContext from 'contexts/uploadProgress';
|
||||||
handleClose,
|
|
||||||
setExpanded,
|
export function UploadProgressDialog() {
|
||||||
expanded,
|
const { open, onClose, uploadStage, finishedUploads } = useContext(
|
||||||
fileProgressStatuses,
|
UploadProgressContext
|
||||||
sectionInfo,
|
);
|
||||||
fileUploadResultMap,
|
|
||||||
filesNotUploaded,
|
const hasUnUploadedFiles = useMemo(() => {
|
||||||
...props
|
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
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}, [finishedUploads]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DialogBoxBase maxWidth="xs" open={props.show} onClose={handleClose}>
|
<DialogBoxBase maxWidth="xs" open={open} onClose={onClose}>
|
||||||
<UploadProgressHeader
|
<UploadProgressHeader />
|
||||||
uploadStage={props.uploadStage}
|
{(uploadStage === UPLOAD_STAGES.UPLOADING ||
|
||||||
setExpanded={setExpanded}
|
uploadStage === UPLOAD_STAGES.FINISH) && (
|
||||||
expanded={expanded}
|
|
||||||
handleClose={handleClose}
|
|
||||||
fileCounter={props.fileCounter}
|
|
||||||
now={props.now}
|
|
||||||
/>
|
|
||||||
{(props.uploadStage === UPLOAD_STAGES.UPLOADING ||
|
|
||||||
props.uploadStage === UPLOAD_STAGES.FINISH) && (
|
|
||||||
<DialogContent sx={{ '&&&': { px: 0 } }}>
|
<DialogContent sx={{ '&&&': { px: 0 } }}>
|
||||||
{props.uploadStage === UPLOAD_STAGES.UPLOADING && (
|
{uploadStage === UPLOAD_STAGES.UPLOADING && (
|
||||||
<InProgressSection
|
<InProgressSection />
|
||||||
filenames={props.filenames}
|
|
||||||
fileProgressStatuses={fileProgressStatuses}
|
|
||||||
sectionTitle={constants.INPROGRESS_UPLOADS}
|
|
||||||
sectionInfo={sectionInfo}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<ResultSection
|
<ResultSection
|
||||||
filenames={props.filenames}
|
uploadResult={UPLOAD_RESULT.UPLOADED}
|
||||||
fileUploadResultMap={fileUploadResultMap}
|
|
||||||
fileUploadResult={FileUploadResults.UPLOADED}
|
|
||||||
sectionTitle={constants.SUCCESSFUL_UPLOADS}
|
sectionTitle={constants.SUCCESSFUL_UPLOADS}
|
||||||
/>
|
/>
|
||||||
<ResultSection
|
<ResultSection
|
||||||
filenames={props.filenames}
|
uploadResult={
|
||||||
fileUploadResultMap={fileUploadResultMap}
|
UPLOAD_RESULT.UPLOADED_WITH_STATIC_THUMBNAIL
|
||||||
fileUploadResult={
|
|
||||||
FileUploadResults.UPLOADED_WITH_STATIC_THUMBNAIL
|
|
||||||
}
|
}
|
||||||
sectionTitle={
|
sectionTitle={
|
||||||
constants.THUMBNAIL_GENERATION_FAILED_UPLOADS
|
constants.THUMBNAIL_GENERATION_FAILED_UPLOADS
|
||||||
|
@ -59,40 +56,32 @@ export function UploadProgressDialog({
|
||||||
sectionInfo={constants.THUMBNAIL_GENERATION_FAILED_INFO}
|
sectionInfo={constants.THUMBNAIL_GENERATION_FAILED_INFO}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{props.uploadStage === UPLOAD_STAGES.FINISH &&
|
{uploadStage === UPLOAD_STAGES.FINISH &&
|
||||||
filesNotUploaded && (
|
hasUnUploadedFiles && (
|
||||||
<NotUploadSectionHeader>
|
<NotUploadSectionHeader>
|
||||||
{constants.FILE_NOT_UPLOADED_LIST}
|
{constants.FILE_NOT_UPLOADED_LIST}
|
||||||
</NotUploadSectionHeader>
|
</NotUploadSectionHeader>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<ResultSection
|
<ResultSection
|
||||||
filenames={props.filenames}
|
uploadResult={UPLOAD_RESULT.BLOCKED}
|
||||||
fileUploadResultMap={fileUploadResultMap}
|
|
||||||
fileUploadResult={FileUploadResults.BLOCKED}
|
|
||||||
sectionTitle={constants.BLOCKED_UPLOADS}
|
sectionTitle={constants.BLOCKED_UPLOADS}
|
||||||
sectionInfo={constants.ETAGS_BLOCKED(
|
sectionInfo={constants.ETAGS_BLOCKED(
|
||||||
getOSSpecificDesktopAppDownloadLink()
|
getOSSpecificDesktopAppDownloadLink()
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<ResultSection
|
<ResultSection
|
||||||
filenames={props.filenames}
|
uploadResult={UPLOAD_RESULT.FAILED}
|
||||||
fileUploadResultMap={fileUploadResultMap}
|
|
||||||
fileUploadResult={FileUploadResults.FAILED}
|
|
||||||
sectionTitle={constants.FAILED_UPLOADS}
|
sectionTitle={constants.FAILED_UPLOADS}
|
||||||
/>
|
/>
|
||||||
<ResultSection
|
<ResultSection
|
||||||
filenames={props.filenames}
|
uploadResult={UPLOAD_RESULT.ALREADY_UPLOADED}
|
||||||
fileUploadResultMap={fileUploadResultMap}
|
|
||||||
fileUploadResult={FileUploadResults.ALREADY_UPLOADED}
|
|
||||||
sectionTitle={constants.SKIPPED_FILES}
|
sectionTitle={constants.SKIPPED_FILES}
|
||||||
sectionInfo={constants.SKIPPED_INFO}
|
sectionInfo={constants.SKIPPED_INFO}
|
||||||
/>
|
/>
|
||||||
<ResultSection
|
<ResultSection
|
||||||
filenames={props.filenames}
|
uploadResult={
|
||||||
fileUploadResultMap={fileUploadResultMap}
|
UPLOAD_RESULT.LARGER_THAN_AVAILABLE_STORAGE
|
||||||
fileUploadResult={
|
|
||||||
FileUploadResults.LARGER_THAN_AVAILABLE_STORAGE
|
|
||||||
}
|
}
|
||||||
sectionTitle={
|
sectionTitle={
|
||||||
constants.LARGER_THAN_AVAILABLE_STORAGE_UPLOADS
|
constants.LARGER_THAN_AVAILABLE_STORAGE_UPLOADS
|
||||||
|
@ -102,29 +91,18 @@ export function UploadProgressDialog({
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<ResultSection
|
<ResultSection
|
||||||
filenames={props.filenames}
|
uploadResult={UPLOAD_RESULT.UNSUPPORTED}
|
||||||
fileUploadResultMap={fileUploadResultMap}
|
|
||||||
fileUploadResult={FileUploadResults.UNSUPPORTED}
|
|
||||||
sectionTitle={constants.UNSUPPORTED_FILES}
|
sectionTitle={constants.UNSUPPORTED_FILES}
|
||||||
sectionInfo={constants.UNSUPPORTED_INFO}
|
sectionInfo={constants.UNSUPPORTED_INFO}
|
||||||
/>
|
/>
|
||||||
<ResultSection
|
<ResultSection
|
||||||
filenames={props.filenames}
|
uploadResult={UPLOAD_RESULT.TOO_LARGE}
|
||||||
fileUploadResultMap={fileUploadResultMap}
|
|
||||||
fileUploadResult={FileUploadResults.TOO_LARGE}
|
|
||||||
sectionTitle={constants.TOO_LARGE_UPLOADS}
|
sectionTitle={constants.TOO_LARGE_UPLOADS}
|
||||||
sectionInfo={constants.TOO_LARGE_INFO}
|
sectionInfo={constants.TOO_LARGE_INFO}
|
||||||
/>
|
/>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
)}
|
)}
|
||||||
{props.uploadStage === UPLOAD_STAGES.FINISH && (
|
{uploadStage === UPLOAD_STAGES.FINISH && <UploadProgressFooter />}
|
||||||
<UploadProgressFooter
|
|
||||||
uploadStage={props.uploadStage}
|
|
||||||
retryFailed={props.retryFailed}
|
|
||||||
closeModal={handleClose}
|
|
||||||
fileUploadResultMap={fileUploadResultMap}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</DialogBoxBase>
|
</DialogBoxBase>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,27 @@
|
||||||
import { Button, DialogActions } from '@mui/material';
|
import { Button, DialogActions } from '@mui/material';
|
||||||
import { UPLOAD_STAGES, FileUploadResults } from 'constants/upload';
|
import { UPLOAD_STAGES, UPLOAD_RESULT } from 'constants/upload';
|
||||||
import React from 'react';
|
import React, { useContext } from 'react';
|
||||||
import constants from 'utils/strings/constants';
|
import constants from 'utils/strings/constants';
|
||||||
export function UploadProgressFooter({
|
import UploadProgressContext from 'contexts/uploadProgress';
|
||||||
uploadStage,
|
|
||||||
fileUploadResultMap,
|
export function UploadProgressFooter() {
|
||||||
retryFailed,
|
const { uploadStage, finishedUploads, retryFailed, onClose } = useContext(
|
||||||
closeModal,
|
UploadProgressContext
|
||||||
}) {
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
{uploadStage === UPLOAD_STAGES.FINISH &&
|
{uploadStage === UPLOAD_STAGES.FINISH &&
|
||||||
(fileUploadResultMap?.get(FileUploadResults.FAILED)?.length >
|
(finishedUploads?.get(UPLOAD_RESULT.FAILED)?.length > 0 ||
|
||||||
0 ||
|
finishedUploads?.get(UPLOAD_RESULT.BLOCKED)?.length > 0 ? (
|
||||||
fileUploadResultMap?.get(FileUploadResults.BLOCKED)?.length >
|
|
||||||
0 ? (
|
|
||||||
<Button variant="contained" fullWidth onClick={retryFailed}>
|
<Button variant="contained" fullWidth onClick={retryFailed}>
|
||||||
{constants.RETRY_FAILED}
|
{constants.RETRY_FAILED}
|
||||||
</Button>
|
</Button>
|
||||||
) : (
|
) : (
|
||||||
<Button variant="contained" fullWidth onClick={closeModal}>
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
fullWidth
|
||||||
|
onClick={() => onClose.bind(null)}>
|
||||||
{constants.CLOSE}
|
{constants.CLOSE}
|
||||||
</Button>
|
</Button>
|
||||||
))}
|
))}
|
||||||
|
|
|
@ -2,24 +2,11 @@ import React from 'react';
|
||||||
import { UploadProgressBar } from './progressBar';
|
import { UploadProgressBar } from './progressBar';
|
||||||
import { UploadProgressTitle } from './title';
|
import { UploadProgressTitle } from './title';
|
||||||
|
|
||||||
export function UploadProgressHeader({
|
export function UploadProgressHeader() {
|
||||||
uploadStage,
|
|
||||||
setExpanded,
|
|
||||||
expanded,
|
|
||||||
handleClose,
|
|
||||||
fileCounter,
|
|
||||||
now,
|
|
||||||
}) {
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<UploadProgressTitle
|
<UploadProgressTitle />
|
||||||
uploadStage={uploadStage}
|
<UploadProgressBar />
|
||||||
setExpanded={setExpanded}
|
|
||||||
expanded={expanded}
|
|
||||||
handleClose={handleClose}
|
|
||||||
fileCounter={fileCounter}
|
|
||||||
/>
|
|
||||||
<UploadProgressBar now={now} uploadStage={uploadStage} />
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,37 +1,35 @@
|
||||||
import React from 'react';
|
import React, { useContext } from 'react';
|
||||||
import FileList from 'components/FileList';
|
import FileList from 'components/FileList';
|
||||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||||
import { InProgressItemContainer } from './styledComponents';
|
import { InProgressItemContainer } from './styledComponents';
|
||||||
import { FileProgresses } from '.';
|
|
||||||
import {
|
import {
|
||||||
SectionInfo,
|
SectionInfo,
|
||||||
UploadProgressSection,
|
UploadProgressSection,
|
||||||
UploadProgressSectionContent,
|
UploadProgressSectionContent,
|
||||||
UploadProgressSectionTitle,
|
UploadProgressSectionTitle,
|
||||||
} from './section';
|
} from './section';
|
||||||
|
import UploadProgressContext from 'contexts/uploadProgress';
|
||||||
|
import constants from 'utils/strings/constants';
|
||||||
|
|
||||||
export interface InProgressProps {
|
export const InProgressSection = () => {
|
||||||
filenames: Map<number, string>;
|
const { inProgressUploads, hasLivePhotos, uploadFileNames } = useContext(
|
||||||
sectionTitle: string;
|
UploadProgressContext
|
||||||
fileProgressStatuses: FileProgresses[];
|
);
|
||||||
sectionInfo?: any;
|
const fileList = inProgressUploads ?? [];
|
||||||
}
|
|
||||||
export const InProgressSection = (props: InProgressProps) => {
|
|
||||||
const fileList = props.fileProgressStatuses ?? [];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<UploadProgressSection defaultExpanded>
|
<UploadProgressSection defaultExpanded>
|
||||||
<UploadProgressSectionTitle expandIcon={<ExpandMoreIcon />}>
|
<UploadProgressSectionTitle expandIcon={<ExpandMoreIcon />}>
|
||||||
{props.sectionTitle}
|
{constants.INPROGRESS_UPLOADS}
|
||||||
</UploadProgressSectionTitle>
|
</UploadProgressSectionTitle>
|
||||||
<UploadProgressSectionContent>
|
<UploadProgressSectionContent>
|
||||||
{props.sectionInfo && (
|
{hasLivePhotos && (
|
||||||
<SectionInfo>{props.sectionInfo}</SectionInfo>
|
<SectionInfo>{constants.LIVE_PHOTOS_DETECTED}</SectionInfo>
|
||||||
)}
|
)}
|
||||||
<FileList
|
<FileList
|
||||||
fileList={fileList.map(({ fileID, progress }) => (
|
fileList={fileList.map(({ localFileID, progress }) => (
|
||||||
<InProgressItemContainer key={fileID}>
|
<InProgressItemContainer key={localFileID}>
|
||||||
<span>{props.filenames.get(fileID)}</span>
|
<span>{uploadFileNames.get(localFileID)}</span>
|
||||||
<span className="separator">{`-`}</span>
|
<span className="separator">{`-`}</span>
|
||||||
<span>{`${progress}%`}</span>
|
<span>{`${progress}%`}</span>
|
||||||
</InProgressItemContainer>
|
</InProgressItemContainer>
|
||||||
|
|
|
@ -1,64 +1,71 @@
|
||||||
import { UploadProgressDialog } from './dialog';
|
import { UploadProgressDialog } from './dialog';
|
||||||
import { MinimizedUploadProgress } from './minimized';
|
import { MinimizedUploadProgress } from './minimized';
|
||||||
import React, { useContext, useState } from 'react';
|
import React, { useContext, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import constants from 'utils/strings/constants';
|
import constants from 'utils/strings/constants';
|
||||||
import { FileUploadResults, UPLOAD_STAGES } from 'constants/upload';
|
import { UPLOAD_STAGES } from 'constants/upload';
|
||||||
import { AppContext } from 'pages/_app';
|
import { AppContext } from 'pages/_app';
|
||||||
import { dialogCloseHandler } from 'components/DialogBox/base';
|
import { dialogCloseHandler } from 'components/DialogBox/base';
|
||||||
|
import {
|
||||||
|
UploadFileNames,
|
||||||
|
UploadCounter,
|
||||||
|
InProgressUploads,
|
||||||
|
InProgressUpload,
|
||||||
|
FinishedUploads,
|
||||||
|
SegregatedFinishedUploads,
|
||||||
|
} from 'types/upload/ui';
|
||||||
|
import UploadProgressContext from 'contexts/uploadProgress';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
fileCounter;
|
open: boolean;
|
||||||
uploadStage;
|
onClose: () => void;
|
||||||
now;
|
uploadCounter: UploadCounter;
|
||||||
closeModal;
|
uploadStage: UPLOAD_STAGES;
|
||||||
retryFailed;
|
percentComplete: number;
|
||||||
fileProgress: Map<number, number>;
|
retryFailed: () => void;
|
||||||
filenames: Map<number, string>;
|
inProgressUploads: InProgressUploads;
|
||||||
show;
|
uploadFileNames: UploadFileNames;
|
||||||
uploadResult: Map<number, FileUploadResults>;
|
finishedUploads: FinishedUploads;
|
||||||
hasLivePhotos: boolean;
|
hasLivePhotos: boolean;
|
||||||
cancelUploads: () => void;
|
cancelUploads: () => void;
|
||||||
}
|
}
|
||||||
export interface FileProgresses {
|
|
||||||
fileID: number;
|
|
||||||
progress: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function UploadProgress(props: Props) {
|
export default function UploadProgress({
|
||||||
|
open,
|
||||||
|
uploadCounter,
|
||||||
|
uploadStage,
|
||||||
|
percentComplete,
|
||||||
|
retryFailed,
|
||||||
|
uploadFileNames,
|
||||||
|
hasLivePhotos,
|
||||||
|
...props
|
||||||
|
}: Props) {
|
||||||
const appContext = useContext(AppContext);
|
const appContext = useContext(AppContext);
|
||||||
const [expanded, setExpanded] = useState(true);
|
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 &&
|
|
||||||
progress !== FileUploadResults.UPLOADED_WITH_STATIC_THUMBNAIL
|
|
||||||
) {
|
|
||||||
filesNotUploaded = true;
|
|
||||||
}
|
|
||||||
const fileList = fileUploadResultMap.get(progress);
|
|
||||||
|
|
||||||
fileUploadResultMap.set(progress, [...fileList, localID]);
|
const inProgressUploads = useMemo(
|
||||||
|
() =>
|
||||||
|
[...props.inProgressUploads.entries()].map(
|
||||||
|
([localFileID, progress]) =>
|
||||||
|
({
|
||||||
|
localFileID,
|
||||||
|
progress,
|
||||||
|
} as InProgressUpload)
|
||||||
|
),
|
||||||
|
|
||||||
|
[props.inProgressUploads]
|
||||||
|
);
|
||||||
|
|
||||||
|
const finishedUploads = useMemo(() => {
|
||||||
|
const finishedUploads = new Map() as SegregatedFinishedUploads;
|
||||||
|
for (const [localID, result] of props.finishedUploads) {
|
||||||
|
if (!finishedUploads.has(result)) {
|
||||||
|
finishedUploads.set(result, []);
|
||||||
|
}
|
||||||
|
finishedUploads.get(result).push(localID);
|
||||||
}
|
}
|
||||||
}
|
return finishedUploads;
|
||||||
if (props.hasLivePhotos) {
|
}, [props.finishedUploads]);
|
||||||
sectionInfo = constants.LIVE_PHOTOS_DETECTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
function confirmCancelUpload() {
|
function confirmCancelUpload() {
|
||||||
appContext.setDialogMessage({
|
appContext.setDialogMessage({
|
||||||
|
@ -78,10 +85,10 @@ export default function UploadProgress(props: Props) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function onClose() {
|
function onClose() {
|
||||||
if (props.uploadStage !== UPLOAD_STAGES.FINISH) {
|
if (uploadStage !== UPLOAD_STAGES.FINISH) {
|
||||||
confirmCancelUpload();
|
confirmCancelUpload();
|
||||||
} else {
|
} else {
|
||||||
props.closeModal();
|
props.onClose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,26 +97,22 @@ export default function UploadProgress(props: Props) {
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<UploadProgressContext.Provider
|
||||||
{expanded ? (
|
value={{
|
||||||
<UploadProgressDialog
|
open,
|
||||||
handleClose={handleClose}
|
onClose: handleClose,
|
||||||
setExpanded={setExpanded}
|
uploadCounter,
|
||||||
expanded={expanded}
|
uploadStage,
|
||||||
fileProgressStatuses={fileProgressStatuses}
|
percentComplete,
|
||||||
sectionInfo={sectionInfo}
|
retryFailed,
|
||||||
fileUploadResultMap={fileUploadResultMap}
|
inProgressUploads,
|
||||||
filesNotUploaded={filesNotUploaded}
|
uploadFileNames,
|
||||||
{...props}
|
finishedUploads,
|
||||||
/>
|
hasLivePhotos,
|
||||||
) : (
|
expanded,
|
||||||
<MinimizedUploadProgress
|
setExpanded,
|
||||||
setExpanded={setExpanded}
|
}}>
|
||||||
expanded={expanded}
|
{expanded ? <UploadProgressDialog /> : <MinimizedUploadProgress />}
|
||||||
handleClose={handleClose}
|
</UploadProgressContext.Provider>
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ export function MinimizedUploadProgress(props) {
|
||||||
sx={{
|
sx={{
|
||||||
width: '360px',
|
width: '360px',
|
||||||
}}>
|
}}>
|
||||||
<UploadProgressHeader {...props} />
|
<UploadProgressHeader />
|
||||||
</Paper>
|
</Paper>
|
||||||
</Snackbar>
|
</Snackbar>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
import React from 'react';
|
import React, { useContext } from 'react';
|
||||||
import { LinearProgress, Divider, Box } from '@mui/material';
|
import { LinearProgress, Divider, Box } from '@mui/material';
|
||||||
import { UPLOAD_STAGES } from 'constants/upload';
|
import { UPLOAD_STAGES } from 'constants/upload';
|
||||||
|
import UploadProgressContext from 'contexts/uploadProgress';
|
||||||
|
|
||||||
export function UploadProgressBar({ uploadStage, now }) {
|
export function UploadProgressBar() {
|
||||||
|
const { uploadStage, percentComplete } = useContext(UploadProgressContext);
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
{(uploadStage === UPLOAD_STAGES.READING_GOOGLE_METADATA_FILES ||
|
{(uploadStage === UPLOAD_STAGES.READING_GOOGLE_METADATA_FILES ||
|
||||||
|
@ -15,7 +17,7 @@ export function UploadProgressBar({ uploadStage, now }) {
|
||||||
backgroundColor: 'transparent',
|
backgroundColor: 'transparent',
|
||||||
}}
|
}}
|
||||||
variant="determinate"
|
variant="determinate"
|
||||||
value={now}
|
value={percentComplete}
|
||||||
/>
|
/>
|
||||||
<Divider />
|
<Divider />
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -1,25 +1,28 @@
|
||||||
import React from 'react';
|
import React, { useContext } from 'react';
|
||||||
import FileList from 'components/FileList';
|
import FileList from 'components/FileList';
|
||||||
import { Typography } from '@mui/material';
|
import { Typography } from '@mui/material';
|
||||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||||
import { ResultItemContainer } from './styledComponents';
|
import { ResultItemContainer } from './styledComponents';
|
||||||
import { FileUploadResults } from 'constants/upload';
|
import { UPLOAD_RESULT } from 'constants/upload';
|
||||||
import {
|
import {
|
||||||
SectionInfo,
|
SectionInfo,
|
||||||
UploadProgressSection,
|
UploadProgressSection,
|
||||||
UploadProgressSectionContent,
|
UploadProgressSectionContent,
|
||||||
UploadProgressSectionTitle,
|
UploadProgressSectionTitle,
|
||||||
} from './section';
|
} from './section';
|
||||||
|
import UploadProgressContext from 'contexts/uploadProgress';
|
||||||
|
|
||||||
export interface ResultSectionProps {
|
export interface ResultSectionProps {
|
||||||
filenames: Map<number, string>;
|
uploadResult: UPLOAD_RESULT;
|
||||||
fileUploadResultMap: Map<FileUploadResults, number[]>;
|
|
||||||
fileUploadResult: FileUploadResults;
|
|
||||||
sectionTitle: any;
|
sectionTitle: any;
|
||||||
sectionInfo?: any;
|
sectionInfo?: any;
|
||||||
}
|
}
|
||||||
export const ResultSection = (props: ResultSectionProps) => {
|
export const ResultSection = (props: ResultSectionProps) => {
|
||||||
const fileList = props.fileUploadResultMap?.get(props.fileUploadResult);
|
const { finishedUploads, uploadFileNames } = useContext(
|
||||||
|
UploadProgressContext
|
||||||
|
);
|
||||||
|
const fileList = finishedUploads.get(props.uploadResult);
|
||||||
|
|
||||||
if (!fileList?.length) {
|
if (!fileList?.length) {
|
||||||
return <></>;
|
return <></>;
|
||||||
}
|
}
|
||||||
|
@ -35,7 +38,7 @@ export const ResultSection = (props: ResultSectionProps) => {
|
||||||
<FileList
|
<FileList
|
||||||
fileList={fileList.map((fileID) => (
|
fileList={fileList.map((fileID) => (
|
||||||
<ResultItemContainer key={fileID}>
|
<ResultItemContainer key={fileID}>
|
||||||
{props.filenames.get(fileID)}
|
{uploadFileNames.get(fileID)}
|
||||||
</ResultItemContainer>
|
</ResultItemContainer>
|
||||||
))}
|
))}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React from 'react';
|
import React, { useContext } from 'react';
|
||||||
import Close from '@mui/icons-material/Close';
|
import Close from '@mui/icons-material/Close';
|
||||||
import {
|
import {
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
|
@ -13,6 +13,7 @@ import { UPLOAD_STAGES } from 'constants/upload';
|
||||||
import constants from 'utils/strings/constants';
|
import constants from 'utils/strings/constants';
|
||||||
import { MaximizeIcon } from 'components/icons/Maximize';
|
import { MaximizeIcon } from 'components/icons/Maximize';
|
||||||
import { MinimizeIcon } from 'components/icons/Minimize';
|
import { MinimizeIcon } from 'components/icons/Minimize';
|
||||||
|
import UploadProgressContext from 'contexts/uploadProgress';
|
||||||
|
|
||||||
const IconButtonWithBG = styled(IconButton)(({ theme }) => ({
|
const IconButtonWithBG = styled(IconButton)(({ theme }) => ({
|
||||||
backgroundColor: theme.palette.secondary.main,
|
backgroundColor: theme.palette.secondary.main,
|
||||||
|
@ -33,24 +34,22 @@ line-height: 36px;
|
||||||
</Typography>
|
</Typography>
|
||||||
);
|
);
|
||||||
|
|
||||||
function UploadProgressSubtitleText(props) {
|
function UploadProgressSubtitleText() {
|
||||||
|
const { uploadStage, uploadCounter } = useContext(UploadProgressContext);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Typography color="text.secondary">
|
<Typography color="text.secondary">
|
||||||
{props.uploadStage === UPLOAD_STAGES.UPLOADING
|
{uploadStage === UPLOAD_STAGES.UPLOADING
|
||||||
? constants.UPLOAD_STAGE_MESSAGE[props.uploadStage](
|
? constants.UPLOAD_STAGE_MESSAGE[uploadStage](uploadCounter)
|
||||||
props.fileCounter
|
: constants.UPLOAD_STAGE_MESSAGE[uploadStage]}
|
||||||
)
|
|
||||||
: constants.UPLOAD_STAGE_MESSAGE[props.uploadStage]}
|
|
||||||
</Typography>
|
</Typography>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function UploadProgressTitle({
|
export function UploadProgressTitle() {
|
||||||
setExpanded,
|
const { setExpanded, onClose, expanded } = useContext(
|
||||||
expanded,
|
UploadProgressContext
|
||||||
handleClose,
|
);
|
||||||
...props
|
|
||||||
}) {
|
|
||||||
const toggleExpanded = () => setExpanded((expanded) => !expanded);
|
const toggleExpanded = () => setExpanded((expanded) => !expanded);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -58,14 +57,14 @@ export function UploadProgressTitle({
|
||||||
<SpaceBetweenFlex>
|
<SpaceBetweenFlex>
|
||||||
<Box>
|
<Box>
|
||||||
<UploadProgressTitleText expanded={expanded} />
|
<UploadProgressTitleText expanded={expanded} />
|
||||||
<UploadProgressSubtitleText {...props} />
|
<UploadProgressSubtitleText />
|
||||||
</Box>
|
</Box>
|
||||||
<Box>
|
<Box>
|
||||||
<Stack direction={'row'} spacing={1}>
|
<Stack direction={'row'} spacing={1}>
|
||||||
<IconButtonWithBG onClick={toggleExpanded}>
|
<IconButtonWithBG onClick={toggleExpanded}>
|
||||||
{expanded ? <MinimizeIcon /> : <MaximizeIcon />}
|
{expanded ? <MinimizeIcon /> : <MaximizeIcon />}
|
||||||
</IconButtonWithBG>
|
</IconButtonWithBG>
|
||||||
<IconButtonWithBG onClick={handleClose}>
|
<IconButtonWithBG onClick={onClose.bind(null)}>
|
||||||
<Close />
|
<Close />
|
||||||
</IconButtonWithBG>
|
</IconButtonWithBG>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
|
@ -19,7 +19,6 @@ import { METADATA_FOLDER_NAME } from 'constants/export';
|
||||||
import { CustomError } from 'utils/error';
|
import { CustomError } from 'utils/error';
|
||||||
import { Collection } from 'types/collection';
|
import { Collection } from 'types/collection';
|
||||||
import { SetLoading, SetFiles } from 'types/gallery';
|
import { SetLoading, SetFiles } from 'types/gallery';
|
||||||
import { FileUploadResults, UPLOAD_STAGES } from 'constants/upload';
|
|
||||||
import { ElectronFile, FileWithCollection } from 'types/upload';
|
import { ElectronFile, FileWithCollection } from 'types/upload';
|
||||||
import UploadTypeSelector from '../../UploadTypeSelector';
|
import UploadTypeSelector from '../../UploadTypeSelector';
|
||||||
import Router from 'next/router';
|
import Router from 'next/router';
|
||||||
|
@ -27,6 +26,13 @@ import { isCanvasBlocked } from 'utils/upload/isCanvasBlocked';
|
||||||
import { downloadApp } from 'utils/common';
|
import { downloadApp } from 'utils/common';
|
||||||
import DiscFullIcon from '@mui/icons-material/DiscFull';
|
import DiscFullIcon from '@mui/icons-material/DiscFull';
|
||||||
import { NotificationAttributes } from 'types/Notification';
|
import { NotificationAttributes } from 'types/Notification';
|
||||||
|
import {
|
||||||
|
UploadFileNames,
|
||||||
|
InProgressUploads,
|
||||||
|
UploadCounter,
|
||||||
|
FinishedUploads,
|
||||||
|
} from 'types/upload/ui';
|
||||||
|
import { UPLOAD_STAGES } from 'constants/upload';
|
||||||
|
|
||||||
const FIRST_ALBUM_NAME = 'My First Album';
|
const FIRST_ALBUM_NAME = 'My First Album';
|
||||||
|
|
||||||
|
@ -74,16 +80,13 @@ const NULL_ANALYSIS_RESULT = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function Upload(props: Props) {
|
export default function Upload(props: Props) {
|
||||||
const [progressView, setProgressView] = useState(false);
|
const [uploadProgressView, setUploadProgressView] = useState(false);
|
||||||
const [uploadStage, setUploadStage] = useState<UPLOAD_STAGES>(
|
const [uploadStage, setUploadStage] = useState<UPLOAD_STAGES>();
|
||||||
UPLOAD_STAGES.START
|
const [uploadFileNames, setUploadFileNames] = useState<UploadFileNames>();
|
||||||
);
|
const [uploadCounter, setUploadCounter] = useState<UploadCounter>();
|
||||||
const [filenames, setFilenames] = useState(new Map<number, string>());
|
const [inProgressUploads, setInProgressUploads] =
|
||||||
const [fileCounter, setFileCounter] = useState({ finished: 0, total: 0 });
|
useState<InProgressUploads>();
|
||||||
const [fileProgress, setFileProgress] = useState(new Map<number, number>());
|
const [finishedUploads, setFinishedUploads] = useState<FinishedUploads>();
|
||||||
const [uploadResult, setUploadResult] = useState(
|
|
||||||
new Map<number, FileUploadResults>()
|
|
||||||
);
|
|
||||||
const [percentComplete, setPercentComplete] = useState(0);
|
const [percentComplete, setPercentComplete] = useState(0);
|
||||||
const [hasLivePhotos, setHasLivePhotos] = useState(false);
|
const [hasLivePhotos, setHasLivePhotos] = useState(false);
|
||||||
|
|
||||||
|
@ -103,11 +106,11 @@ export default function Upload(props: Props) {
|
||||||
UploadManager.initUploader(
|
UploadManager.initUploader(
|
||||||
{
|
{
|
||||||
setPercentComplete,
|
setPercentComplete,
|
||||||
setFileCounter,
|
setUploadCounter,
|
||||||
setFileProgress,
|
setInProgressUploads,
|
||||||
setUploadResult,
|
setFinishedUploads,
|
||||||
setUploadStage,
|
setUploadStage,
|
||||||
setFilenames,
|
setUploadFilenames: setUploadFileNames,
|
||||||
setHasLivePhotos,
|
setHasLivePhotos,
|
||||||
},
|
},
|
||||||
props.setFiles
|
props.setFiles
|
||||||
|
@ -171,12 +174,12 @@ export default function Upload(props: Props) {
|
||||||
|
|
||||||
const uploadInit = function () {
|
const uploadInit = function () {
|
||||||
setUploadStage(UPLOAD_STAGES.START);
|
setUploadStage(UPLOAD_STAGES.START);
|
||||||
setFileCounter({ finished: 0, total: 0 });
|
setUploadCounter({ finished: 0, total: 0 });
|
||||||
setFileProgress(new Map<number, number>());
|
setInProgressUploads(new Map());
|
||||||
setUploadResult(new Map<number, number>());
|
setFinishedUploads(new Map());
|
||||||
setPercentComplete(0);
|
setPercentComplete(0);
|
||||||
props.closeCollectionSelector();
|
props.closeCollectionSelector();
|
||||||
setProgressView(true);
|
setUploadProgressView(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const resumeDesktopUpload = async (
|
const resumeDesktopUpload = async (
|
||||||
|
@ -307,7 +310,7 @@ export default function Upload(props: Props) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setProgressView(false);
|
setUploadProgressView(false);
|
||||||
logError(e, 'Failed to create album');
|
logError(e, 'Failed to create album');
|
||||||
appContext.setDialogMessage({
|
appContext.setDialogMessage({
|
||||||
title: constants.ERROR,
|
title: constants.ERROR,
|
||||||
|
@ -354,7 +357,7 @@ export default function Upload(props: Props) {
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showUserFacingError(err.message);
|
showUserFacingError(err.message);
|
||||||
setProgressView(false);
|
setUploadProgressView(false);
|
||||||
throw err;
|
throw err;
|
||||||
} finally {
|
} finally {
|
||||||
props.setUploadInProgress(false);
|
props.setUploadInProgress(false);
|
||||||
|
@ -371,7 +374,7 @@ export default function Upload(props: Props) {
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showUserFacingError(err.message);
|
showUserFacingError(err.message);
|
||||||
|
|
||||||
setProgressView(false);
|
setUploadProgressView(false);
|
||||||
} finally {
|
} finally {
|
||||||
props.setUploadInProgress(false);
|
props.setUploadInProgress(false);
|
||||||
props.syncWithRemote();
|
props.syncWithRemote();
|
||||||
|
@ -494,7 +497,7 @@ export default function Upload(props: Props) {
|
||||||
};
|
};
|
||||||
|
|
||||||
const cancelUploads = async () => {
|
const cancelUploads = async () => {
|
||||||
setProgressView(false);
|
setUploadProgressView(false);
|
||||||
if (isElectron()) {
|
if (isElectron()) {
|
||||||
ImportService.cancelRemainingUploads();
|
ImportService.cancelRemainingUploads();
|
||||||
}
|
}
|
||||||
|
@ -502,6 +505,8 @@ export default function Upload(props: Props) {
|
||||||
Router.reload();
|
Router.reload();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const closeUploadProgress = () => setUploadProgressView(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<UploadStrategyChoiceModal
|
<UploadStrategyChoiceModal
|
||||||
|
@ -532,16 +537,16 @@ export default function Upload(props: Props) {
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<UploadProgress
|
<UploadProgress
|
||||||
now={percentComplete}
|
open={uploadProgressView}
|
||||||
filenames={filenames}
|
onClose={closeUploadProgress}
|
||||||
fileCounter={fileCounter}
|
percentComplete={percentComplete}
|
||||||
|
uploadFileNames={uploadFileNames}
|
||||||
|
uploadCounter={uploadCounter}
|
||||||
uploadStage={uploadStage}
|
uploadStage={uploadStage}
|
||||||
fileProgress={fileProgress}
|
inProgressUploads={inProgressUploads}
|
||||||
hasLivePhotos={hasLivePhotos}
|
hasLivePhotos={hasLivePhotos}
|
||||||
show={progressView}
|
|
||||||
closeModal={() => setProgressView(false)}
|
|
||||||
retryFailed={retryFailed}
|
retryFailed={retryFailed}
|
||||||
uploadResult={uploadResult}
|
finishedUploads={finishedUploads}
|
||||||
cancelUploads={cancelUploads}
|
cancelUploads={cancelUploads}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -31,7 +31,7 @@ export enum UPLOAD_STAGES {
|
||||||
FINISH,
|
FINISH,
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum FileUploadResults {
|
export enum UPLOAD_RESULT {
|
||||||
FAILED,
|
FAILED,
|
||||||
ALREADY_UPLOADED,
|
ALREADY_UPLOADED,
|
||||||
UNSUPPORTED,
|
UNSUPPORTED,
|
||||||
|
|
43
src/contexts/uploadProgress.tsx
Normal file
43
src/contexts/uploadProgress.tsx
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
import { DialogProps } from '@mui/material';
|
||||||
|
import { UPLOAD_STAGES } from 'constants/upload';
|
||||||
|
import { createContext } from 'react';
|
||||||
|
import {
|
||||||
|
UploadCounter,
|
||||||
|
InProgressUpload,
|
||||||
|
UploadFileNames,
|
||||||
|
SegregatedFinishedUploads,
|
||||||
|
} from 'types/upload/ui';
|
||||||
|
|
||||||
|
interface UploadProgressContextType {
|
||||||
|
open: boolean;
|
||||||
|
onClose: DialogProps['onClose'];
|
||||||
|
uploadCounter: UploadCounter;
|
||||||
|
uploadStage: UPLOAD_STAGES;
|
||||||
|
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,
|
||||||
|
uploadStage: null,
|
||||||
|
percentComplete: null,
|
||||||
|
retryFailed: () => null,
|
||||||
|
inProgressUploads: null,
|
||||||
|
uploadFileNames: null,
|
||||||
|
finishedUploads: null,
|
||||||
|
hasLivePhotos: null,
|
||||||
|
expanded: null,
|
||||||
|
setExpanded: () => null,
|
||||||
|
};
|
||||||
|
const UploadProgressContext = createContext<UploadProgressContextType>(
|
||||||
|
defaultUploadProgressContext
|
||||||
|
);
|
||||||
|
|
||||||
|
export default UploadProgressContext;
|
|
@ -1,16 +1,16 @@
|
||||||
import {
|
import {
|
||||||
FileUploadResults,
|
UPLOAD_RESULT,
|
||||||
RANDOM_PERCENTAGE_PROGRESS_FOR_PUT,
|
RANDOM_PERCENTAGE_PROGRESS_FOR_PUT,
|
||||||
UPLOAD_STAGES,
|
UPLOAD_STAGES,
|
||||||
} from 'constants/upload';
|
} from 'constants/upload';
|
||||||
import { ProgressUpdater } from 'types/upload';
|
import { ProgressUpdater } from 'types/upload/ui';
|
||||||
|
|
||||||
class UIService {
|
class UIService {
|
||||||
private perFileProgress: number;
|
private perFileProgress: number;
|
||||||
private filesUploaded: number;
|
private filesUploaded: number;
|
||||||
private totalFileCount: number;
|
private totalFileCount: number;
|
||||||
private fileProgress: Map<number, number>;
|
private inProgressUploads: Map<number, number>;
|
||||||
private uploadResult: Map<number, FileUploadResults>;
|
private finishedUploads: Map<number, UPLOAD_RESULT>;
|
||||||
private progressUpdater: ProgressUpdater;
|
private progressUpdater: ProgressUpdater;
|
||||||
|
|
||||||
init(progressUpdater: ProgressUpdater) {
|
init(progressUpdater: ProgressUpdater) {
|
||||||
|
@ -20,8 +20,8 @@ class UIService {
|
||||||
reset(count: number) {
|
reset(count: number) {
|
||||||
this.setTotalFileCount(count);
|
this.setTotalFileCount(count);
|
||||||
this.filesUploaded = 0;
|
this.filesUploaded = 0;
|
||||||
this.fileProgress = new Map<number, number>();
|
this.inProgressUploads = new Map<number, number>();
|
||||||
this.uploadResult = new Map<number, FileUploadResults>();
|
this.finishedUploads = new Map<number, UPLOAD_RESULT>();
|
||||||
this.updateProgressBarUI();
|
this.updateProgressBarUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ class UIService {
|
||||||
}
|
}
|
||||||
|
|
||||||
setFileProgress(key: number, progress: number) {
|
setFileProgress(key: number, progress: number) {
|
||||||
this.fileProgress.set(key, progress);
|
this.inProgressUploads.set(key, progress);
|
||||||
this.updateProgressBarUI();
|
this.updateProgressBarUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ class UIService {
|
||||||
}
|
}
|
||||||
|
|
||||||
setFilenames(filenames: Map<number, string>) {
|
setFilenames(filenames: Map<number, string>) {
|
||||||
this.progressUpdater.setFilenames(filenames);
|
this.progressUpdater.setUploadFilenames(filenames);
|
||||||
}
|
}
|
||||||
|
|
||||||
setHasLivePhoto(hasLivePhoto: boolean) {
|
setHasLivePhoto(hasLivePhoto: boolean) {
|
||||||
|
@ -56,18 +56,18 @@ class UIService {
|
||||||
this.updateProgressBarUI();
|
this.updateProgressBarUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
moveFileToResultList(key: number, uploadResult: FileUploadResults) {
|
moveFileToResultList(key: number, uploadResult: UPLOAD_RESULT) {
|
||||||
this.uploadResult.set(key, uploadResult);
|
this.finishedUploads.set(key, uploadResult);
|
||||||
this.fileProgress.delete(key);
|
this.inProgressUploads.delete(key);
|
||||||
this.updateProgressBarUI();
|
this.updateProgressBarUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateProgressBarUI() {
|
updateProgressBarUI() {
|
||||||
const {
|
const {
|
||||||
setPercentComplete,
|
setPercentComplete,
|
||||||
setFileCounter,
|
setUploadCounter: setFileCounter,
|
||||||
setFileProgress,
|
setInProgressUploads,
|
||||||
setUploadResult,
|
setFinishedUploads,
|
||||||
} = this.progressUpdater;
|
} = this.progressUpdater;
|
||||||
setFileCounter({
|
setFileCounter({
|
||||||
finished: this.filesUploaded,
|
finished: this.filesUploaded,
|
||||||
|
@ -75,10 +75,10 @@ class UIService {
|
||||||
});
|
});
|
||||||
let percentComplete =
|
let percentComplete =
|
||||||
this.perFileProgress *
|
this.perFileProgress *
|
||||||
(this.uploadResult.size || this.filesUploaded);
|
(this.finishedUploads.size || this.filesUploaded);
|
||||||
if (this.fileProgress) {
|
if (this.inProgressUploads) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
for (const [_, progress] of this.fileProgress) {
|
for (const [_, progress] of this.inProgressUploads) {
|
||||||
// filter negative indicator values during percentComplete calculation
|
// filter negative indicator values during percentComplete calculation
|
||||||
if (progress < 0) {
|
if (progress < 0) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -88,8 +88,8 @@ class UIService {
|
||||||
}
|
}
|
||||||
|
|
||||||
setPercentComplete(percentComplete);
|
setPercentComplete(percentComplete);
|
||||||
setFileProgress(this.fileProgress);
|
setInProgressUploads(this.inProgressUploads);
|
||||||
setUploadResult(this.uploadResult);
|
setFinishedUploads(this.finishedUploads);
|
||||||
}
|
}
|
||||||
|
|
||||||
trackUploadProgress(
|
trackUploadProgress(
|
||||||
|
@ -108,7 +108,7 @@ class UIService {
|
||||||
return {
|
return {
|
||||||
cancel,
|
cancel,
|
||||||
onUploadProgress: (event) => {
|
onUploadProgress: (event) => {
|
||||||
this.fileProgress.set(
|
this.inProgressUploads.set(
|
||||||
fileLocalID,
|
fileLocalID,
|
||||||
Math.min(
|
Math.min(
|
||||||
Math.round(
|
Math.round(
|
||||||
|
|
|
@ -26,12 +26,11 @@ import {
|
||||||
MetadataAndFileTypeInfoMap,
|
MetadataAndFileTypeInfoMap,
|
||||||
ParsedMetadataJSON,
|
ParsedMetadataJSON,
|
||||||
ParsedMetadataJSONMap,
|
ParsedMetadataJSONMap,
|
||||||
ProgressUpdater,
|
|
||||||
} from 'types/upload';
|
} from 'types/upload';
|
||||||
import {
|
import {
|
||||||
UPLOAD_STAGES,
|
UPLOAD_RESULT,
|
||||||
FileUploadResults,
|
|
||||||
MAX_FILE_SIZE_SUPPORTED,
|
MAX_FILE_SIZE_SUPPORTED,
|
||||||
|
UPLOAD_STAGES,
|
||||||
} from 'constants/upload';
|
} from 'constants/upload';
|
||||||
import { ComlinkWorker } from 'utils/comlink';
|
import { ComlinkWorker } from 'utils/comlink';
|
||||||
import { FILE_TYPE } from 'constants/file';
|
import { FILE_TYPE } from 'constants/file';
|
||||||
|
@ -39,6 +38,7 @@ import uiService from './uiService';
|
||||||
import { logUploadInfo } from 'utils/upload';
|
import { logUploadInfo } from 'utils/upload';
|
||||||
import isElectron from 'is-electron';
|
import isElectron from 'is-electron';
|
||||||
import ImportService from 'services/importService';
|
import ImportService from 'services/importService';
|
||||||
|
import { ProgressUpdater } from 'types/upload/ui';
|
||||||
|
|
||||||
const MAX_CONCURRENT_UPLOADS = 4;
|
const MAX_CONCURRENT_UPLOADS = 4;
|
||||||
const FILE_UPLOAD_COMPLETED = 100;
|
const FILE_UPLOAD_COMPLETED = 100;
|
||||||
|
@ -350,7 +350,7 @@ class UploadManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
async postUploadTask(
|
async postUploadTask(
|
||||||
fileUploadResult: FileUploadResults,
|
fileUploadResult: UPLOAD_RESULT,
|
||||||
uploadedFile: EnteFile,
|
uploadedFile: EnteFile,
|
||||||
skipDecryption: boolean,
|
skipDecryption: boolean,
|
||||||
fileWithCollection: FileWithCollection
|
fileWithCollection: FileWithCollection
|
||||||
|
@ -359,9 +359,9 @@ class UploadManager {
|
||||||
logUploadInfo(`uploadedFile ${JSON.stringify(uploadedFile)}`);
|
logUploadInfo(`uploadedFile ${JSON.stringify(uploadedFile)}`);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(fileUploadResult === FileUploadResults.UPLOADED ||
|
(fileUploadResult === UPLOAD_RESULT.UPLOADED ||
|
||||||
fileUploadResult ===
|
fileUploadResult ===
|
||||||
FileUploadResults.UPLOADED_WITH_STATIC_THUMBNAIL) &&
|
UPLOAD_RESULT.UPLOADED_WITH_STATIC_THUMBNAIL) &&
|
||||||
!skipDecryption
|
!skipDecryption
|
||||||
) {
|
) {
|
||||||
const decryptedFile = await decryptFile(
|
const decryptedFile = await decryptFile(
|
||||||
|
@ -387,8 +387,8 @@ class UploadManager {
|
||||||
.push(decryptedFile);
|
.push(decryptedFile);
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
fileUploadResult === FileUploadResults.FAILED ||
|
fileUploadResult === UPLOAD_RESULT.FAILED ||
|
||||||
fileUploadResult === FileUploadResults.BLOCKED
|
fileUploadResult === UPLOAD_RESULT.BLOCKED
|
||||||
) {
|
) {
|
||||||
this.failedFiles.push(fileWithCollection);
|
this.failedFiles.push(fileWithCollection);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ import UploadHttpClient from './uploadHttpClient';
|
||||||
import UIService from './uiService';
|
import UIService from './uiService';
|
||||||
import UploadService from './uploadService';
|
import UploadService from './uploadService';
|
||||||
import { FILE_TYPE } from 'constants/file';
|
import { FILE_TYPE } from 'constants/file';
|
||||||
import { FileUploadResults, MAX_FILE_SIZE_SUPPORTED } from 'constants/upload';
|
import { UPLOAD_RESULT, MAX_FILE_SIZE_SUPPORTED } from 'constants/upload';
|
||||||
import { FileWithCollection, BackupedFile, UploadFile } from 'types/upload';
|
import { FileWithCollection, BackupedFile, UploadFile } from 'types/upload';
|
||||||
import { logUploadInfo } from 'utils/upload';
|
import { logUploadInfo } from 'utils/upload';
|
||||||
import { convertBytesToHumanReadable } from 'utils/billing';
|
import { convertBytesToHumanReadable } from 'utils/billing';
|
||||||
|
@ -18,7 +18,7 @@ import { sleep } from 'utils/common';
|
||||||
import { addToCollection } from 'services/collectionService';
|
import { addToCollection } from 'services/collectionService';
|
||||||
|
|
||||||
interface UploadResponse {
|
interface UploadResponse {
|
||||||
fileUploadResult: FileUploadResults;
|
fileUploadResult: UPLOAD_RESULT;
|
||||||
uploadedFile?: EnteFile;
|
uploadedFile?: EnteFile;
|
||||||
skipDecryption?: boolean;
|
skipDecryption?: boolean;
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ export default async function uploader(
|
||||||
try {
|
try {
|
||||||
const fileSize = UploadService.getAssetSize(uploadAsset);
|
const fileSize = UploadService.getAssetSize(uploadAsset);
|
||||||
if (fileSize >= MAX_FILE_SIZE_SUPPORTED) {
|
if (fileSize >= MAX_FILE_SIZE_SUPPORTED) {
|
||||||
return { fileUploadResult: FileUploadResults.TOO_LARGE };
|
return { fileUploadResult: UPLOAD_RESULT.TOO_LARGE };
|
||||||
}
|
}
|
||||||
if (fileTypeInfo.fileType === FILE_TYPE.OTHERS) {
|
if (fileTypeInfo.fileType === FILE_TYPE.OTHERS) {
|
||||||
throw Error(CustomError.UNSUPPORTED_FILE_FORMAT);
|
throw Error(CustomError.UNSUPPORTED_FILE_FORMAT);
|
||||||
|
@ -52,7 +52,7 @@ export default async function uploader(
|
||||||
|
|
||||||
if (fileAlreadyInCollection(existingFilesInCollection, metadata)) {
|
if (fileAlreadyInCollection(existingFilesInCollection, metadata)) {
|
||||||
logUploadInfo(`skipped upload for ${fileNameSize}`);
|
logUploadInfo(`skipped upload for ${fileNameSize}`);
|
||||||
return { fileUploadResult: FileUploadResults.ALREADY_UPLOADED };
|
return { fileUploadResult: UPLOAD_RESULT.ALREADY_UPLOADED };
|
||||||
}
|
}
|
||||||
|
|
||||||
const sameFileInOtherCollection = findSameFileInOtherCollection(
|
const sameFileInOtherCollection = findSameFileInOtherCollection(
|
||||||
|
@ -68,7 +68,7 @@ export default async function uploader(
|
||||||
resultFile.collectionID = collection.id;
|
resultFile.collectionID = collection.id;
|
||||||
await addToCollection(collection, [resultFile]);
|
await addToCollection(collection, [resultFile]);
|
||||||
return {
|
return {
|
||||||
fileUploadResult: FileUploadResults.UPLOADED,
|
fileUploadResult: UPLOAD_RESULT.UPLOADED,
|
||||||
uploadedFile: resultFile,
|
uploadedFile: resultFile,
|
||||||
skipDecryption: true,
|
skipDecryption: true,
|
||||||
};
|
};
|
||||||
|
@ -82,7 +82,7 @@ export default async function uploader(
|
||||||
fileAlreadyInCollection(existingFiles, metadata)
|
fileAlreadyInCollection(existingFiles, metadata)
|
||||||
) {
|
) {
|
||||||
logUploadInfo(`deduped upload for ${fileNameSize}`);
|
logUploadInfo(`deduped upload for ${fileNameSize}`);
|
||||||
return { fileUploadResult: FileUploadResults.ALREADY_UPLOADED };
|
return { fileUploadResult: UPLOAD_RESULT.ALREADY_UPLOADED };
|
||||||
}
|
}
|
||||||
logUploadInfo(`reading asset ${fileNameSize}`);
|
logUploadInfo(`reading asset ${fileNameSize}`);
|
||||||
|
|
||||||
|
@ -125,8 +125,8 @@ export default async function uploader(
|
||||||
|
|
||||||
return {
|
return {
|
||||||
fileUploadResult: metadata.hasStaticThumbnail
|
fileUploadResult: metadata.hasStaticThumbnail
|
||||||
? FileUploadResults.UPLOADED_WITH_STATIC_THUMBNAIL
|
? UPLOAD_RESULT.UPLOADED_WITH_STATIC_THUMBNAIL
|
||||||
: FileUploadResults.UPLOADED,
|
: UPLOAD_RESULT.UPLOADED,
|
||||||
uploadedFile: uploadedFile,
|
uploadedFile: uploadedFile,
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -140,16 +140,16 @@ export default async function uploader(
|
||||||
const error = handleUploadError(e);
|
const error = handleUploadError(e);
|
||||||
switch (error.message) {
|
switch (error.message) {
|
||||||
case CustomError.ETAG_MISSING:
|
case CustomError.ETAG_MISSING:
|
||||||
return { fileUploadResult: FileUploadResults.BLOCKED };
|
return { fileUploadResult: UPLOAD_RESULT.BLOCKED };
|
||||||
case CustomError.UNSUPPORTED_FILE_FORMAT:
|
case CustomError.UNSUPPORTED_FILE_FORMAT:
|
||||||
return { fileUploadResult: FileUploadResults.UNSUPPORTED };
|
return { fileUploadResult: UPLOAD_RESULT.UNSUPPORTED };
|
||||||
case CustomError.FILE_TOO_LARGE:
|
case CustomError.FILE_TOO_LARGE:
|
||||||
return {
|
return {
|
||||||
fileUploadResult:
|
fileUploadResult:
|
||||||
FileUploadResults.LARGER_THAN_AVAILABLE_STORAGE,
|
UPLOAD_RESULT.LARGER_THAN_AVAILABLE_STORAGE,
|
||||||
};
|
};
|
||||||
default:
|
default:
|
||||||
return { fileUploadResult: FileUploadResults.FAILED };
|
return { fileUploadResult: UPLOAD_RESULT.FAILED };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { FILE_TYPE } from 'constants/file';
|
import { FILE_TYPE } from 'constants/file';
|
||||||
import { UPLOAD_STAGES } from 'constants/upload';
|
|
||||||
import { Collection } from 'types/collection';
|
import { Collection } from 'types/collection';
|
||||||
import { fileAttribute } from 'types/file';
|
import { fileAttribute } from 'types/file';
|
||||||
|
|
||||||
|
@ -56,21 +55,6 @@ export interface FileTypeInfo {
|
||||||
videoType?: string;
|
videoType?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ProgressUpdater {
|
|
||||||
setPercentComplete: React.Dispatch<React.SetStateAction<number>>;
|
|
||||||
setFileCounter: React.Dispatch<
|
|
||||||
React.SetStateAction<{
|
|
||||||
finished: number;
|
|
||||||
total: number;
|
|
||||||
}>
|
|
||||||
>;
|
|
||||||
setUploadStage: React.Dispatch<React.SetStateAction<UPLOAD_STAGES>>;
|
|
||||||
setFileProgress: React.Dispatch<React.SetStateAction<Map<number, number>>>;
|
|
||||||
setUploadResult: React.Dispatch<React.SetStateAction<Map<number, number>>>;
|
|
||||||
setFilenames: React.Dispatch<React.SetStateAction<Map<number, string>>>;
|
|
||||||
setHasLivePhotos: React.Dispatch<React.SetStateAction<boolean>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ElectronFile is a custom interface that is used to represent
|
* ElectronFile is a custom interface that is used to represent
|
||||||
* any file on disk as a File-like object in the Electron desktop app.
|
* any file on disk as a File-like object in the Electron desktop app.
|
||||||
|
|
40
src/types/upload/ui.ts
Normal file
40
src/types/upload/ui.ts
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import { UPLOAD_RESULT, UPLOAD_STAGES } from 'constants/upload';
|
||||||
|
|
||||||
|
export type FileID = number;
|
||||||
|
export type FileName = string;
|
||||||
|
|
||||||
|
export type PercentageUploaded = number;
|
||||||
|
export type UploadFileNames = Map<FileID, FileName>;
|
||||||
|
|
||||||
|
export interface UploadCounter {
|
||||||
|
finished: number;
|
||||||
|
total: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InProgressUpload {
|
||||||
|
localFileID: FileID;
|
||||||
|
progress: PercentageUploaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FinishedUpload {
|
||||||
|
localFileID: FileID;
|
||||||
|
result: UPLOAD_RESULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type InProgressUploads = Map<FileID, PercentageUploaded>;
|
||||||
|
|
||||||
|
export type FinishedUploads = Map<FileID, UPLOAD_RESULT>;
|
||||||
|
|
||||||
|
export type SegregatedFinishedUploads = Map<UPLOAD_RESULT, FileID[]>;
|
||||||
|
|
||||||
|
export interface ProgressUpdater {
|
||||||
|
setPercentComplete: React.Dispatch<React.SetStateAction<number>>;
|
||||||
|
setUploadCounter: React.Dispatch<React.SetStateAction<UploadCounter>>;
|
||||||
|
setUploadStage: React.Dispatch<React.SetStateAction<UPLOAD_STAGES>>;
|
||||||
|
setInProgressUploads: React.Dispatch<
|
||||||
|
React.SetStateAction<InProgressUploads>
|
||||||
|
>;
|
||||||
|
setFinishedUploads: React.Dispatch<React.SetStateAction<FinishedUploads>>;
|
||||||
|
setUploadFilenames: React.Dispatch<React.SetStateAction<UploadFileNames>>;
|
||||||
|
setHasLivePhotos: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
|
}
|
Loading…
Reference in a new issue