Farewell, react-toast-notifications!

This commit is contained in:
Pogodaanton 2020-06-20 14:16:43 +02:00
parent 4e5b4862bf
commit 6764f1269e
15 changed files with 58 additions and 312 deletions

View File

@ -5,7 +5,7 @@ import { RouteChildrenProps } from "react-router-dom";
import React, { useRef, useState, useEffect } from "react";
import axios from "../_interceptedAxios";
import { AnimatePresence, AnimateSharedLayout } from "framer-motion";
import { useToasts, isLoggedIn } from "../_DesignSystem";
import { isLoggedIn, toast } from "../_DesignSystem";
import { useTranslation } from "react-i18next";
import { History } from "history";
@ -45,7 +45,6 @@ const isFileDataNotEmpty = (toDetermine: FileData): toDetermine is Window["fileD
*/
const useFilePrefetcher = (id: string, history: History<{}>) => {
const [fileData, setFileData] = useState<FileData>(null);
const { addToast } = useToasts();
const { t } = useTranslation("common");
useEffect(() => {
@ -61,7 +60,7 @@ const useFilePrefetcher = (id: string, history: History<{}>) => {
setFileData(res.data);
}
} catch (err) {
addToast(t("error.requestFile", { id }), { appearance: "error" });
toast.error(t("error.requestFile", { id }));
console.log(t("error.requestFile", { id }), "\n", err.message);
history.replace("/");
}
@ -75,7 +74,7 @@ const useFilePrefetcher = (id: string, history: History<{}>) => {
if (id) fetchFileData(id);
else setFileData(null);
}, [addToast, history, id, t]);
}, [history, id, t]);
return fileData;
};

View File

