refactor: optimize the use of notification center (#3621)

This commit is contained in:
JimmFly
2023-08-09 03:00:44 +08:00
committed by GitHub
parent 73272e266b
commit 5842619206
5 changed files with 72 additions and 46 deletions

View File

@@ -177,21 +177,20 @@ export const notificationContentStyle = style({
width: '380px',
borderRadius: '8px',
boxShadow: 'var(--affine-shadow-1)',
border: '1px solid var(--affine-border-color)',
border: '1px solid var(--affine-black-10)',
background: 'var(--affine-white)',
transition: 'all 0.3s',
});
export const notificationTitleContactStyle = style({
marginRight: '22px',
width: '200px',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
overflow: 'hidden',
lineHeight: '1.5',
overflow: 'wrap',
lineHeight: '24px',
fontSize: 'var(--affine-font-base)',
});
export const notificationTitleStyle = style({
display: 'flex',
alignItems: 'center',
alignItems: 'flex-start',
width: '100%',
justifyContent: 'flex-start',
});
@@ -199,6 +198,7 @@ export const notificationDescriptionStyle = style({
fontSize: 'var(--affine-font-sm)',
color: 'var(--affine-text-secondary-color)',
marginBottom: '4px',
lineHeight: '22px',
});
export const notificationTimeStyle = style({
fontSize: 'var(--affine-font-sm)',
@@ -234,7 +234,7 @@ export const closeButtonWithMediaStyle = style({
},
});
export const closeButtonColorStyle = style({
color: 'var(--affine-white)',
color: 'var(--affine-text-primary-color)',
});
export const undoButtonStyle = style({
fontSize: 'var(--affine-font-sm)',
@@ -296,10 +296,10 @@ export const lightWarningStyle = style({
borderRadius: '8px',
});
export const darkColorStyle = style({
color: 'var(--affine-white)',
color: 'var(--affine-pure-white)',
});
export const lightInfoIconStyle = style({
color: 'var(--affine-processing-color)',
color: 'var(--affine-icon-color)',
});
export const defaultCollapseStyle = styleVariants({
secondary: {

View File

@@ -1,11 +1,12 @@
import { uuidv4 } from '@blocksuite/store';
import { atom } from 'jotai';
export type Notification = {
key: string;
key?: string;
title: string;
message: string;
message?: string;
type: 'success' | 'error' | 'warning' | 'info';
theme?: 'light' | 'dark';
theme?: 'light' | 'dark' | 'default';
timeout?: number;
progressingBar?: boolean;
multimedia?: React.ReactNode | JSX.Element | HTMLElement;
@@ -41,6 +42,7 @@ export const removeNotificationAtom = atom(null, (_, set, key: string) => {
export const pushNotificationAtom = atom<null, [Notification], void>(
null,
(_, set, newNotification) => {
newNotification.key = newNotification.key || uuidv4();
const key = newNotification.key;
const removeNotification = () =>
set(notificationsBaseAtom, notifications =>

View File

@@ -2,7 +2,7 @@
// License on the MIT
// https://github.com/emilkowalski/sonner/blob/5cb703edc108a23fd74979235c2f3c4005edd2a7/src/index.tsx
import { CloseIcon, InformationIcon } from '@blocksuite/icons';
import { CloseIcon, InformationFillDuotoneIcon } from '@blocksuite/icons';
import * as Toast from '@radix-ui/react-toast';
import { IconButton } from '@toeverything/components/button';
import clsx from 'clsx';
@@ -33,7 +33,7 @@ export {
};
type Height = {
height: number;
notificationKey: number | string;
notificationKey: number | string | undefined;
};
export type NotificationCardProps = {
notification: Notification;
@@ -46,24 +46,29 @@ const typeColorMap = {
info: {
light: styles.lightInfoStyle,
dark: styles.darkInfoStyle,
default: '',
},
success: {
light: styles.lightSuccessStyle,
dark: styles.darkSuccessStyle,
default: '',
},
warning: {
light: styles.lightWarningStyle,
dark: styles.darkWarningStyle,
default: '',
},
error: {
light: styles.lightErrorStyle,
dark: styles.darkErrorStyle,
default: '',
},
};
function NotificationCard(props: NotificationCardProps): ReactElement {
const removeNotification = useSetAtom(removeNotificationAtom);
const { notification, notifications, setHeights, heights, index } = props;
const [expand, setExpand] = useAtom(expandNotificationCenterAtom);
// const setNotificationRemoveAnimation = useSetAtom(notificationRemoveAnimationAtom);
const [mounted, setMounted] = useState<boolean>(false);
@@ -89,6 +94,7 @@ function NotificationCard(props: NotificationCardProps): ReactElement {
const duration = notification.timeout || 3000;
const offset = useRef(0);
const pointerStartYRef = useRef<number | null>(null);
const notificationsHeightBefore = useMemo(() => {
return heights.reduce((prev, curr, reducerIndex) => {
// Calculate offset up until current notification
@@ -149,7 +155,7 @@ function NotificationCard(props: NotificationCardProps): ReactElement {
}, [notification.title, notification.key, mounted, setHeights]);
const typeStyle =
typeColorMap[notification.type][notification.theme || 'light'];
typeColorMap[notification.type][notification.theme || 'dark'];
const onClickRemove = useCallback(() => {
// Save the offset for the exit swipe animation
@@ -159,6 +165,9 @@ function NotificationCard(props: NotificationCardProps): ReactElement {
h.filter(height => height.notificationKey !== notification.key)
);
setTimeout(() => {
if (!notification.key) {
return;
}
removeNotification(notification.key);
}, 200);
}, [setHeights, notification.key, removeNotification, offset]);
@@ -291,7 +300,7 @@ function NotificationCard(props: NotificationCardProps): ReactElement {
>
<div
className={clsx({
[typeStyle]: notification.theme,
[typeStyle]: notification.theme !== 'default',
[styles.hasMediaStyle]: notification.multimedia,
[styles.notificationContentStyle]: !notification.multimedia,
})}
@@ -306,16 +315,20 @@ function NotificationCard(props: NotificationCardProps): ReactElement {
) : null}
<Toast.Title
className={clsx(styles.notificationTitleStyle, {
[styles.darkColorStyle]: notification.theme === 'dark',
[styles.darkColorStyle]:
notification.theme !== 'light' &&
notification.theme !== 'default',
})}
>
<div
className={clsx(styles.notificationIconStyle, {
[styles.darkColorStyle]: notification.theme === 'dark',
[styles.lightInfoIconStyle]: notification.theme !== 'dark',
[styles.darkColorStyle]:
notification.theme !== 'light' &&
notification.theme !== 'default',
[styles.lightInfoIconStyle]: notification.theme === 'light',
})}
>
<InformationIcon />
<InformationFillDuotoneIcon />
</div>
<div className={styles.notificationTitleContactStyle}>
{notification.title}
@@ -323,7 +336,9 @@ function NotificationCard(props: NotificationCardProps): ReactElement {
{notification.undo && (
<div
className={clsx(styles.undoButtonStyle, {
[styles.darkColorStyle]: notification.theme === 'dark',
[styles.darkColorStyle]:
notification.theme !== 'light' &&
notification.theme !== 'default',
[styles.undoButtonWithMediaStyle]: notification.multimedia,
})}
onClick={onClickUndo}
@@ -338,9 +353,10 @@ function NotificationCard(props: NotificationCardProps): ReactElement {
})}
style={{
color:
notification.theme === 'dark'
? 'var(--affine-white)'
: 'var(--affine-icon-color)',
notification.theme !== 'light' &&
notification.theme !== 'default'
? 'var(--affine-pure-white)'
: 'var(--affine-text-primary-color)',
}}
>
<CloseIcon onClick={onClickRemove} />
@@ -349,7 +365,9 @@ function NotificationCard(props: NotificationCardProps): ReactElement {
</Toast.Title>
<Toast.Description
className={clsx(styles.messageStyle, {
[styles.darkColorStyle]: notification.theme === 'dark',
[styles.darkColorStyle]:
notification.theme !== 'light' &&
notification.theme !== 'default',
})}
>
{notification.message}
@@ -410,16 +428,18 @@ export function NotificationCenter(): ReactElement {
if (!notifications.length) return <></>;
return (
<Toast.Provider swipeDirection="right">
{notifications.map((notification, index) => (
<NotificationCard
notification={notification}
index={index}
key={notification.key}
notifications={notifications}
heights={heights}
setHeights={setHeights}
/>
))}
{notifications.map((notification, index) =>
notification.key ? (
<NotificationCard
notification={notification}
index={index}
key={notification.key}
notifications={notifications}
heights={heights}
setHeights={setHeights}
/>
) : null
)}
<Toast.Viewport
tabIndex={-1}
ref={listRef}

View File

@@ -9,7 +9,6 @@ import {
ExportToPdfIcon,
ExportToPngIcon,
} from '@blocksuite/icons';
import { uuidv4 } from '@blocksuite/store';
import { useSetAtom } from 'jotai';
import { useCallback } from 'react';
@@ -41,7 +40,6 @@ export const ExportToPdfMenuItem = ({
.then(() => {
onSelect?.({ type: 'pdf' });
setPushNotification({
key: uuidv4(),
title: t['com.affine.export.success.title'](),
message: t['com.affine.export.success.message'](),
type: 'success',
@@ -50,7 +48,6 @@ export const ExportToPdfMenuItem = ({
.catch(err => {
console.error(err);
setPushNotification({
key: uuidv4(),
title: t['com.affine.export.error.title'](),
message: t['com.affine.export.error.message'](),
type: 'error',
@@ -64,7 +61,6 @@ export const ExportToPdfMenuItem = ({
.then(() => {
onSelect?.({ type: 'pdf' });
setPushNotification({
key: uuidv4(),
title: t['com.affine.export.success.title'](),
message: t['com.affine.export.success.message'](),
type: 'success',
@@ -73,7 +69,6 @@ export const ExportToPdfMenuItem = ({
.catch(err => {
console.error(err);
setPushNotification({
key: uuidv4(),
title: t['com.affine.export.error.title'](),
message: t['com.affine.export.error.message'](),
type: 'error',
@@ -110,7 +105,6 @@ export const ExportToHtmlMenuItem = ({
.then(() => {
onSelect?.({ type: 'html' });
setPushNotification({
key: uuidv4(),
title: t['com.affine.export.success.title'](),
message: t['com.affine.export.success.message'](),
type: 'success',
@@ -119,7 +113,6 @@ export const ExportToHtmlMenuItem = ({
.catch(err => {
console.error(err);
setPushNotification({
key: uuidv4(),
title: t['com.affine.export.error.title'](),
message: t['com.affine.export.error.message'](),
type: 'error',
@@ -159,7 +152,6 @@ export const ExportToPngMenuItem = ({
.then(() => {
onSelect?.({ type: 'png' });
setPushNotification({
key: uuidv4(),
title: t['com.affine.export.success.title'](),
message: t['com.affine.export.success.message'](),
type: 'success',
@@ -168,7 +160,6 @@ export const ExportToPngMenuItem = ({
.catch(err => {
console.error(err);
setPushNotification({
key: uuidv4(),
title: t['com.affine.export.error.title'](),
message: t['com.affine.export.error.message'](),
type: 'error',
@@ -206,7 +197,6 @@ export const ExportToMarkdownMenuItem = ({
.then(() => {
onSelect?.({ type: 'markdown' });
setPushNotification({
key: uuidv4(),
title: t['com.affine.export.success.title'](),
message: t['com.affine.export.success.message'](),
type: 'success',
@@ -215,7 +205,6 @@ export const ExportToMarkdownMenuItem = ({
.catch(err => {
console.error(err);
setPushNotification({
key: uuidv4(),
title: t['com.affine.export.error.title'](),
message: t['com.affine.export.error.message'](),
type: 'error',