import * as React from "react";
import * as Styles from "~/common/styles";
import * as System from "~/components/system";
import * as Constants from "~/common/constants";
import * as SVG from "~/common/svg";
import * as Strings from "~/common/strings";
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 ObjectBoxPreview from "~/components/core/ObjectBoxPreview";
/* -------------------------------------------------------------------------------------------------
* Popup
* -----------------------------------------------------------------------------------------------*/
const STYLES_POPUP_WRAPPER = (theme) => css`
position: fixed;
bottom: 24px;
right: 24px;
z-index: ${theme.zindex.tooltip};
@media (max-width: ${theme.sizes.mobile}px) {
right: 50%;
transform: translateX(50%);
}
`;
const STYLES_DISMISS_BUTTON = (theme) => css`
${Styles.BUTTON_RESET};
position: absolute;
right: -8px;
top: -8px;
border-radius: 50%;
padding: 4px;
border: 1px solid ${theme.semantic.borderGrayLight4};
color: ${theme.semantic.textGrayDark};
background-color: ${theme.semantic.bgWhite};
@supports ((-webkit-backdrop-filter: blur(75px)) or (backdrop-filter: blur(75px))) {
-webkit-backdrop-filter: blur(75px);
backdrop-filter: blur(75px);
background-color: ${theme.semantic.bgBlurWhiteOP};
}
svg {
display: block;
}
`;
const STYLES_POPUP_CONTENT = css`
border-radius: 12px;
overflow: hidden;
`;
const useUploadPopup = ({ totalFilesSummary }) => {
const {
state: { isFinished },
handlers: { resetUploadState },
} = useUploadStore();
const [popupState, setPopupState] = React.useState({
isVisible: false,
isSummaryExpanded: false,
});
// NOTE(amine): popup handlers
const showUploadPopup = () => setPopupState((prev) => ({ ...prev, isVisible: true }));
const hideUploadPopup = () => setPopupState((prev) => ({ ...prev, isVisible: false }));
const expandUploadSummary = () => setPopupState({ isVisible: true, isSummaryExpanded: true });
const collapseUploadSummary = () => setPopupState({ isVisible: true, isSummaryExpanded: false });
const timeoutRef = React.useRef();
//NOTE(amine): show the upload summary, then automatically collapse the upload summary after 3 seconds
const isStarted = totalFilesSummary.total > 0;
React.useEffect(() => {
if (!isStarted) return;
expandUploadSummary();
timeoutRef.current = setTimeout(collapseUploadSummary, 3000);
}, [isStarted]);
/**
* NOTE(amine): show the upload summary when a file fails to upload or is added to the queue,
* then automatically collapse the upload summary after 3 seconds
*/
const isSummaryExpandedRef = React.useRef();
isSummaryExpandedRef.current = popupState.isSummaryExpanded;
React.useEffect(() => {
if (isSummaryExpandedRef.current || totalFilesSummary.total === 0) return;
expandUploadSummary();
clearTimeout(timeoutRef.current);
timeoutRef.current = setTimeout(collapseUploadSummary, 3000);
}, [totalFilesSummary.failed, totalFilesSummary.total]);
// NOTE(amine): show the upload summary when upload finishes
const totalFilesSummaryRef = React.useRef();
totalFilesSummaryRef.current = totalFilesSummary;
React.useEffect(() => {
clearTimeout(timeoutRef.current);
if (!isFinished) return;
//NOTE(amine): if all the upload items have been canceled, hide the upload popup
if (totalFilesSummaryRef.current.total === 0) {
hideUploadPopup();
resetUploadState();
return;
}
expandUploadSummary();
//NOTE(amine): if the upload is successful, automatically close the popup
if (totalFilesSummaryRef.current.failed === 0) {
timeoutRef.current = setTimeout(() => {
hideUploadPopup();
resetUploadState();
}, 10000);
}
}, [isFinished]);
/**
* NOTE(amine): the upload summary is set to automatically collapse when the upload starts and when a file fails to upload.
* Let's cancel those effects when the user hovers over the summary
*/
const cancelAutoCollapseOnMouseEnter = () => clearTimeout(timeoutRef.current);
return [
popupState,
{
showUploadPopup,
hideUploadPopup,
expandUploadSummary,
collapseUploadSummary,
cancelAutoCollapseOnMouseEnter,
},
];
};
const useUploadSummary = ({ fileLoading }) =>
React.useMemo(() => {
let totalFilesSummary = { failed: 0, duplicate: 0, saved: 0, total: 0 };
const uploadSummary = Object.entries(fileLoading).map(([, file]) => {
totalFilesSummary["total"]++;
if (file.status === "uploading") return { ...file, filename: file.name };
totalFilesSummary[file.status]++;
return { ...file, filename: file.name };
});
const statusOrder = {
failed: 1,
uploading: 2,
duplicate: 3,
saved: 4,
};
return {
totalFilesSummary,
uploadSummary: uploadSummary.sort(
(a, b) => statusOrder[a.status] - statusOrder[b.status] || a.createdAt - b.createdAt
),
};
}, [fileLoading]);
export function Popup() {
const {
state: { isFinished, fileLoading },
handlers: { resetUploadState },
} = useUploadStore();
const { uploadSummary, totalFilesSummary } = useUploadSummary({ fileLoading });
const [isHovered, { handleOnMouseEnter, handleOnMouseLeave }] = useHover();
const [
popupState,
{ hideUploadPopup, expandUploadSummary, collapseUploadSummary, cancelAutoCollapseOnMouseEnter },
] = useUploadPopup({ totalFilesSummary });
return (