@ -2,7 +2,7 @@ import React, { useState, useEffect } from "react";
import { DesignSystem } from "@microsoft/fast-components-styles-msft";
import manageJss, { ComponentStyles } from "@microsoft/fast-jss-manager-react";
import { DashboardClassNameContract, DashboardProps } from "./Dashboard.props";
import { useToasts, Header } from "../_DesignSystem";
import { Header, toast } from "../_DesignSystem";
import { withDropzone } from "../FullscreenDropzone/FullscreenDropzone";
import axios from "../_interceptedAxios";
import { useTranslation } from "react-i18next";
@ -29,7 +29,6 @@ const styles: ComponentStyles<DashboardClassNameContract, DesignSystem> = {
const Dashboard: React.FC<DashboardProps> = props => {
const [listData, setListData] = useState<ListDataItem[]>(null);
const [isFrozen, setFrozenState] = useState<boolean>(false);
const { addToast } = useToasts();
const { t } = useTranslation("dashboard");
/**
@ -46,16 +45,13 @@ const Dashboard: React.FC<DashboardProps> = props => {
const res = await axios.get(window.location.origin + "/api/getAll.php");
setListData(res.data);
} catch (err) {
addToast(t(err.i18n, err.message), {
appearance: "error",
title: t("error.listGeneric") + ":",
});
toast.error(t("error.listGeneric") + ":", t(err.i18n, err.message));
console.log(`${t("error.listGeneric")}:\n`, `(${err.code}) - ${err.message}`);
}
};
updateFileList();
}, [addToast, t]);
}, [t]);
const onDeleteSelected = async (selection: string[]) => {
try {
@ -64,18 +60,12 @@ const Dashboard: React.FC<DashboardProps> = props => {
action: "delete",
});
addToast("", {
appearance: "success",
title: t("selectedDeleted", { count: selection.length }),
});
toast.success(t("selectedDeleted", { count: selection.length }));
setListData(
listData.filter(obj => selection.findIndex(id => id === obj.id) === -1)
);
} catch (err) {
addToast(t(err.i18n, err.message), {
appearance: "error",
title: t("error.requestGeneric") + ":",
});
toast.error(t("error.requestGeneric") + ":", t(err.i18n, err.message));
console.log(`${t("error.requestGeneric")}:\n`, `(${err.code}) - ${err.message}`);
}
};

View File

@ -3,7 +3,7 @@ import { FVSidebarFooterProps } from "./FVSidebarFooter.props";
import { useTranslation } from "react-i18next";
import { useHistory } from "react-router-dom";
import axios from "../../../_interceptedAxios";
import { Button, ButtonAppearance, useToasts } from "../../../_DesignSystem";
import { Button, ButtonAppearance, toast } from "../../../_DesignSystem";
import { FaTrash } from "react-icons/fa";
/**
@ -13,7 +13,6 @@ const FVSidebarDeleteButton: React.ComponentType<FVSidebarFooterProps> = ({
fileData,
}) => {
const { t } = useTranslation("dashboard");
const { addToast } = useToasts();
const history = useHistory();
const onDelete = async () => {
@ -23,17 +22,10 @@ const FVSidebarDeleteButton: React.ComponentType<FVSidebarFooterProps> = ({
action: "delete",
});
addToast("", {
appearance: "success",
title: t("selectedDeleted", { count: 1 }),
});
toast.success(t("selectedDeleted", { count: 1 }));
history.replace("/");
} catch (err) {
addToast(t(err.i18n, err.message), {
appearance: "error",
title: t("error.requestGeneric") + ":",
});
toast.error(t("error.requestGeneric") + ":", t(err.i18n, err.message));
console.log(`${t("error.requestGeneric")}:\n`, `(${err.code}) - ${err.message}`);
}
};

View File

@ -12,7 +12,7 @@ import {
} from "@microsoft/fast-components-react-msft";
import { FaCheck, FaExclamationTriangle } from "react-icons/fa";
import axios from "../../../_interceptedAxios";
import { useToasts } from "../../../_DesignSystem";
import { toast } from "../../../_DesignSystem";
import { useTranslation } from "react-i18next";
import { SidebarData } from "./FVSidebarContext";
@ -46,7 +46,6 @@ const FVSidebarDescEditor: React.ComponentType<FVSidebarDescEditorProps> = ({
managedClasses,
fileData,
}) => {
const { addToast } = useToasts();
const { t } = useTranslation("common");
const { setFileTitle, fileTitle } = useContext(SidebarData);
const [loadingState, setLoadingState] = useState<
@ -81,10 +80,7 @@ const FVSidebarDescEditor: React.ComponentType<FVSidebarDescEditorProps> = ({
} catch (err) {
clearTimeout(deferredLoading);
setLoadingState("error");
addToast(t(err.i18n, err.message), {
appearance: "error",
title: t("error.requestGeneric") + ":",
});
toast.error(t("error.requestGeneric") + ":", t(err.i18n, err.message));
console.log(`${t("error.requestGeneric")}:\n`, `(${err.code}) - ${err.message}`);
}
};

View File

@ -1,12 +1,11 @@
import React, { useState } from "react";
import { Button, ButtonAppearance, Logo, iconToGlyph } from "../_DesignSystem";
import { Button, ButtonAppearance, Logo, iconToGlyph, toast } from "../_DesignSystem";
import { DesignSystem } from "@microsoft/fast-components-styles-msft";
import manageJss, { ComponentStyles } from "@microsoft/fast-jss-manager-react";
import { LoginClassNameContract, LoginProps } from "./Login.props";
import { FaSignInAlt, FaUserAlt, FaKey } from "react-icons/fa";
import { TextAction, TextFieldType } from "@microsoft/fast-components-react-msft";
import axios from "../_interceptedAxios";
import { useToasts } from "../_DesignSystem";
import { useTranslation } from "react-i18next";
import { motion } from "framer-motion";
@ -36,7 +35,6 @@ const styles: ComponentStyles<LoginClassNameContract, DesignSystem> = {
const Login: React.FC<LoginProps> = props => {
const { t, i18n } = useTranslation("common");
const { addToast } = useToasts();
const [isDebounced, setDebounce] = useState(false);
/**
@ -56,10 +54,7 @@ const Login: React.FC<LoginProps> = props => {
} catch (err) {
setTimeout(() => {
setDebounce(false);
addToast(i18n.t(err.i18n, err.message), {
appearance: "error",
title: i18n.t("error.loginGeneric") + ":",
});
toast.error(i18n.t("error.loginGeneric") + ":", i18n.t(err.i18n, err.message));
console.error("User could not log in:\n", `(${err.code}) - ${err.message}`);
}, 1000);
}

View File

@ -1,21 +0,0 @@
import { ManagedClasses } from "@microsoft/fast-jss-manager-react";
import { ToastProps as OriginalToastProps } from "react-toast-notifications";
/**
* Class name contract for the Toast component
*/
export interface ToastClassNameContract {
toast_element: string;
toast_title: string;
toast_content: string;
}
/**
* Props for the Toast component
*/
export interface ToastProps
extends ManagedClasses<ToastClassNameContract>,
OriginalToastProps {
title?: string;
progressPercentage?: number;
}

View File

@ -1,87 +0,0 @@
import React from "react";
import { DefaultToast } from "react-toast-notifications";
import manageJss, { ComponentStyles } from "@microsoft/fast-jss-manager-react";
import {
DesignSystem,
neutralLayerFloating,
applyElevation,
ElevationMultiplier,
applyFloatingCornerRadius,
neutralForegroundRest,
applyFontWeightBold,
} from "@microsoft/fast-components-styles-msft";
import { ToastProps, ToastClassNameContract } from "./Toast.props";
import { useTranslation } from "react-i18next";
const toastStyles: ComponentStyles<ToastClassNameContract, DesignSystem> = {
toast_element: {
"& > div > div": {
alignItems: "center",
backgroundColor: (des: DesignSystem) => neutralLayerFloating(des),
...applyFloatingCornerRadius(),
...applyElevation(ElevationMultiplier.e9),
"& > .react-toast-notifications__toast__icon-wrapper": {
"border-top-left-radius": "0",
"border-bottom-left-radius": "0",
"padding-left": "5px",
"padding-right": "5px",
"& > .react-toast-notifications__toast__countdown": {
backgroundColor: "rgba(0,0,0,0.12)",
},
},
"& > .react-toast-notifications__toast__content": {
display: "flex",
flexDirection: "column",
justifyContent: "center",
lineHeight: "1.2",
},
"& > .react-toast-notifications__toast__dismiss-button": {
color: (des: DesignSystem) => neutralForegroundRest(des),
},
},
},
toast_title: {
color: (des: DesignSystem) => neutralForegroundRest(des),
...applyFontWeightBold(),
},
toast_content: {
color: (des: DesignSystem) => neutralForegroundRest(des),
},
};
const BaseToast = (props: ToastProps) => {
const { t } = useTranslation("common");
let defaultToastProps = { ...props };
delete defaultToastProps.managedClasses;
delete defaultToastProps.title;
defaultToastProps.appearance = props.appearance || "info";
const title = () => {
switch (props.appearance) {
case "error":
return t("notification.error");
case "warning":
return t("notification.warning");
default:
return t("notification.info");
}
};
return (
<div className={props.managedClasses.toast_element}>
<DefaultToast {...defaultToastProps}>
<span className={props.managedClasses.toast_title}>{props.title || title()}</span>
<span
className={props.managedClasses.toast_content}
data-content={typeof props.children === "string" ? props.children : ""}
>
{props.children}
</span>
</DefaultToast>
</div>
);
};
export default manageJss(toastStyles)(BaseToast);

View File

@ -1,16 +0,0 @@
import { ManagedClasses } from "@microsoft/fast-jss-manager-react";
import { ToastContainerProps as OriginalToastContainerProps } from "react-toast-notifications";
/**
* Class name contract for the ToastContainer component
*/
export interface ToastContainerClassNameContract {
toast_container: string;
}
/**
* Props for the ToastContainer component
*/
export interface ToastContainerProps
extends ManagedClasses<ToastContainerClassNameContract>,
OriginalToastContainerProps {}

View File

@ -1,35 +0,0 @@
import React from "react";
import { DefaultToastContainer } from "react-toast-notifications";
import manageJss, { ComponentStyles } from "@microsoft/fast-jss-manager-react";
import { DesignSystem } from "@microsoft/fast-components-styles-msft";
import {
ToastContainerProps,
ToastContainerClassNameContract,
} from "./ToastContainer.props";
const toastContainerStyles: ComponentStyles<
ToastContainerClassNameContract,
DesignSystem
> = {
toast_container: {
"& > div": {
overflowY: "visible",
overflowX: "visible",
},
},
};
const BaseToastContainer = (props: ToastContainerProps) => {
let defaultToastContainerProps = { ...props };
delete defaultToastContainerProps.managedClasses;
return (
<div className={props.managedClasses.toast_container}>
<DefaultToastContainer {...defaultToastContainerProps}>
{props.children}
</DefaultToastContainer>
</div>
);
};
export default manageJss(toastContainerStyles)(BaseToastContainer);

View File

@ -1,41 +0,0 @@
import {
useToasts as originalUseToasts,
RemoveToast,
RemoveAllToasts,
AppearanceTypes,
UpdateToast,
Options,
} from "react-toast-notifications";
import { ReactNode } from "react";
/**
* Since a custom "title" prop is available in the toast,
* The type definition needs to be updated.
*
* @interface CustomOptions
* @extends {Options}
*/
interface CustomOptions extends Options {
title?: string;
}
type AddToast = (
content: ReactNode,
options?: CustomOptions,
callback?: (id: string) => void
) => void;
type UseToasts = () => {
addToast: AddToast;
removeToast: RemoveToast;
removeAllToasts: RemoveAllToasts;
toastStack: Array<{
content: ReactNode;
id: string;
appearance: AppearanceTypes;
}>;
updateToast: UpdateToast;
};
const useToasts: UseToasts = originalUseToasts;
export default useToasts;

View File

@ -12,7 +12,6 @@ import {
applyBackdropBackground,
} from "./Utils/stylesheetModifiers";
import { iconToGlyph } from "./Utils/iconToGlyph";
import useToasts from "./Toast/useToasts";
import ProgressIcon from "./ProgressIcon/ProgressIcon";
import useMotionValueFactory from "./Hooks/useMotionValueFactory";
import { useScaleFactor } from "./Hooks/useScaleFactor";
@ -25,7 +24,6 @@ export {
Logo,
ButtonAppearance,
iconToGlyph,
useToasts,
applyCenteredFlexbox,
ProgressIcon,
applyBackdropBackground,

View File

@ -1,8 +1,7 @@
import React, { useEffect, useCallback, useRef, useState } from "react";
import { useToasts } from "../../_DesignSystem";
import { toast } from "../../_DesignSystem";
import gifJS from "gif.js";
import axios from "../../_interceptedAxios";
import { Progress } from "@microsoft/fast-components-react-msft";
/**
* gif.js can use a faster quantization through WASM
@ -19,13 +18,9 @@ const chunkSize = 1024 * 1024;
*/
const desiredFramerate = 8;
/**
* Percentage to compare current percentage to
*/
let lastText = "";
/**
* Gif.js instance
* NOTE: Worker scripts are not automatically terminated!
*/
const gif = new gifJS({
workerScript:
@ -43,8 +38,6 @@ const gif = new gifJS({
});
const VideoGifGenerator: React.ComponentType<{}> = props => {
const { addToast, updateToast, removeToast } = useToasts();
/**
* An array containing video IDs that need GIF equivalents.
*/
@ -86,7 +79,6 @@ const VideoGifGenerator: React.ComponentType<{}> = props => {
*/
const seekVideo = (time: number, seekSummand: number) => {
const seekHandler = () => {
console.log("Loaded Data at " + videoEl.currentTime + "!");
videoEl.removeEventListener("seeked", seekHandler);
// Setting progress for toast
@ -94,7 +86,6 @@ const VideoGifGenerator: React.ComponentType<{}> = props => {
// Start GIF rendering if there is no frame to add anymore
if (videoEl.currentTime >= videoEl.duration) {
console.log("Finished at " + videoEl.currentTime + ", now rendering!");
gif.render();
document.body.removeChild(videoEl);
return;
@ -109,7 +100,6 @@ const VideoGifGenerator: React.ComponentType<{}> = props => {
};
videoEl.addEventListener("loadedmetadata", () => {
console.log("Loaded Metadata!", videoEl.duration);
const seekSummand = 1 / desiredFramerate;
gif.abort();
@ -147,6 +137,8 @@ const VideoGifGenerator: React.ComponentType<{}> = props => {
*/
const uploadGif = useCallback(
(gifBlob: Blob) => {
const maxChunkAmount = gifBlob.size / chunkSize;
const uploadGifChunk = async (chunkNum: number) => {
const curID = taskList.current[0].id;
const postData = new FormData();
@ -169,8 +161,24 @@ const VideoGifGenerator: React.ComponentType<{}> = props => {
postData.append("data", !isStitchRequest ? blobChunk : null);
try {
await axios.post(window.location.origin + "/api/finishAdminTask.php", postData);
await axios.post(
window.location.origin + "/api/finishAdminTask.php",
postData,
{
onUploadProgress: p => {
const uploadPercent = (p.loaded * 100) / p.total;
const chunkCompletePercent = 34 / maxChunkAmount;
const progress =
(uploadPercent * chunkCompletePercent) / 100 +
66 +
chunkCompletePercent * chunkNum;
setProgress(progress);
},
}
);
if (isStitchRequest)
console.log("Wow, we're at chunk " + chunkNum + " / " + maxChunkAmount);
if (!isStitchRequest) uploadGifChunk(chunkNum + 1);
else shiftToNextGif();
} catch (err) {
@ -180,14 +188,14 @@ const VideoGifGenerator: React.ComponentType<{}> = props => {
return;
}
addToast("error.gifUpload", { appearance: "error" });
toast.error("error.gifUpload");
console.log("error.gifUpload", "\n", err.message, err, typeof err);
}
};
uploadGifChunk(0);
},
[addToast, shiftToNextGif]
[shiftToNextGif]
);
useEffect(() => {
@ -197,17 +205,18 @@ const VideoGifGenerator: React.ComponentType<{}> = props => {
params: { type: "video-gif" },
});
if (res.data) {
if (typeof res.data.length !== "undefined" && res.data.length > 0) {
console.log(res.data);
taskList.current = res.data;
generateGif(taskList.current[0].id);
}
} catch (err) {
addToast("error.requestTaskList", { appearance: "error" });
toast.error("error.requestTaskList");
console.log("error.requestTaskList", "\n", err.message);
}
})();
return () => {};
}, [addToast, generateGif]);
}, [generateGif]);
useEffect(() => {
const gifFinishHandler = uploadGif;
@ -225,24 +234,15 @@ const VideoGifGenerator: React.ComponentType<{}> = props => {
useEffect(() => {
let toastId: string = null;
addToast(
<Progress minValue={0} maxValue={100} />,
{
title: `Spinning up GIF generator...`,
appearance: "info",
autoDismiss: false,
},
(id: string) => {
setToastId(id);
toastId = id;
}
);
toastId =
toast("Spinning up GIF generator...", "", { progress: 0, type: "info" }) + "";
setToastId(toastId);
return () => {
if (toastId) removeToast(toastId);
if (toastId) toast.dismiss(toastId);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [removeToast, addToast]);
}, []);
useEffect(() => {
if (!toastId || !curFileId) return;
@ -253,13 +253,10 @@ const VideoGifGenerator: React.ComponentType<{}> = props => {
if (progressPercentage >= 33) title = `Generating ${curFileId}.gif...`;
if (progressPercentage >= 66) title = `Uploading ${curFileId}.gif...`;
if (title !== lastText) {
lastText = title;
updateToast(toastId, { title } as any);
}
toast.update(toastId, { progress: progressPercentage, title });
console.log(`${title} - ${progressPercentage}%`);
}, [curFileId, progressPercentage, toastId, updateToast]);
}, [curFileId, progressPercentage, toastId]);
return null;
};

View File

@ -1,7 +1,7 @@
import React, { useEffect } from "react";
// eslint-disable-next-line import/no-webpack-loader-syntax
import VideoWorker from "worker-loader!./video.worker.ts";
import { useToasts } from "react-toast-notifications";
import { toast } from "../../_DesignSystem";
let worker: VideoWorker = null;
@ -15,8 +15,6 @@ let worker: VideoWorker = null;
* fulness of a worker for a job like this.
*/
const VideoThumbnailGenerator: React.ComponentType<{}> = props => {
const { addToast } = useToasts();
useEffect(() => {
if (typeof Worker === "undefined") return;
if (!worker) worker = new VideoWorker();
@ -69,7 +67,7 @@ const VideoThumbnailGenerator: React.ComponentType<{}> = props => {
if (typeof data === "object" && "task" in data && "arguments" in data) {
switch (data.task) {
case "addToast":
addToast(data.arguments[0], data.arguments[1]);
toast(data.arguments[0], data.arguments[1], data.arguments[3]);
break;
case "getFirstFrame":
if (typeof data.arguments === "string") {
@ -93,7 +91,7 @@ const VideoThumbnailGenerator: React.ComponentType<{}> = props => {
if (worker.addEventListener) worker.removeEventListener("message", taskHandler);
else worker.onmessage = null;
};
}, [addToast]);
}, []);
useEffect(() => {
worker.postMessage("fetchList");

View File

@ -1,14 +1,14 @@
/* eslint-disable no-restricted-globals */
import axios from "../../_interceptedAxios";
import { AddToast } from "react-toast-notifications";
import { ToastContent, ToastOptions } from "react-toastify";
const ctx: Worker = self as any;
let IDList: { id: string }[] = [];
const addToast: AddToast = (content, options) => {
const addToast = (content: ToastContent, title: ToastContent, options: ToastOptions) => {
ctx.postMessage({
task: "addToast",
arguments: [content, options],
arguments: [content, title, options],
});
};
@ -44,7 +44,7 @@ ctx.addEventListener("message", ({ data }) => {
console.log("Web Worker does not have anything to do.");
}
} catch (err) {
addToast("error.requestTaskList", { appearance: "error" });
addToast("error.requestTaskList", "", { type: "error" });
console.log("error.requestTaskList", "\n", err.message);
}
})();
@ -68,7 +68,7 @@ ctx.addEventListener("message", ({ data }) => {
IDList.shift();
generateNextThumbnail();
} catch (err) {
addToast("error.requestTaskList", { appearance: "error" });
addToast("error.requestTaskList", "", { type: "error" });
console.log("error.requestTaskList", "\n", err.message);
}
})();

View File

@ -4,29 +4,10 @@ import App from "./App/App";
import "./index.scss";
import "./i18n";
import PogodaDesignToolkitProvider from "./_DesignSystem/Toolkit/DesignSystem";
import { ToastProvider } from "react-toast-notifications";
import Toast from "./_DesignSystem/Toast/StaticToast/Toast";
import ToastContainer from "./_DesignSystem/Toast/ToastContainer/ToastContainer";
// import loadable from "@loadable/component";
//const Toast = loadable(() =>
// import(/* webpackChunkName: "Toast" */ "./_DesignSystem/Toast/StaticToast/Toast")
//);
//const ToastContainer = loadable(() =>
// import(
// /* webpackChunkName: "Toast" */ "./_DesignSystem/Toast/ToastContainer/ToastContainer"
// )
//);*/
ReactDOM.render(
<PogodaDesignToolkitProvider>
<ToastProvider
autoDismiss={true}
autoDismissTimeout={4500}
components={{ Toast, ToastContainer }}
>
<App />
</ToastProvider>
<App />
</PogodaDesignToolkitProvider>,
document.getElementById("root")
);