Add Socket.IO event which is used for showing notifications in UI

This commit is contained in:
LASER-Yi 2021-05-09 01:58:56 +08:00
parent 08712ec5ba
commit 24cb712ad7
9 changed files with 56 additions and 46 deletions

View File

@ -7,7 +7,6 @@ import {
SITE_NEED_AUTH,
SITE_NOTIFICATIONS_ADD,
SITE_NOTIFICATIONS_REMOVE,
SITE_NOTIFICATIONS_REMOVE_BY_TIMESTAMP,
SITE_OFFLINE_UPDATE,
SITE_SIDEBAR_UPDATE,
} from "../constants";
@ -30,19 +29,14 @@ export const badgeUpdateAll = createAsyncAction(SITE_BADGE_UPDATE, () =>
BadgesApi.all()
);
export const siteAddNotification = createAction(
export const siteAddNotifications = createAction(
SITE_NOTIFICATIONS_ADD,
(err: ReduxStore.Notification) => err
(err: ReduxStore.Notification[]) => err
);
export const siteRemoveNotification = createAction(
export const siteRemoveNotifications = createAction(
SITE_NOTIFICATIONS_REMOVE,
(id: string) => id
);
export const siteRemoveNotificationByTime = createAction(
SITE_NOTIFICATIONS_REMOVE_BY_TIMESTAMP,
(date: Date) => date
(date: Date[]) => date
);
export const siteChangeSidebar = createAction(

View File

@ -36,8 +36,6 @@ 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_NOTIFICATIONS_REMOVE_BY_TIMESTAMP =
"SITE_NOTIFICATIONS_REMOVE_BY_TIMESTAMP";
export const SITE_SIDEBAR_UPDATE = "SITE_SIDEBAR_UPDATE";
export const SITE_BADGE_UPDATE = "SITE_BADGE_UPDATE";
export const SITE_OFFLINE_UPDATE = "SITE_OFFLINE_UPDATE";

View File

@ -1,27 +1,26 @@
import { useCallback, useEffect } from "react";
import { useSystemSettings } from ".";
import {
siteAddNotification,
siteAddNotifications,
siteChangeSidebar,
siteRemoveNotificationByTime,
siteRemoveNotifications,
} from "../actions";
import { useReduxAction, useReduxStore } from "./base";
export function useNotification(id: string, sec: number = 5) {
const add = useReduxAction(siteAddNotification);
const remove = useReduxAction(siteRemoveNotificationByTime);
export function useNotification(timeout: number = 5000) {
const add = useReduxAction(siteAddNotifications);
const remove = useReduxAction(siteRemoveNotifications);
return useCallback(
(msg: Omit<ReduxStore.Notification, "id" | "timestamp">) => {
(msg: Omit<ReduxStore.Notification, "timestamp">) => {
const error: ReduxStore.Notification = {
...msg,
id,
timestamp: new Date(),
};
add(error);
setTimeout(() => remove(error.timestamp), sec * 1000);
add([error]);
setTimeout(() => remove([error.timestamp]), timeout);
},
[add, remove, sec, id]
[add, remove, timeout]
);
}

View File

@ -1,3 +1,4 @@
import { differenceWith } from "lodash";
import { Action, handleActions } from "redux-actions";
import apis from "../../apis";
import {
@ -7,7 +8,6 @@ import {
SITE_NEED_AUTH,
SITE_NOTIFICATIONS_ADD,
SITE_NOTIFICATIONS_REMOVE,
SITE_NOTIFICATIONS_REMOVE_BY_TIMESTAMP,
SITE_OFFLINE_UPDATE,
SITE_SIDEBAR_UPDATE,
} from "../constants";
@ -34,21 +34,16 @@ const reducer = handleActions<ReduxStore.Site, any>(
}),
[SITE_NOTIFICATIONS_ADD]: (
state,
action: Action<ReduxStore.Notification>
action: Action<ReduxStore.Notification[]>
) => {
const alerts = [
...state.notifications.filter((v) => v.id !== action.payload.id),
const alerts = [...state.notifications, ...action.payload];
return { ...state, notifications: alerts };
},
[SITE_NOTIFICATIONS_REMOVE]: (state, action: Action<Date[]>) => {
const alerts = differenceWith(
state.notifications,
action.payload,
];
return { ...state, notifications: alerts };
},
[SITE_NOTIFICATIONS_REMOVE]: (state, action: Action<string>) => {
const alerts = state.notifications.filter((v) => v.id !== action.payload);
return { ...state, notifications: alerts };
},
[SITE_NOTIFICATIONS_REMOVE_BY_TIMESTAMP]: (state, action: Action<Date>) => {
const alerts = state.notifications.filter(
(v) => v.timestamp !== action.payload
(n, t) => n.timestamp === t
);
return { ...state, notifications: alerts };
},

View File

@ -10,7 +10,6 @@ namespace ReduxStore {
type: "error" | "warning" | "info";
message: string;
timestamp: Date;
id: string;
}
interface Site {

View File

@ -5,6 +5,8 @@ import {
movieUpdateList,
seriesDeleteItems,
seriesUpdateList,
siteAddNotifications,
siteRemoveNotifications,
siteUpdateOffline,
systemUpdateLanguagesAll,
systemUpdateSettings,
@ -29,6 +31,26 @@ export function createDefaultReducer(): SocketIO.Reducer[] {
key: "disconnect",
any: () => reduxStore.dispatch(siteUpdateOffline(true)),
},
{
key: "message",
update: (msg?: string[]) => {
if (msg) {
const notifications = msg.map<ReduxStore.Notification>((message) => ({
message,
type: "info",
timestamp: new Date(),
}));
reduxStore.dispatch(siteAddNotifications(notifications));
const ts = notifications.map((n) => n.timestamp);
setTimeout(
() => reduxStore.dispatch(siteRemoveNotifications(ts)),
5000
);
}
},
},
{
key: "series",
update: bindToReduxStore(seriesUpdateList),

View File

@ -25,7 +25,7 @@ interface Props {}
const App: FunctionComponent<Props> = () => {
const { initialized, auth } = useReduxStore((s) => s.site);
const notify = useNotification("has-update", 10);
const notify = useNotification(10 * 1000);
// Has any update?
const hasUpdate = useHasUpdateInject();

View File

@ -3,7 +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 { siteRemoveNotification } from "../../@redux/actions";
import { siteRemoveNotifications } from "../../@redux/actions";
import { useReduxAction, useReduxStore } from "../../@redux/hooks/base";
import "./style.scss";
@ -14,8 +14,11 @@ const NotificationContainer: FunctionComponent<NotificationContainerProps> = ()
const items = useMemo(
() =>
list.map((v, idx) => (
<NotificationToast key={v.id} {...v}></NotificationToast>
list.map((v) => (
<NotificationToast
key={v.timestamp.getTime()}
{...v}
></NotificationToast>
)),
[list]
);
@ -29,12 +32,12 @@ const NotificationContainer: FunctionComponent<NotificationContainerProps> = ()
type MessageHolderProps = ReduxStore.Notification & {};
const NotificationToast: FunctionComponent<MessageHolderProps> = (props) => {
const { message, id, type } = props;
const removeNotification = useReduxAction(siteRemoveNotification);
const { message, type, timestamp } = props;
const removeNotification = useReduxAction(siteRemoveNotifications);
const remove = useCallback(() => removeNotification(id), [
const remove = useCallback(() => removeNotification([timestamp]), [
removeNotification,
id,
timestamp,
]);
return (

View File

@ -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("async-overlay");
const onError = useNotification();
useEffect(() => {
if (!state.updating && state.error !== undefined && !missing) {