summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorLASER-Yi <[email protected]>2021-05-09 11:22:24 +0800
committerLASER-Yi <[email protected]>2021-05-09 11:22:24 +0800
commita417f35856faf18086adbc05af43a52a45838880 (patch)
treea8d74bb2292730170fa70dafd088789ae7ac7a91
parent28ad20de889b5b5eee0c6d4bd25bf7dcb8f6ce4e (diff)
downloadbazarr-a417f35856faf18086adbc05af43a52a45838880.tar.gz
bazarr-a417f35856faf18086adbc05af43a52a45838880.zip
Rewrite notification system
-rw-r--r--frontend/src/@redux/actions/site.ts4
-rw-r--r--frontend/src/@redux/hooks/site.ts21
-rw-r--r--frontend/src/@redux/reducers/site.ts20
-rw-r--r--frontend/src/@redux/redux.d.ts3
-rw-r--r--frontend/src/@socketio/reducer.ts10
-rw-r--r--frontend/src/App/index.tsx2
-rw-r--r--frontend/src/App/notifications/index.tsx14
-rw-r--r--frontend/src/components/async.tsx2
8 files changed, 33 insertions, 43 deletions
diff --git a/frontend/src/@redux/actions/site.ts b/frontend/src/@redux/actions/site.ts
index 258d03502..d7ab89562 100644
--- a/frontend/src/@redux/actions/site.ts
+++ b/frontend/src/@redux/actions/site.ts
@@ -19,7 +19,7 @@ export const bootstrap = createCallbackAction(
() => siteInitializationFailed()
);
-// TODO: Override error message
+// TODO: Override error messages
export const siteInitializationFailed = createAction(SITE_INITIALIZE_FAILED);
const siteInitialized = createAction(SITE_INITIALIZED);
@@ -37,7 +37,7 @@ export const siteAddNotifications = createAction(
export const siteRemoveNotifications = createAction(
SITE_NOTIFICATIONS_REMOVE,
- (date: Date[]) => date
+ (id: string) => id
);
export const siteChangeSidebar = createAction(
diff --git a/frontend/src/@redux/hooks/site.ts b/frontend/src/@redux/hooks/site.ts
index 08f5b0e29..21f6034f9 100644
--- a/frontend/src/@redux/hooks/site.ts
+++ b/frontend/src/@redux/hooks/site.ts
@@ -1,26 +1,21 @@
import { useCallback, useEffect } from "react";
import { useSystemSettings } from ".";
-import {
- siteAddNotifications,
- siteChangeSidebar,
- siteRemoveNotifications,
-} from "../actions";
+import { siteAddNotifications, siteChangeSidebar } from "../actions";
import { useReduxAction, useReduxStore } from "./base";
-export function useNotification(timeout: number = 5000) {
+export function useNotification(id: string, timeout: number = 5000) {
const add = useReduxAction(siteAddNotifications);
- const remove = useReduxAction(siteRemoveNotifications);
return useCallback(
- (msg: Omit<ReduxStore.Notification, "timestamp">) => {
- const error: ReduxStore.Notification = {
+ (msg: Omit<ReduxStore.Notification, "id" | "timeout">) => {
+ const notification: ReduxStore.Notification = {
...msg,
- timestamp: new Date(),
+ id,
+ timeout,
};
- add([error]);
- setTimeout(() => remove([error.timestamp]), timeout);
+ add([notification]);
},
- [add, remove, timeout]
+ [add, timeout, id]
);
}
diff --git a/frontend/src/@redux/reducers/site.ts b/frontend/src/@redux/reducers/site.ts
index 6c8cd3917..2a2d9d626 100644
--- a/frontend/src/@redux/reducers/site.ts
+++ b/frontend/src/@redux/reducers/site.ts
@@ -1,4 +1,4 @@
-import { differenceWith } from "lodash";
+import { remove, uniqBy } from "lodash";
import { Action, handleActions } from "redux-actions";
import apis from "../../apis";
import {
@@ -36,16 +36,16 @@ const reducer = handleActions<ReduxStore.Site, any>(
state,
action: Action<ReduxStore.Notification[]>
) => {
- const alerts = [...state.notifications, ...action.payload];
- return { ...state, notifications: alerts };
- },
- [SITE_NOTIFICATIONS_REMOVE]: (state, action: Action<Date[]>) => {
- const alerts = differenceWith(
- state.notifications,
- action.payload,
- (n, t) => n.timestamp === t
+ const notifications = uniqBy(
+ [...action.payload, ...state.notifications],
+ (n) => n.id
);
- return { ...state, notifications: alerts };
+ return { ...state, notifications };
+ },
+ [SITE_NOTIFICATIONS_REMOVE]: (state, action: Action<string>) => {
+ const notifications = [...state.notifications];
+ remove(notifications, (n) => n.id === action.payload);
+ return { ...state, notifications };
},
[SITE_SIDEBAR_UPDATE]: (state, action: Action<string>) => {
return {
diff --git a/frontend/src/@redux/redux.d.ts b/frontend/src/@redux/redux.d.ts
index d48d1f221..e080fac43 100644
--- a/frontend/src/@redux/redux.d.ts
+++ b/frontend/src/@redux/redux.d.ts
@@ -8,8 +8,9 @@ interface ReduxStore {
namespace ReduxStore {
interface Notification {
type: "error" | "warning" | "info";
+ id: string;
message: string;
- timestamp: Date;
+ timeout: number;
}
interface Site {
diff --git a/frontend/src/@socketio/reducer.ts b/frontend/src/@socketio/reducer.ts
index 1454ea4a0..7d686b4d1 100644
--- a/frontend/src/@socketio/reducer.ts
+++ b/frontend/src/@socketio/reducer.ts
@@ -7,7 +7,6 @@ import {
seriesUpdateList,
siteAddNotifications,
siteInitializationFailed,
- siteRemoveNotifications,
siteUpdateOffline,
systemUpdateLanguagesAll,
systemUpdateSettings,
@@ -50,16 +49,11 @@ export function createDefaultReducer(): SocketIO.Reducer[] {
const notifications = msg.map<ReduxStore.Notification>((message) => ({
message,
type: "info",
- timestamp: new Date(),
+ id: "backend-message",
+ timeout: 5 * 1000,
}));
reduxStore.dispatch(siteAddNotifications(notifications));
-
- const ts = notifications.map((n) => n.timestamp);
- setTimeout(
- () => reduxStore.dispatch(siteRemoveNotifications(ts)),
- 5000
- );
}
},
},
diff --git a/frontend/src/App/index.tsx b/frontend/src/App/index.tsx
index 0fb016e45..995a17ebc 100644
--- a/frontend/src/App/index.tsx
+++ b/frontend/src/App/index.tsx
@@ -25,7 +25,7 @@ interface Props {}
const App: FunctionComponent<Props> = () => {
const { initialized, auth } = useReduxStore((s) => s.site);
- const notify = useNotification(10 * 1000);
+ const notify = useNotification("has-update", 10 * 1000);
// Has any update?
const hasUpdate = useHasUpdateInject();
diff --git a/frontend/src/App/notifications/index.tsx b/frontend/src/App/notifications/index.tsx
index 4fbc76399..2484ba793 100644
--- a/frontend/src/App/notifications/index.tsx
+++ b/frontend/src/App/notifications/index.tsx
@@ -3,6 +3,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { capitalize } from "lodash";
import React, { FunctionComponent, useCallback, useMemo } from "react";
import { Toast } from "react-bootstrap";
+import { useTimeoutWhen } from "rooks";
import { siteRemoveNotifications } from "../../@redux/actions";
import { useReduxAction, useReduxStore } from "../../@redux/hooks/base";
import "./style.scss";
@@ -15,10 +16,7 @@ const NotificationContainer: FunctionComponent<NotificationContainerProps> = ()
const items = useMemo(
() =>
list.map((v) => (
- <NotificationToast
- key={v.timestamp.getTime()}
- {...v}
- ></NotificationToast>
+ <NotificationToast key={v.id} {...v}></NotificationToast>
)),
[list]
);
@@ -32,14 +30,16 @@ const NotificationContainer: FunctionComponent<NotificationContainerProps> = ()
type MessageHolderProps = ReduxStore.Notification & {};
const NotificationToast: FunctionComponent<MessageHolderProps> = (props) => {
- const { message, type, timestamp } = props;
+ const { message, type, id, timeout } = props;
const removeNotification = useReduxAction(siteRemoveNotifications);
- const remove = useCallback(() => removeNotification([timestamp]), [
+ const remove = useCallback(() => removeNotification(id), [
removeNotification,
- timestamp,
+ id,
]);
+ useTimeoutWhen(remove, timeout);
+
return (
<Toast onClose={remove} animation={false}>
<Toast.Header>
diff --git a/frontend/src/components/async.tsx b/frontend/src/components/async.tsx
index c5487b534..9f5ac273c 100644
--- a/frontend/src/components/async.tsx
+++ b/frontend/src/components/async.tsx
@@ -48,7 +48,7 @@ export function AsyncStateOverlay<T>(props: AsyncStateOverlayProps<T>) {
const { exist, state, children } = props;
const missing = exist ? !exist(state.data) : !defaultExist(state.data);
- const onError = useNotification();
+ const onError = useNotification("async-loading");
useEffect(() => {
if (!state.updating && state.error !== undefined && !missing) {