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

pull/1405/head
LASER-Yi 3 years ago
parent 08712ec5ba
commit 24cb712ad7

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

@ -36,8 +36,6 @@ export const SITE_INITIALIZED = "SITE_SYSTEM_INITIALIZED";
export const SITE_INITIALIZE_FAILED = "SITE_INITIALIZE_FAILED"; export const SITE_INITIALIZE_FAILED = "SITE_INITIALIZE_FAILED";
export const SITE_NOTIFICATIONS_ADD = "SITE_NOTIFICATIONS_ADD"; export const SITE_NOTIFICATIONS_ADD = "SITE_NOTIFICATIONS_ADD";
export const SITE_NOTIFICATIONS_REMOVE = "SITE_NOTIFICATIONS_REMOVE"; 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_SIDEBAR_UPDATE = "SITE_SIDEBAR_UPDATE";
export const SITE_BADGE_UPDATE = "SITE_BADGE_UPDATE"; export const SITE_BADGE_UPDATE = "SITE_BADGE_UPDATE";
export const SITE_OFFLINE_UPDATE = "SITE_OFFLINE_UPDATE"; export const SITE_OFFLINE_UPDATE = "SITE_OFFLINE_UPDATE";

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

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

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

@ -5,6 +5,8 @@ import {
movieUpdateList, movieUpdateList,
seriesDeleteItems, seriesDeleteItems,
seriesUpdateList, seriesUpdateList,
siteAddNotifications,
siteRemoveNotifications,
siteUpdateOffline, siteUpdateOffline,
systemUpdateLanguagesAll, systemUpdateLanguagesAll,
systemUpdateSettings, systemUpdateSettings,
@ -29,6 +31,26 @@ export function createDefaultReducer(): SocketIO.Reducer[] {
key: "disconnect", key: "disconnect",
any: () => reduxStore.dispatch(siteUpdateOffline(true)), 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", key: "series",
update: bindToReduxStore(seriesUpdateList), update: bindToReduxStore(seriesUpdateList),

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

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

@ -48,7 +48,7 @@ export function AsyncStateOverlay<T>(props: AsyncStateOverlayProps<T>) {
const { exist, state, children } = props; const { exist, state, children } = props;
const missing = exist ? !exist(state.data) : !defaultExist(state.data); const missing = exist ? !exist(state.data) : !defaultExist(state.data);
const onError = useNotification("async-overlay"); const onError = useNotification();
useEffect(() => { useEffect(() => {
if (!state.updating && state.error !== undefined && !missing) { if (!state.updating && state.error !== undefined && !missing) {

Loading…
Cancel
Save