From 4513c4f676cc2c1683e3023b1af04715ebcdf022 Mon Sep 17 00:00:00 2001 From: LASER-Yi Date: Sun, 9 May 2021 16:41:19 +0800 Subject: [PATCH] Add progress notifications --- frontend/src/@redux/actions/site.ts | 14 ++++- frontend/src/@redux/constants/index.ts | 2 + frontend/src/@redux/reducers/site.ts | 15 ++++++ frontend/src/@redux/redux.d.ts | 8 +++ frontend/src/@types/socket.d.ts | 4 ++ frontend/src/App/notifications/index.tsx | 67 ++++++++++++++++++++---- 6 files changed, 98 insertions(+), 12 deletions(-) diff --git a/frontend/src/@redux/actions/site.ts b/frontend/src/@redux/actions/site.ts index d7ab89562..d8433aef6 100644 --- a/frontend/src/@redux/actions/site.ts +++ b/frontend/src/@redux/actions/site.ts @@ -8,6 +8,8 @@ import { SITE_NOTIFICATIONS_ADD, SITE_NOTIFICATIONS_REMOVE, SITE_OFFLINE_UPDATE, + SITE_PROGRESS_ADD, + SITE_PROGRESS_REMOVE, SITE_SIDEBAR_UPDATE, } from "../constants"; import { createAsyncAction, createCallbackAction } from "./factory"; @@ -32,7 +34,7 @@ export const badgeUpdateAll = createAsyncAction(SITE_BADGE_UPDATE, () => export const siteAddNotifications = createAction( SITE_NOTIFICATIONS_ADD, - (err: ReduxStore.Notification[]) => err + (notification: ReduxStore.Notification[]) => notification ); export const siteRemoveNotifications = createAction( @@ -40,6 +42,16 @@ export const siteRemoveNotifications = createAction( (id: string) => id ); +export const siteAddProgress = createAction( + SITE_PROGRESS_ADD, + (progress: ReduxStore.Progress[]) => progress +); + +export const siteRemoveProgress = createAction( + SITE_PROGRESS_REMOVE, + (id: string) => id +); + export const siteChangeSidebar = createAction( SITE_SIDEBAR_UPDATE, (id: string) => id diff --git a/frontend/src/@redux/constants/index.ts b/frontend/src/@redux/constants/index.ts index ae1db1f75..a76476b1b 100644 --- a/frontend/src/@redux/constants/index.ts +++ b/frontend/src/@redux/constants/index.ts @@ -36,6 +36,8 @@ export const SITE_INITIALIZED = "SITE_SYSTEM_INITIALIZED"; export const SITE_INITIALIZE_FAILED = "SITE_INITIALIZE_FAILED"; export const SITE_NOTIFICATIONS_ADD = "SITE_NOTIFICATIONS_ADD"; export const SITE_NOTIFICATIONS_REMOVE = "SITE_NOTIFICATIONS_REMOVE"; +export const SITE_PROGRESS_ADD = "SITE_PROGRESS_ADD"; +export const SITE_PROGRESS_REMOVE = "SITE_PROGRESS_REMOVE"; export const SITE_SIDEBAR_UPDATE = "SITE_SIDEBAR_UPDATE"; export const SITE_BADGE_UPDATE = "SITE_BADGE_UPDATE"; export const SITE_OFFLINE_UPDATE = "SITE_OFFLINE_UPDATE"; diff --git a/frontend/src/@redux/reducers/site.ts b/frontend/src/@redux/reducers/site.ts index 2a2d9d626..660f192a9 100644 --- a/frontend/src/@redux/reducers/site.ts +++ b/frontend/src/@redux/reducers/site.ts @@ -9,6 +9,8 @@ import { SITE_NOTIFICATIONS_ADD, SITE_NOTIFICATIONS_REMOVE, SITE_OFFLINE_UPDATE, + SITE_PROGRESS_ADD, + SITE_PROGRESS_REMOVE, SITE_SIDEBAR_UPDATE, } from "../constants"; import { AsyncAction } from "../types"; @@ -47,6 +49,18 @@ const reducer = handleActions( remove(notifications, (n) => n.id === action.payload); return { ...state, notifications }; }, + [SITE_PROGRESS_ADD]: (state, action: Action) => { + const progress = uniqBy( + [...action.payload, ...state.progress], + (n) => n.id + ); + return { ...state, progress }; + }, + [SITE_PROGRESS_REMOVE]: (state, action: Action) => { + const progress = [...state.progress]; + remove(progress, (n) => n.id === action.payload); + return { ...state, progress }; + }, [SITE_SIDEBAR_UPDATE]: (state, action: Action) => { return { ...state, @@ -70,6 +84,7 @@ const reducer = handleActions( { initialized: false, auth: true, + progress: [], notifications: [], sidebar: "", badges: { diff --git a/frontend/src/@redux/redux.d.ts b/frontend/src/@redux/redux.d.ts index e080fac43..d8eca212f 100644 --- a/frontend/src/@redux/redux.d.ts +++ b/frontend/src/@redux/redux.d.ts @@ -13,10 +13,18 @@ namespace ReduxStore { timeout: number; } + interface Progress { + id: string; + name: string; + value: number; + count: number; + } + interface Site { // Initialization state or error message initialized: boolean | string; auth: boolean; + progress: Progress[]; notifications: Notification[]; sidebar: string; badges: Badge; diff --git a/frontend/src/@types/socket.d.ts b/frontend/src/@types/socket.d.ts index 979bd5262..aac73de01 100644 --- a/frontend/src/@types/socket.d.ts +++ b/frontend/src/@types/socket.d.ts @@ -37,4 +37,8 @@ namespace SocketIO { EventType, OptionalRecord >; + + namespace CustomEvent { + type Progress = ReduxStore.Progress; + } } diff --git a/frontend/src/App/notifications/index.tsx b/frontend/src/App/notifications/index.tsx index 2484ba793..c347b2274 100644 --- a/frontend/src/App/notifications/index.tsx +++ b/frontend/src/App/notifications/index.tsx @@ -1,25 +1,35 @@ -import { faExclamationTriangle } from "@fortawesome/free-solid-svg-icons"; +import { + faExclamationTriangle, + faPaperPlane, +} from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { capitalize } from "lodash"; import React, { FunctionComponent, useCallback, useMemo } from "react"; -import { Toast } from "react-bootstrap"; +import { ProgressBar, Toast } from "react-bootstrap"; import { useTimeoutWhen } from "rooks"; -import { siteRemoveNotifications } from "../../@redux/actions"; +import { + siteRemoveNotifications, + siteRemoveProgress, +} from "../../@redux/actions"; import { useReduxAction, useReduxStore } from "../../@redux/hooks/base"; import "./style.scss"; export interface NotificationContainerProps {} const NotificationContainer: FunctionComponent = () => { - const list = useReduxStore((s) => s.site.notifications); + const { progress, notifications } = useReduxStore((s) => s.site); - const items = useMemo( - () => - list.map((v) => ( - - )), - [list] - ); + const items = useMemo(() => { + const progressItems = progress.map((v) => ( + + )); + + const notificationItems = notifications.map((v) => ( + + )); + + return [...progressItems, ...notificationItems]; + }, [notifications, progress]); return (
{items}
@@ -54,4 +64,39 @@ const NotificationToast: FunctionComponent = (props) => { ); }; +type ProgressHolderProps = ReduxStore.Progress & {}; + +const ProgressToast: FunctionComponent = ({ + id, + name, + value, + count, +}) => { + const removeProgress = useReduxAction(siteRemoveProgress); + const remove = useCallback(() => removeProgress(id), [removeProgress, id]); + + // TODO: Auto remove + + return ( + + +
+ + {name} +
+ +
+
+ ); +}; + export default NotificationContainer;