UNPKG

@blocklet/ui-react

Version:

Some useful front-end web components that can be used in Blocklets.

126 lines (108 loc) 4.34 kB
import PropTypes from 'prop-types'; import { useCallback, useEffect, useMemo } from 'react'; import { IconButton, Badge } from '@mui/material'; import { useSnackbar } from 'notistack'; import NotificationsOutlinedIcon from '@arcblock/icons/lib/Notification'; import { useCreation } from 'ahooks'; import { EVENTS, WELLKNOWN_SERVICE_PATH_PREFIX } from '@abtnode/constant'; import useBrowser from '@arcblock/react-hooks/lib/useBrowser'; import { joinURL, withQuery } from 'ufo'; import { useListenWsClient } from './ws'; import NotificationSnackbar from '../Notifications/Snackbar'; import { compareVersions } from '../utils'; const viewAllUrl = joinURL(WELLKNOWN_SERVICE_PATH_PREFIX, 'user', 'notifications'); const getNotificationLink = (notification) => { return withQuery(viewAllUrl, { id: notification.id, severity: notification.severity || 'all', componentDid: notification.source === 'system' ? 'system' : notification.componentDid || 'all', }); }; export default function NotificationAddon({ session = {} }) { const { unReadCount, user, setUnReadCount } = session; const userDid = useCreation(() => user?.did, [user]); const { enqueueSnackbar } = useSnackbar(); const browser = useBrowser(); // 在 ArcSphere 和 Wallet 端隐藏, 消息的 toast 提示,因为有 native 的通知 const hiddenNotificationToast = useMemo(() => { return browser.arcSphere || browser.wallet; }, [browser]); const serverVersion = useCreation(() => { return window.blocklet?.serverVersion; }, []); const wsClient = useListenWsClient('user'); const listenEvent = useCreation( () => `${window.blocklet.did}/${userDid}/${EVENTS.NOTIFICATION_BLOCKLET_CREATE}`, [userDid] ); const readListenEvent = useCreation( () => `${window.blocklet.did}/${userDid}/${EVENTS.NOTIFICATION_BLOCKLET_READ}`, [userDid] ); const listenCallback = useCallback( (notification) => { const { receivers } = notification ?? {}; const { receiver: notificationReceiver } = receivers[0] ?? {}; if (notificationReceiver === userDid) { setUnReadCount((x) => x + 1); // 显示通知, 如果是系统通知则不需要显示, 如果是移动端不需要显示 // 兼容代码,如果 server 没有升级那么不需要提示 const isCompatible = compareVersions(serverVersion, '1.16.42-beta-20250407'); if (!hiddenNotificationToast && notification.source === 'component' && isCompatible) { const link = getNotificationLink(notification); const { severity, description } = notification || {}; const disableAutoHide = ['error', 'warning'].includes(severity) || notification.sticky; enqueueSnackbar(description, { variant: severity, autoHideDuration: disableAutoHide ? null : 5000, // eslint-disable-next-line react/no-unstable-nested-components content: (key) => <NotificationSnackbar viewAllUrl={link} keyId={key} notification={notification} />, }); } } }, [userDid, setUnReadCount, enqueueSnackbar, serverVersion, hiddenNotificationToast] ); const readListenCallback = useCallback( (data) => { const { receiver, readCount } = data ?? {}; if (receiver === userDid) { setUnReadCount((x) => Math.max(x - readCount, 0)); } }, [userDid, setUnReadCount] ); useEffect(() => { if (wsClient) { wsClient.on(listenEvent, listenCallback); wsClient.on(readListenEvent, readListenCallback); } return () => { if (wsClient) { wsClient.off(listenEvent, listenCallback); wsClient.off(readListenEvent, readListenCallback); } }; }, [wsClient, setUnReadCount, listenCallback, listenEvent, readListenCallback, readListenEvent]); if (!session.user || !viewAllUrl) { return null; } return ( <IconButton size="medium" variant="outlined" href={viewAllUrl} sx={{ '&:hover': { borderRadius: '50%', }, }}> <Badge badgeContent={unReadCount} color="error" invisible={unReadCount === 0}> <NotificationsOutlinedIcon style={{ width: 'auto', height: 24 }} /> </Badge> </IconButton> ); } NotificationAddon.propTypes = { session: PropTypes.object, };