From 27ff9ab9f42c7b0d9a776ec4e68e270efb016677 Mon Sep 17 00:00:00 2001 From: L-Sun Date: Wed, 23 Apr 2025 05:41:14 +0000 Subject: [PATCH] refactor(core): move actions to footer of notification card (#11894) This PR move all actions button to the footer of `NotificationCard`. There are some example as following: ### No Changes ![CleanShot 2025-04-22 at 20.02.58@2x.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/MyRfgiN4RuBxJfrza3SG/1207cbfb-6033-48f4-8a7b-b7b024f16881.png) ![CleanShot 2025-04-22 at 20.03.42@2x.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/MyRfgiN4RuBxJfrza3SG/6da27303-06c3-4f40-b3f9-3cf88759f685.png) ![CleanShot 2025-04-22 at 20.03.57@2x.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/MyRfgiN4RuBxJfrza3SG/a53d2bdf-a103-4275-9833-a55387af6f09.png) ### Changes ### Before ![CleanShot 2025-04-22 at 20.07.40@2x.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/MyRfgiN4RuBxJfrza3SG/d785b12b-480c-4b9d-bb0b-d93dd18939a2.png) #### After ![CleanShot 2025-04-22 at 20.05.51@2x.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/MyRfgiN4RuBxJfrza3SG/242a9956-6a68-46a7-865a-454117ee997c.png) --- .../embed/src/common/render-linked-doc.ts | 15 ++-- .../src/components/view-in-page-notify.css.ts | 24 ------ .../affine/blocks/note/src/configs/toolbar.ts | 29 +++---- .../components/src/notification/linked-doc.ts | 15 ++-- .../src/services/notification-service.ts | 6 +- .../desktop/notification-card.tsx | 61 ++++++++------ .../src/ui/notification/desktop/styles.css.ts | 6 +- .../notification/mobile/notification-card.tsx | 42 ++++++---- .../notification-center.stories.tsx | 37 ++++----- .../component/src/ui/notification/types.ts | 11 ++- .../editor-config/toolbar/copy-as-image.ts | 13 +-- .../extensions/notification-service.tsx | 23 ++++-- .../ai-onboarding/edgeless.dialog.css.ts | 4 - .../affine/ai-onboarding/edgeless.dialog.tsx | 76 +++++++++--------- .../affine/ai-onboarding/local.dialog.tsx | 75 +++++++----------- .../affine/subscription-landing/notify.css.ts | 7 -- .../affine/subscription-landing/notify.tsx | 79 ++++++------------- .../src/components/over-capacity/index.tsx | 11 ++- .../setting/general-setting/backup/index.tsx | 17 ++-- .../modules/at-menu-config/services/index.ts | 78 +++++++++--------- .../e2e/blocksuite/edgeless/note.spec.ts | 14 ++-- 21 files changed, 304 insertions(+), 339 deletions(-) delete mode 100644 blocksuite/affine/blocks/note/src/components/view-in-page-notify.css.ts diff --git a/blocksuite/affine/blocks/embed/src/common/render-linked-doc.ts b/blocksuite/affine/blocks/embed/src/common/render-linked-doc.ts index 3601db8c52..6a2acdd8af 100644 --- a/blocksuite/affine/blocks/embed/src/common/render-linked-doc.ts +++ b/blocksuite/affine/blocks/embed/src/common/render-linked-doc.ts @@ -364,13 +364,16 @@ export function notifyDocCreated(std: BlockStdScope, doc: Store) { message: 'You can click undo to recovery block content', accent: 'info', duration: 10 * 1000, - action: { - label: 'Undo', - onClick: () => { - doc.undo(); - clear(); + actions: [ + { + key: 'undo', + label: 'Undo', + onClick: () => { + doc.undo(); + clear(); + }, }, - }, + ], abort: abortController.signal, onClose: clear, }); diff --git a/blocksuite/affine/blocks/note/src/components/view-in-page-notify.css.ts b/blocksuite/affine/blocks/note/src/components/view-in-page-notify.css.ts deleted file mode 100644 index 409eefeac2..0000000000 --- a/blocksuite/affine/blocks/note/src/components/view-in-page-notify.css.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { cssVar } from '@toeverything/theme'; -import { cssVarV2 } from '@toeverything/theme/v2'; -import { style } from '@vanilla-extract/css'; - -export const viewInPageNotifyFooter = style({ - display: 'flex', - justifyContent: 'flex-end', - gap: '12px', -}); - -export const viewInPageNotifyFooterButton = style({ - padding: '0px 6px', - borderRadius: '4px', - color: cssVarV2('text/primary'), - - fontSize: cssVar('fontSm'), - lineHeight: '22px', - fontWeight: '500', - textAlign: 'center', - - ':hover': { - background: cssVarV2('layer/background/hoverOverlay'), - }, -}); diff --git a/blocksuite/affine/blocks/note/src/configs/toolbar.ts b/blocksuite/affine/blocks/note/src/configs/toolbar.ts index dc13d2cefa..d8a0efbcf9 100644 --- a/blocksuite/affine/blocks/note/src/configs/toolbar.ts +++ b/blocksuite/affine/blocks/note/src/configs/toolbar.ts @@ -41,7 +41,6 @@ import { html } from 'lit'; import { keyed } from 'lit/directives/keyed.js'; import { changeNoteDisplayMode } from '../commands'; -import * as styles from '../components/view-in-page-notify.css'; import { NoteConfigExtension } from '../config'; const trackBaseProps = { @@ -550,22 +549,18 @@ function setDisplayMode( message: `${data.message} Find it in the TOC for quick navigation.`, accent: 'success', duration: 5 * 1000, - footer: html`
- - -
`, + actions: [ + { + key: 'undo-display-in-page', + label: 'Undo', + onClick: () => undo(), + }, + { + key: 'view-in-toc', + label: 'View in Toc', + onClick: () => viewInToc(), + }, + ], abort: abortController.signal, onClose: () => { clear(); diff --git a/blocksuite/affine/components/src/notification/linked-doc.ts b/blocksuite/affine/components/src/notification/linked-doc.ts index 3d72fb0dbf..fc74b9a18c 100644 --- a/blocksuite/affine/components/src/notification/linked-doc.ts +++ b/blocksuite/affine/components/src/notification/linked-doc.ts @@ -35,13 +35,16 @@ function notify(std: BlockStdScope, title: string, message: string) { message, accent: 'info', duration: 10 * 1000, - action: { - label: 'Undo', - onClick: () => { - doc.undo(); - clear(); + actions: [ + { + key: 'undo', + label: 'Undo', + onClick: () => { + doc.undo(); + clear(); + }, }, - }, + ], abort: abortController.signal, onClose: clear, }); diff --git a/blocksuite/affine/shared/src/services/notification-service.ts b/blocksuite/affine/shared/src/services/notification-service.ts index 5ac1c8bcb0..9b8dc2fabb 100644 --- a/blocksuite/affine/shared/src/services/notification-service.ts +++ b/blocksuite/affine/shared/src/services/notification-service.ts @@ -29,14 +29,14 @@ export interface NotificationService { notify(options: { title: string | TemplateResult; message?: string | TemplateResult; - footer?: string | TemplateResult; accent?: 'info' | 'success' | 'warning' | 'error'; duration?: number; // unit ms, give 0 to disable auto dismiss abort?: AbortSignal; - action?: { + actions?: { + key: string; label: string | TemplateResult; onClick: () => void; - }; + }[]; onClose: () => void; }): void; } diff --git a/packages/frontend/component/src/ui/notification/desktop/notification-card.tsx b/packages/frontend/component/src/ui/notification/desktop/notification-card.tsx index 2ab268dd54..f44888a7d1 100644 --- a/packages/frontend/component/src/ui/notification/desktop/notification-card.tsx +++ b/packages/frontend/component/src/ui/notification/desktop/notification-card.tsx @@ -4,7 +4,8 @@ import clsx from 'clsx'; import { useCallback } from 'react'; import { Button, IconButton } from '../../button'; -import type { NotificationCardProps } from '../types'; +import { FlexWrapper } from '../../layout/wrapper'; +import type { NotificationActionProps, NotificationCardProps } from '../types'; import { getCardVars } from '../utils'; import * as styles from './styles.css'; @@ -18,10 +19,9 @@ export const DesktopNotificationCard = ({ icon = , iconColor, thumb, - action, + actions, error, title, - footer, alignMessage = 'title', onDismiss, rootAttrs, @@ -33,13 +33,6 @@ export const DesktopNotificationCard = ({ ? t[errorI18nKey](error?.data) : undefined; - const onActionClicked = useCallback(() => { - action?.onClick()?.catch(console.error); - if (action?.autoClose !== false) { - onDismiss?.(); - } - }, [action, onDismiss]); - return (
) : null}
{title || errorTitle}
- - {action ? ( -
- -
- ) : null}
{notification.message} -
{footer}
+
+ + {actions?.map(action => ( + + ))} + +
); }; + +const NotificationCardAction = ({ + action, + onDismiss, +}: NotificationActionProps) => { + const onActionClicked = useCallback(() => { + action.onClick()?.catch(console.error); + if (action.autoClose !== false) { + onDismiss?.(); + } + }, [action, onDismiss]); + + return ( + + ); +}; diff --git a/packages/frontend/component/src/ui/notification/desktop/styles.css.ts b/packages/frontend/component/src/ui/notification/desktop/styles.css.ts index 05c39a69de..bc0f278ab4 100644 --- a/packages/frontend/component/src/ui/notification/desktop/styles.css.ts +++ b/packages/frontend/component/src/ui/notification/desktop/styles.css.ts @@ -55,14 +55,14 @@ export const title = style({ fontSize: 15, marginRight: 10, }); -export const action = style({ - marginRight: 16, -}); + export const actionButton = style({ color: actionTextColor, position: 'relative', background: 'transparent', border: 'none', + fontSize: cssVar('fontSm'), + lineHeight: '22px', }); export const closeButton = style({ selectors: { diff --git a/packages/frontend/component/src/ui/notification/mobile/notification-card.tsx b/packages/frontend/component/src/ui/notification/mobile/notification-card.tsx index d520ad4c69..f3efe0b9f8 100644 --- a/packages/frontend/component/src/ui/notification/mobile/notification-card.tsx +++ b/packages/frontend/component/src/ui/notification/mobile/notification-card.tsx @@ -4,7 +4,7 @@ import { useCallback, useState } from 'react'; import { Button, IconButton } from '../../button'; import { Modal } from '../../modal'; -import type { NotificationCardProps } from '../types'; +import type { NotificationActionProps, NotificationCardProps } from '../types'; import { getCardVars } from '../utils'; import * as styles from './styles.css'; @@ -70,8 +70,7 @@ const MobileNotifyDetail = ({ iconColor, title, message, - footer, - action, + actions, error, } = notification; const t = useI18n(); @@ -87,12 +86,6 @@ const MobileNotifyDetail = ({ }, [onClose] ); - const onActionClicked = useCallback(() => { - action?.onClick()?.catch(console.error); - if (action?.autoClose !== false) { - onClose?.(); - } - }, [action, onClose]); return ( {message} {/* actions */}
- {action ? ( - - ) : null} - {footer} + {actions?.map(action => ( + + ))}
); }; + +const NotificationCardAction = ({ + action, + onDismiss, +}: NotificationActionProps) => { + const onActionClicked = useCallback(() => { + action.onClick()?.catch(console.error); + if (action.autoClose !== false) { + onDismiss?.(); + } + }, [action, onDismiss]); + + return ( + + ); +}; diff --git a/packages/frontend/component/src/ui/notification/notification-center.stories.tsx b/packages/frontend/component/src/ui/notification/notification-center.stories.tsx index 685e8940e8..2d04f406c1 100644 --- a/packages/frontend/component/src/ui/notification/notification-center.stories.tsx +++ b/packages/frontend/component/src/ui/notification/notification-center.stories.tsx @@ -181,10 +181,13 @@ export const WithAction: StoryFn = () => { ), style, theme, - action: { - label: 'UNDO', - onClick: () => console.log('undo'), - }, + actions: [ + { + key: 'undo', + label: 'UNDO', + onClick: () => console.log('undo'), + }, + ], }) } > @@ -204,11 +207,14 @@ export const WithAction: StoryFn = () => { { title: 'Disable auto close', message: 'Test with disable auto close', - action: { - label: 'UNDO', - onClick: () => console.log('undo'), - autoClose: false, - }, + actions: [ + { + key: 'undo', + label: 'UNDO', + onClick: () => console.log('undo'), + autoClose: false, + }, + ], }, { duration: 22222222 } ); @@ -296,25 +302,12 @@ export const DifferentSize: StoryFn = () => { { duration: 60000 } ); }; - const openWithFooter = () => { - notify( - { - title: 'With footer', - message: 'With basic title and one line message', - footer: ( - - ), - }, - { duration: 60000 } - ); - }; return ( - ); }; diff --git a/packages/frontend/component/src/ui/notification/types.ts b/packages/frontend/component/src/ui/notification/types.ts index eced811437..828a03b5f3 100644 --- a/packages/frontend/component/src/ui/notification/types.ts +++ b/packages/frontend/component/src/ui/notification/types.ts @@ -14,7 +14,8 @@ export interface Notification { background?: string; foreground?: string; alignMessage?: 'title' | 'icon'; - action?: { + actions?: { + key: string; label: ReactNode; onClick: (() => void) | (() => Promise); buttonProps?: ButtonProps; @@ -22,7 +23,7 @@ export interface Notification { * @default true */ autoClose?: boolean; - }; + }[]; rootAttrs?: HTMLAttributes; @@ -33,7 +34,6 @@ export interface Notification { error?: UserFriendlyError; icon?: ReactNode; iconColor?: string; - footer?: ReactNode; // events onDismiss?: () => void; @@ -50,3 +50,8 @@ export interface NotificationCustomRendererProps { export interface NotificationCardProps extends HTMLAttributes { notification: Notification; } + +export interface NotificationActionProps { + action: NonNullable[number]; + onDismiss: Notification['onDismiss']; +} diff --git a/packages/frontend/core/src/blocksuite/extensions/editor-config/toolbar/copy-as-image.ts b/packages/frontend/core/src/blocksuite/extensions/editor-config/toolbar/copy-as-image.ts index e192d8768d..8c74276f9d 100644 --- a/packages/frontend/core/src/blocksuite/extensions/editor-config/toolbar/copy-as-image.ts +++ b/packages/frontend/core/src/blocksuite/extensions/editor-config/toolbar/copy-as-image.ts @@ -109,12 +109,15 @@ export function copyAsImage(std: BlockStdScope) { notify.error({ title: I18n.t('com.affine.copy.asImage.notAvailable.title'), message: I18n.t('com.affine.copy.asImage.notAvailable.message'), - action: { - label: I18n.t('com.affine.copy.asImage.notAvailable.action'), - onClick: () => { - window.open('https://affine.pro/download'); + actions: [ + { + key: 'download', + label: I18n.t('com.affine.copy.asImage.notAvailable.action'), + onClick: () => { + window.open('https://affine.pro/download'); + }, }, - }, + ], }); return; } diff --git a/packages/frontend/core/src/blocksuite/extensions/notification-service.tsx b/packages/frontend/core/src/blocksuite/extensions/notification-service.tsx index bd6b9c2899..e552c9d6a7 100644 --- a/packages/frontend/core/src/blocksuite/extensions/notification-service.tsx +++ b/packages/frontend/core/src/blocksuite/extensions/notification-service.tsx @@ -1,5 +1,6 @@ import { Input, + type Notification, notify, toast, type ToastOptions, @@ -96,17 +97,25 @@ export function patchNotificationService({ throw new Error('Invalid notification accent'); } + const toAffineNotificationActions = ( + actions: (typeof notification)['actions'] + ): Notification['actions'] => { + if (!actions) return undefined; + + return actions.map(({ label, onClick, key }) => { + return { + key, + label: toReactNode(label), + onClick, + }; + }); + }; + const toastId = fn( { title: toReactNode(notification.title), message: toReactNode(notification.message), - footer: toReactNode(notification.footer), - action: notification.action?.onClick - ? { - label: toReactNode(notification.action?.label), - onClick: notification.action.onClick, - } - : undefined, + actions: toAffineNotificationActions(notification.actions), onDismiss: notification.onClose, }, { diff --git a/packages/frontend/core/src/components/affine/ai-onboarding/edgeless.dialog.css.ts b/packages/frontend/core/src/components/affine/ai-onboarding/edgeless.dialog.css.ts index 99b33d8b98..379433190c 100644 --- a/packages/frontend/core/src/components/affine/ai-onboarding/edgeless.dialog.css.ts +++ b/packages/frontend/core/src/components/affine/ai-onboarding/edgeless.dialog.css.ts @@ -20,10 +20,6 @@ export const thumbContent = style({ height: 'calc(100% + 4px)', }); -export const actionButton = style({ - fontSize: cssVar('fontSm'), - lineHeight: '22px', -}); export const getStartedButtonText = style({ color: cssVar('textSecondaryColor'), }); diff --git a/packages/frontend/core/src/components/affine/ai-onboarding/edgeless.dialog.tsx b/packages/frontend/core/src/components/affine/ai-onboarding/edgeless.dialog.tsx index b91595a812..67a479ae2d 100644 --- a/packages/frontend/core/src/components/affine/ai-onboarding/edgeless.dialog.tsx +++ b/packages/frontend/core/src/components/affine/ai-onboarding/edgeless.dialog.tsx @@ -1,4 +1,5 @@ -import { Button, FlexWrapper, notify } from '@affine/component'; +import { notify } from '@affine/component'; +import { type Notification } from '@affine/component/ui/notification'; import { SubscriptionService } from '@affine/core/modules/cloud'; import { WorkspaceDialogService } from '@affine/core/modules/dialogs'; import { EditorService } from '@affine/core/modules/editor'; @@ -63,6 +64,38 @@ export const AIOnboardingEdgeless = () => { }); }, [workspaceDialogService]); + const actions = useMemo(() => { + const result: NonNullable = [ + { + key: 'get-started', + label: ( + + {t['com.affine.ai-onboarding.edgeless.get-started']()} + + ), + onClick: () => { + toggleEdgelessAIOnboarding(false); + }, + }, + ]; + + if (!aiSubscription) { + result.push({ + key: 'purchase', + label: ( + + {t['com.affine.ai-onboarding.edgeless.purchase']()} + + ), + onClick: () => { + goToPricingPlans(); + toggleEdgelessAIOnboarding(false); + }, + }); + } + return result; + }, [aiSubscription, goToPricingPlans, t]); + useEffect(() => { if (generalAIOnboardingOpened) return; if (notifyId) return; @@ -83,50 +116,13 @@ export const AIOnboardingEdgeless = () => { thumb: , alignMessage: 'icon', onDismiss: () => toggleEdgelessAIOnboarding(false), - footer: ( - - - {aiSubscription ? null : ( - - )} - - ), + actions, }, { duration: 1000 * 60 * 10 } ); edgelessNotifyId$.next(id); }, 1000); - }, [ - aiSubscription, - generalAIOnboardingOpened, - goToPricingPlans, - mode, - notifyId, - t, - ]); + }, [actions, generalAIOnboardingOpened, mode, notifyId, t]); return null; }; diff --git a/packages/frontend/core/src/components/affine/ai-onboarding/local.dialog.tsx b/packages/frontend/core/src/components/affine/ai-onboarding/local.dialog.tsx index 5b420e3f59..afb14fe86e 100644 --- a/packages/frontend/core/src/components/affine/ai-onboarding/local.dialog.tsx +++ b/packages/frontend/core/src/components/affine/ai-onboarding/local.dialog.tsx @@ -1,4 +1,4 @@ -import { Button, notify } from '@affine/component'; +import { type Notification, notify } from '@affine/component'; import { RouteLogic, useNavigateHelper, @@ -8,7 +8,7 @@ import { useI18n } from '@affine/i18n'; import { AiIcon } from '@blocksuite/icons/rc'; import { useLiveData, useService } from '@toeverything/infra'; import { cssVar } from '@toeverything/theme'; -import { useEffect, useRef } from 'react'; +import { useEffect, useMemo, useRef } from 'react'; import { toggleLocalAIOnboarding } from './apis'; import * as styles from './local.dialog.css'; @@ -29,51 +29,41 @@ const LocalOnboardingAnimation = () => { ); }; -const FooterActions = ({ onDismiss }: { onDismiss: () => void }) => { - const t = useI18n(); - const authService = useService(AuthService); - const loginStatus = useLiveData(authService.session.status$); - const loggedIn = loginStatus === 'authenticated'; - const { jumpToSignIn } = useNavigateHelper(); - - return ( -
- - - - {loggedIn ? null : ( - - )} -
- ); -}; - export const AIOnboardingLocal = () => { const t = useI18n(); const authService = useService(AuthService); const notifyId = useLiveData(localNotifyId$); const timeoutRef = useRef | null>(null); + const { jumpToSignIn } = useNavigateHelper(); const loginStatus = useLiveData(authService.session.status$); const notSignedIn = loginStatus !== 'authenticated'; + const actions = useMemo(() => { + const result: NonNullable = [ + { + key: 'learn-more', + label: t['com.affine.ai-onboarding.local.action-learn-more'](), + onClick: () => { + window.open('https://ai.affine.pro', '_blank', 'noreferrer'); + }, + }, + ]; + if (notSignedIn) { + result.push({ + key: 'get-started', + label: t['com.affine.ai-onboarding.local.action-get-started'](), + onClick: () => { + jumpToSignIn('', RouteLogic.REPLACE, {}, { initCloud: 'true' }); + }, + }); + } + + return result; + }, [t, jumpToSignIn, notSignedIn]); + useEffect(() => { - if (!notSignedIn) return; + // if (!notSignedIn) return; if (notifyId) return; if (timeoutRef.current) { clearTimeout(timeoutRef.current); @@ -95,21 +85,14 @@ export const AIOnboardingLocal = () => { thumb: , alignMessage: 'icon', onDismiss: () => toggleLocalAIOnboarding(false), - footer: ( - { - toggleLocalAIOnboarding(false); - notify.dismiss(id); - }} - /> - ), + actions, rootAttrs: { className: styles.card }, }, { duration: 1000 * 60 * 10 } ); localNotifyId$.next(id); }, 1000); - }, [notSignedIn, notifyId, t]); + }, [actions, notSignedIn, notifyId, t]); return null; }; diff --git a/packages/frontend/core/src/components/affine/subscription-landing/notify.css.ts b/packages/frontend/core/src/components/affine/subscription-landing/notify.css.ts index 37e544dc73..bd893afd20 100644 --- a/packages/frontend/core/src/components/affine/subscription-landing/notify.css.ts +++ b/packages/frontend/core/src/components/affine/subscription-landing/notify.css.ts @@ -6,13 +6,6 @@ export const notifyHeader = style({ fontSize: 15, }); -export const notifyFooter = style({ - display: 'flex', - justifyContent: 'end', - gap: 12, - paddingTop: 8, -}); - export const actionButton = style({ fontSize: cssVar('fontSm'), fontWeight: 500, diff --git a/packages/frontend/core/src/components/affine/subscription-landing/notify.tsx b/packages/frontend/core/src/components/affine/subscription-landing/notify.tsx index 973349aff2..737832001c 100644 --- a/packages/frontend/core/src/components/affine/subscription-landing/notify.tsx +++ b/packages/frontend/core/src/components/affine/subscription-landing/notify.tsx @@ -1,4 +1,4 @@ -import { Button, notify } from '@affine/component'; +import { type Notification, notify } from '@affine/component'; import { useI18n } from '@affine/i18n'; import clsx from 'clsx'; import { useCallback, useRef } from 'react'; @@ -7,48 +7,9 @@ import { actionButton, cancelButton, confirmButton, - notifyFooter, notifyHeader, } from './notify.css'; -interface SubscriptionChangedNotifyFooterProps { - onCancel: () => void; - onConfirm?: () => void; - to: string; - okText: string; - cancelText: string; -} - -const SubscriptionChangedNotifyFooter = ({ - to, - okText, - cancelText, - onCancel, - onConfirm, -}: SubscriptionChangedNotifyFooterProps) => { - return ( -
- - - - -
- ); -}; - export const useDowngradeNotify = () => { const t = useI18n(); const prevNotifyIdRef = useRef(null); @@ -56,6 +17,30 @@ export const useDowngradeNotify = () => { return useCallback( (link: string) => { prevNotifyIdRef.current && notify.dismiss(prevNotifyIdRef.current); + + const actions: Notification['actions'] = [ + { + key: 'later', + label: t['com.affine.payment.downgraded-notify.later'](), + onClick: () => {}, + buttonProps: { + className: clsx(actionButton, cancelButton), + }, + }, + { + key: 'ok', + label: BUILD_CONFIG.isElectron + ? t['com.affine.payment.downgraded-notify.ok-client']() + : t['com.affine.payment.downgraded-notify.ok-web'](), + onClick: () => { + window.open(link, '_blank', 'noreferrer'); + }, + buttonProps: { + className: clsx(actionButton, confirmButton), + }, + }, + ]; + const id = notify( { title: ( @@ -66,19 +51,7 @@ export const useDowngradeNotify = () => { message: t['com.affine.payment.downgraded-notify.content'](), alignMessage: 'title', icon: null, - footer: ( - notify.dismiss(id)} - onConfirm={() => notify.dismiss(id)} - /> - ), + actions, }, { duration: 24 * 60 * 60 * 1000 } ); diff --git a/packages/frontend/core/src/components/over-capacity/index.tsx b/packages/frontend/core/src/components/over-capacity/index.tsx index 96c0ffcf7e..bab286adac 100644 --- a/packages/frontend/core/src/components/over-capacity/index.tsx +++ b/packages/frontend/core/src/components/over-capacity/index.tsx @@ -43,10 +43,13 @@ export const OverCapacityNotification = () => { title: t['com.affine.payment.storage-limit.new-title'](), message: t['com.affine.payment.storage-limit.new-description.owner'](), - action: { - label: t['com.affine.payment.upgrade'](), - onClick: jumpToPricePlan, - }, + actions: [ + { + key: 'upgrade', + label: t['com.affine.payment.upgrade'](), + onClick: jumpToPricePlan, + }, + ], }); } else { notify.warning({ diff --git a/packages/frontend/core/src/desktop/dialogs/setting/general-setting/backup/index.tsx b/packages/frontend/core/src/desktop/dialogs/setting/general-setting/backup/index.tsx index 79a46e829c..7f93c52efc 100644 --- a/packages/frontend/core/src/desktop/dialogs/setting/general-setting/backup/index.tsx +++ b/packages/frontend/core/src/desktop/dialogs/setting/general-setting/backup/index.tsx @@ -87,14 +87,17 @@ const BackupWorkspaceItem = ({ item }: { item: BackupWorkspaceItem }) => { } notify.success({ title: t['com.affine.settings.workspace.backup.import.success'](), - action: { - label: - t['com.affine.settings.workspace.backup.import.success.action'](), - onClick: () => { - jumpToPage(workspaceId, 'all'); + actions: [ + { + key: 'open', + label: + t['com.affine.settings.workspace.backup.import.success.action'](), + onClick: () => { + jumpToPage(workspaceId, 'all'); + }, + autoClose: false, }, - autoClose: false, - }, + ], }); setMenuOpen(false); setImporting(false); diff --git a/packages/frontend/core/src/modules/at-menu-config/services/index.ts b/packages/frontend/core/src/modules/at-menu-config/services/index.ts index 6c63eee722..804d7498ee 100644 --- a/packages/frontend/core/src/modules/at-menu-config/services/index.ts +++ b/packages/frontend/core/src/modules/at-menu-config/services/index.ts @@ -459,45 +459,49 @@ export class AtMenuConfigService extends Service { ]({ username, }), - action: { - label: 'Invite', - onClick: async () => { - track.$.sharePanel.$.inviteUserDocRole({ - control: 'member list', - role: 'reader', - }); - - try { - await this.docGrantedUsersService.updateUserRole( - id, - DocRole.Reader - ); - - await notificationService.mentionUser( - id, - workspaceId, - { - id: docId, - title: - this.docDisplayMetaService.title$(docId).value, - blockId: block.blockId, - mode: mode as GraphqlDocMode, - } - ); - - notify.success({ - title: I18n.t( - 'com.affine.editor.at-menu.invited-and-notified' - ), + actions: [ + { + key: 'invite', + label: 'Invite', + onClick: async () => { + track.$.sharePanel.$.inviteUserDocRole({ + control: 'member list', + role: 'reader', }); - } catch (error) { - const err = UserFriendlyError.fromAny(error); - notify.error({ - title: I18n[`error.${err.name}`](err.data), - }); - } + + try { + await this.docGrantedUsersService.updateUserRole( + id, + DocRole.Reader + ); + + await notificationService.mentionUser( + id, + workspaceId, + { + id: docId, + title: + this.docDisplayMetaService.title$(docId) + .value, + blockId: block.blockId, + mode: mode as GraphqlDocMode, + } + ); + + notify.success({ + title: I18n.t( + 'com.affine.editor.at-menu.invited-and-notified' + ), + }); + } catch (error) { + const err = UserFriendlyError.fromAny(error); + notify.error({ + title: I18n[`error.${err.name}`](err.data), + }); + } + }, }, - }, + ], }); } else { notify.error({ diff --git a/tests/affine-local/e2e/blocksuite/edgeless/note.spec.ts b/tests/affine-local/e2e/blocksuite/edgeless/note.spec.ts index 7febc3fe85..6391ac2733 100644 --- a/tests/affine-local/e2e/blocksuite/edgeless/note.spec.ts +++ b/tests/affine-local/e2e/blocksuite/edgeless/note.spec.ts @@ -243,14 +243,14 @@ test.describe('edgeless note element toolbar', () => { await displayInPage.click(); await locateModeSwitchButton(page, 'page').click(); - expect(notes).toHaveCount(2); + await expect(notes).toHaveCount(2); await clickEdgelessModeButton(page); await clickView(page, [100, 100]); await displayInPage.click(); await locateModeSwitchButton(page, 'page').click(); await waitForEditorLoad(page); - expect(notes).toHaveCount(1); + await expect(notes).toHaveCount(1); const undoButton = page.getByTestId('undo-display-in-page'); const viewTocButton = page.getByTestId('view-in-toc'); @@ -259,14 +259,14 @@ test.describe('edgeless note element toolbar', () => { await waitForEditorLoad(page); await clickView(page, [100, 100]); await displayInPage.click(); - expect(undoButton).toBeVisible(); - expect(viewTocButton).toBeVisible(); + await expect(undoButton).toBeVisible(); + await expect(viewTocButton).toBeVisible(); await undoButton.click(); await expect(undoButton).toBeHidden(); await locateModeSwitchButton(page, 'page').click(); await waitForEditorLoad(page); - expect(notes).toHaveCount(1); + await expect(notes).toHaveCount(1); await clickEdgelessModeButton(page); await waitForEditorLoad(page); @@ -274,7 +274,7 @@ test.describe('edgeless note element toolbar', () => { await displayInPage.click(); await undoByKeyboard(page); await page.waitForTimeout(500); - expect( + await expect( undoButton, 'the toast should be hidden immediately when undo by keyboard' ).toBeHidden(); @@ -286,7 +286,7 @@ test.describe('edgeless note element toolbar', () => { const highlightNoteCards = toc.locator( 'affine-outline-note-card > [data-status="selected"]' ); - expect(highlightNoteCards).toHaveCount(1); + await expect(highlightNoteCards).toHaveCount(1); }); test('note edgeless styles', async ({ page }) => {