feat(Upload): remove upload state from the Upload context. Instead use a store for it

This commit is contained in:
Aminejv 2021-11-22 14:15:39 +01:00
parent f189affcbb
commit 14faf2eb2f
5 changed files with 205 additions and 183 deletions

View File

@ -6,11 +6,10 @@ import * as Logging from "~/common/logging";
import * as Strings from "~/common/strings";
import * as Styles from "~/common/styles";
import * as Constants from "~/common/constants";
import * as SVG from "~/common/svg";
import { css } from "@emotion/react";
import { useUploadContext } from "~/components/core/Upload/Provider";
import { AnimatePresence, motion } from "framer-motion";
import { useUploadStore } from "~/components/core/Upload/store";
const STYLES_LINK_INPUT = (theme) => css`
background-color: ${theme.semantic.bgWhite};
@ -18,21 +17,6 @@ const STYLES_LINK_INPUT = (theme) => css`
margin-right: 8px;
`;
const STYLES_JUMPER_OVERLAY = (theme) => css`
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: ${theme.zindex.jumper};
@supports ((-webkit-backdrop-filter: blur(75px)) or (backdrop-filter: blur(75px))) {
-webkit-backdrop-filter: blur(75px);
backdrop-filter: blur(75px);
background-color: ${theme.semantic.bgBlurLightTRN};
}
`;
const STYLES_FILE_HIDDEN = css`
height: 1px;
width: 1px;
@ -61,7 +45,9 @@ const STYLES_MOBILE_HIDDEN = css`
`;
export function UploadJumper({ data }) {
const [{ isUploadJumperVisible }, { upload, uploadLink, hideUploadJumper }] = useUploadContext();
const [{ isUploadJumperVisible }, { hideUploadJumper }] = useUploadContext();
const { upload, uploadLink } = useUploadStore((store) => store.handlers);
const [state, setState] = React.useState({
url: "",

View File

@ -5,16 +5,16 @@ import * as Constants from "~/common/constants";
import * as SVG from "~/common/svg";
import * as Strings from "~/common/strings";
import { useUploadContext } from "~/components/core/Upload/Provider";
import { motion, AnimatePresence } from "framer-motion";
import { css } from "@emotion/react";
import { Match, Switch } from "~/components/utility/Switch";
import { Show } from "~/components/utility/Show";
import { useHover } from "~/common/hooks";
import { clamp } from "lodash";
import { useUploadStore } from "~/components/core/Upload/store";
import DataMeter from "~/components/core/DataMeter";
import BlobObjectPreview from "~/components/core/BlobObjectPreview";
import { clamp } from "lodash";
/* -------------------------------------------------------------------------------------------------
* Popup
* -----------------------------------------------------------------------------------------------*/
@ -58,7 +58,11 @@ const STYLES_POPUP_CONTENT = css`
`;
const useUploadPopup = ({ totalFilesSummary }) => {
const [{ isFinished }, { resetUploadState }] = useUploadContext();
const {
state: { isFinished },
handlers: { resetUploadState },
} = useUploadStore();
const [popupState, setPopupState] = React.useState({
isVisible: false,
isSummaryExpanded: false,
@ -159,7 +163,11 @@ const useUploadSummary = ({ fileLoading }) =>
}, [fileLoading]);
export function Popup() {
const [{ isFinished, fileLoading }, { resetUploadState }] = useUploadContext();
const {
state: { isFinished, fileLoading },
handlers: { resetUploadState },
} = useUploadStore();
const { uploadSummary, totalFilesSummary } = useUploadSummary({ fileLoading });
const [isHovered, { handleOnMouseEnter, handleOnMouseLeave }] = useHover();
@ -244,7 +252,10 @@ const STYLES_RESET_BORDER_TOP = css`
`;
function Header({ totalFilesSummary, popupState, expandUploadSummary, collapseUploadSummary }) {
const [{ isFinished, totalBytesUploaded, totalBytes }, { retryAll }] = useUploadContext();
const {
state: { isFinished, totalBytesUploaded, totalBytes },
handlers: { retryAll },
} = useUploadStore();
const uploadProgress = clamp(Math.floor((totalBytesUploaded / totalBytes) * 100), 0, 100);
@ -353,7 +364,8 @@ const STYLES_SUMMARY_ACTION = css`
`;
function Summary({ uploadSummary }) {
const [, { retry, cancel }] = useUploadContext();
const { retry, cancel } = useUploadStore((store) => store.handlers);
return (
<div css={STYLES_SUMMARY}>
{uploadSummary.map((file) => (

View File

@ -1,15 +1,15 @@
import * as React from "react";
import * as UploadUtilities from "~/common/upload-utilities";
import * as FileUtilities from "~/common/file-utilities";
import * as Logging from "~/common/logging";
import { useEventListener } from "~/common/hooks";
import { useUploadStore } from "~/components/core/Upload/store";
const UploadContext = React.createContext({});
export const useUploadContext = () => React.useContext(UploadContext);
export const Provider = ({ children, page, data, viewer }) => {
const [uploadState, uploadHandlers] = useUpload({});
const uploadHandlers = useUploadStore((store) => store.handlers);
const [isUploadJumperVisible, { showUploadJumper, hideUploadJumper }] = useUploadJumper();
@ -27,14 +27,13 @@ export const Provider = ({ children, page, data, viewer }) => {
const providerValue = React.useMemo(
() => [
{ ...uploadState, isUploadJumperVisible },
{ isUploadJumperVisible },
{
...uploadHandlers,
showUploadJumper,
hideUploadJumper,
},
],
[uploadHandlers, uploadState, isUploadJumperVisible]
[isUploadJumperVisible]
);
return <UploadContext.Provider value={providerValue}>{children}</UploadContext.Provider>;
@ -47,159 +46,6 @@ const useUploadJumper = () => {
return [isUploadJumperVisible, { showUploadJumper, hideUploadJumper }];
};
const useUpload = () => {
const DEFAULT_STATE = {
fileLoading: {},
isUploading: false,
totalBytesUploaded: 0,
totalBytes: 0,
totalFilesUploaded: 0,
totalFiles: 0,
isFinished: false,
};
const [uploadState, setUploadState] = React.useState(DEFAULT_STATE);
const uploadProvider = React.useMemo(() => {
const handleStartUploading = () => {
setUploadState((prev) => ({ ...prev, isFinished: false, isUploading: true }));
};
const handleFinishUploading = () => {
setUploadState((prev) => ({
...DEFAULT_STATE,
fileLoading: prev.fileLoading,
isFinished: true,
}));
};
const handleAddToQueue = (file) => {
const fileKey = UploadUtilities.getFileKey(file);
setUploadState((prev) => ({
...prev,
fileLoading: {
...prev.fileLoading,
[fileKey]: {
id: fileKey,
status: "saving",
name: file.name,
type: file.type,
createdAt: Date.now(),
loaded: 0,
total: file.size,
blob: file,
},
},
isFinished: false,
totalFiles: prev.totalFiles + 1,
totalBytes: prev.totalBytes + file.size,
}));
};
const handleSuccess = ({ fileKey, cid }) => {
setUploadState((prev) => {
const newFileLoading = { ...prev.fileLoading };
newFileLoading[fileKey].status = "saved";
newFileLoading[fileKey].cid = cid;
return {
...prev,
fileLoading: newFileLoading,
totalFilesUploaded: prev.totalFilesUploaded + 1,
};
});
};
const handleDuplicate = ({ fileKey, cid }) => {
setUploadState((prev) => {
const newFileLoading = { ...prev.fileLoading };
newFileLoading[fileKey].status = "duplicate";
newFileLoading[fileKey].cid = cid;
return {
...prev,
fileLoading: newFileLoading,
totalFilesUploaded: prev.totalFilesUploaded + 1,
};
});
};
const handleProgress = ({ fileKey, loaded }) => {
setUploadState((prev) => {
const newFileLoading = { ...prev.fileLoading };
const bytesLoaded = loaded - newFileLoading[fileKey].loaded;
newFileLoading[fileKey].loaded = loaded;
return {
...prev,
fileLoading: newFileLoading,
totalBytesUploaded: prev.totalBytesUploaded + bytesLoaded,
};
});
};
const handleError = ({ fileKey }) => {
setUploadState((prev) => {
const newFileLoading = { ...prev.fileLoading };
newFileLoading[fileKey].status = "failed";
return {
...prev,
fileLoading: newFileLoading,
totalFiles: prev.totalFiles - 1,
totalBytes: prev.totalBytes - newFileLoading[fileKey].total,
totalBytesUploaded: prev.totalBytesUploaded - newFileLoading[fileKey].total,
};
});
};
const handleCancelUploading = ({ fileKeys }) => {
setUploadState((prev) => {
const newFileLoading = { ...prev.fileLoading };
const newTotalFiles = prev.totalFiles - fileKeys.length;
let newTotalBytes = prev.totalBytes;
let newTotalBytesUploaded = prev.totalBytesUploaded;
fileKeys.forEach((fileKey) => {
newTotalBytes -= newFileLoading[fileKey].total;
newTotalBytesUploaded -= newFileLoading[fileKey].loaded;
delete newFileLoading[fileKey];
});
return {
...prev,
fileLoading: newFileLoading,
totalFiles: newTotalFiles,
totalBytes: newTotalBytes,
totalBytesUploaded: newTotalBytesUploaded,
};
});
};
return UploadUtilities.createUploadProvider({
onStart: handleStartUploading,
onFinish: handleFinishUploading,
onAddedToQueue: handleAddToQueue,
onSuccess: handleSuccess,
onDuplicate: handleDuplicate,
onProgress: handleProgress,
onCancel: handleCancelUploading,
onError: handleError,
});
}, []);
const resetUploadState = () => (uploadProvider.clearUploadCache(), setUploadState(DEFAULT_STATE));
return [
uploadState,
{
upload: uploadProvider.upload,
uploadLink: uploadProvider.uploadLink,
retry: uploadProvider.retry,
retryAll: uploadProvider.retryAll,
cancel: uploadProvider.cancel,
cancelAll: uploadProvider.cancelAll,
resetUploadState,
},
];
};
const useUploadOnDrop = ({ upload, page, data, viewer }) => {
const handleDragEnter = (e) => e.preventDefault();
const handleDragLeave = (e) => e.preventDefault();

View File

@ -0,0 +1,177 @@
import * as UploadUtilities from "~/common/upload-utilities";
import create from "zustand";
const DEFAULT_STATE = {
fileLoading: {},
isUploading: false,
totalBytesUploaded: 0,
totalBytes: 0,
totalFilesUploaded: 0,
totalFiles: 0,
isFinished: false,
};
export const useUploadStore = create((setUploadState) => {
const handleStartUploading = () => {
setUploadState((prev) => ({
...prev,
state: { ...prev.state, isFinished: false, isUploading: true },
}));
};
const handleFinishUploading = () => {
setUploadState((prev) => ({
...prev,
state: {
...DEFAULT_STATE,
fileLoading: prev.state.fileLoading,
isFinished: true,
},
}));
};
const handleAddToQueue = (file) => {
const fileKey = UploadUtilities.getFileKey(file);
setUploadState((prev) => ({
...prev,
state: {
...prev.state,
fileLoading: {
...prev.state.fileLoading,
[fileKey]: {
id: fileKey,
status: "saving",
name: file.name,
type: file.type,
createdAt: Date.now(),
loaded: 0,
total: file.size,
blob: file,
},
},
isFinished: false,
totalFiles: prev.state.totalFiles + 1,
totalBytes: prev.state.totalBytes + file.size,
},
}));
};
const handleSuccess = ({ fileKey, cid }) => {
setUploadState((prev) => {
const newFileLoading = { ...prev.state.fileLoading };
newFileLoading[fileKey].status = "saved";
newFileLoading[fileKey].cid = cid;
return {
...prev,
state: {
...prev.state,
fileLoading: newFileLoading,
totalFilesUploaded: prev.state.totalFilesUploaded + 1,
},
};
});
};
const handleDuplicate = ({ fileKey, cid }) => {
setUploadState((prev) => {
const newFileLoading = { ...prev.state.fileLoading };
newFileLoading[fileKey].status = "duplicate";
newFileLoading[fileKey].cid = cid;
return {
...prev,
state: {
...prev.state,
fileLoading: newFileLoading,
totalFilesUploaded: prev.state.totalFilesUploaded + 1,
},
};
});
};
const handleProgress = ({ fileKey, loaded }) => {
setUploadState((prev) => {
const newFileLoading = { ...prev.state.fileLoading };
const bytesLoaded = loaded - newFileLoading[fileKey].loaded;
newFileLoading[fileKey].loaded = loaded;
return {
...prev,
state: {
...prev.state,
fileLoading: newFileLoading,
totalBytesUploaded: prev.state.totalBytesUploaded + bytesLoaded,
},
};
});
};
const handleError = ({ fileKey }) => {
setUploadState((prev) => {
const newFileLoading = { ...prev.state.fileLoading };
newFileLoading[fileKey].status = "failed";
return {
...prev,
state: {
...prev.state,
fileLoading: newFileLoading,
totalFiles: prev.state.totalFiles - 1,
totalBytes: prev.state.totalBytes - newFileLoading[fileKey].total,
totalBytesUploaded: prev.state.totalBytesUploaded - newFileLoading[fileKey].total,
},
};
});
};
const handleCancelUploading = ({ fileKeys }) => {
setUploadState((prev) => {
const newFileLoading = { ...prev.state.fileLoading };
const newTotalFiles = prev.state.totalFiles - fileKeys.length;
let newTotalBytes = prev.state.totalBytes;
let newTotalBytesUploaded = prev.state.totalBytesUploaded;
fileKeys.forEach((fileKey) => {
newTotalBytes -= newFileLoading[fileKey].total;
newTotalBytesUploaded -= newFileLoading[fileKey].loaded;
delete newFileLoading[fileKey];
});
return {
...prev,
state: {
...prev.state,
fileLoading: newFileLoading,
totalFiles: newTotalFiles,
totalBytes: newTotalBytes,
totalBytesUploaded: newTotalBytesUploaded,
},
};
});
};
const uploadProvider = UploadUtilities.createUploadProvider({
onStart: handleStartUploading,
onFinish: handleFinishUploading,
onAddedToQueue: handleAddToQueue,
onSuccess: handleSuccess,
onDuplicate: handleDuplicate,
onProgress: handleProgress,
onCancel: handleCancelUploading,
onError: handleError,
});
const resetUploadState = () => (uploadProvider.clearUploadCache(), setUploadState(DEFAULT_STATE));
return {
state: DEFAULT_STATE,
handlers: {
upload: uploadProvider.upload,
uploadLink: uploadProvider.uploadLink,
saveCopy: uploadProvider.saveCopy,
retry: uploadProvider.retry,
retryAll: uploadProvider.retryAll,
cancel: uploadProvider.cancel,
cancelAll: uploadProvider.cancelAll,
resetUploadState,
},
};
});

View File

@ -88,7 +88,8 @@
"universal-cookie": "^4.0.4",
"uuid": "^8.3.2",
"webpack": "^5.37.1",
"ws": "^7.4.3"
"ws": "^7.4.3",
"zustand": "^3.6.5"
},
"devDependencies": {
"@babel/core": "^7.15.0",