refactor(core): replace all notification relies on jotai (#6417)

- remove all notification that implemented with jotai and replaced with new `notify`
- Add some notify presets:
  - `notify.error`
  - `notify.success`
  - `notify.warning`
This commit is contained in:
CatsJuice 2024-04-02 03:19:06 +00:00
parent a4cd51e503
commit 9127bfae67
No known key found for this signature in database
GPG Key ID: 1C1E76924FAFDDE4
24 changed files with 150 additions and 201 deletions

View File

@ -1,11 +1,10 @@
import type { PasswordLimitsFragment } from '@affine/graphql'; import type { PasswordLimitsFragment } from '@affine/graphql';
import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { useSetAtom } from 'jotai';
import type { FC } from 'react'; import type { FC } from 'react';
import { useCallback, useState } from 'react'; import { useCallback, useState } from 'react';
import { Button } from '../../ui/button'; import { Button } from '../../ui/button';
import { pushNotificationAtom } from '../notification-center'; import { notify } from '../../ui/notification';
import { AuthPageContainer } from './auth-page-container'; import { AuthPageContainer } from './auth-page-container';
import { SetPassword } from './set-password'; import { SetPassword } from './set-password';
import type { User } from './type'; import type { User } from './type';
@ -23,22 +22,19 @@ export const ChangePasswordPage: FC<{
}) => { }) => {
const t = useAFFiNEI18N(); const t = useAFFiNEI18N();
const [hasSetUp, setHasSetUp] = useState(false); const [hasSetUp, setHasSetUp] = useState(false);
const pushNotification = useSetAtom(pushNotificationAtom);
const onSetPassword = useCallback( const onSetPassword = useCallback(
(passWord: string) => { (passWord: string) => {
propsOnSetPassword(passWord) propsOnSetPassword(passWord)
.then(() => setHasSetUp(true)) .then(() => setHasSetUp(true))
.catch(e => .catch(e =>
pushNotification({ notify.error({
title: t['com.affine.auth.password.set-failed'](), title: t['com.affine.auth.password.set-failed'](),
message: String(e), message: String(e),
key: Date.now().toString(),
type: 'error',
}) })
); );
}, },
[propsOnSetPassword, t, pushNotification] [propsOnSetPassword, t]
); );
return ( return (

View File

@ -1,11 +1,10 @@
import type { PasswordLimitsFragment } from '@affine/graphql'; import type { PasswordLimitsFragment } from '@affine/graphql';
import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { useSetAtom } from 'jotai';
import type { FC } from 'react'; import type { FC } from 'react';
import { useCallback, useState } from 'react'; import { useCallback, useState } from 'react';
import { Button } from '../../ui/button'; import { Button } from '../../ui/button';
import { pushNotificationAtom } from '../notification-center'; import { notify } from '../../ui/notification';
import { AuthPageContainer } from './auth-page-container'; import { AuthPageContainer } from './auth-page-container';
import { SetPassword } from './set-password'; import { SetPassword } from './set-password';
import type { User } from './type'; import type { User } from './type';
@ -23,22 +22,19 @@ export const SetPasswordPage: FC<{
}) => { }) => {
const t = useAFFiNEI18N(); const t = useAFFiNEI18N();
const [hasSetUp, setHasSetUp] = useState(false); const [hasSetUp, setHasSetUp] = useState(false);
const pushNotification = useSetAtom(pushNotificationAtom);
const onSetPassword = useCallback( const onSetPassword = useCallback(
(passWord: string) => { (passWord: string) => {
propsOnSetPassword(passWord) propsOnSetPassword(passWord)
.then(() => setHasSetUp(true)) .then(() => setHasSetUp(true))
.catch(e => .catch(e =>
pushNotification({ notify.error({
title: t['com.affine.auth.password.set-failed'](), title: t['com.affine.auth.password.set-failed'](),
message: String(e), message: String(e),
key: Date.now().toString(),
type: 'error',
}) })
); );
}, },
[propsOnSetPassword, pushNotification, t] [propsOnSetPassword, t]
); );
return ( return (

View File

@ -1,11 +1,10 @@
import type { PasswordLimitsFragment } from '@affine/graphql'; import type { PasswordLimitsFragment } from '@affine/graphql';
import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { useSetAtom } from 'jotai';
import type { FC } from 'react'; import type { FC } from 'react';
import { useCallback, useState } from 'react'; import { useCallback, useState } from 'react';
import { Button } from '../../ui/button'; import { Button } from '../../ui/button';
import { pushNotificationAtom } from '../notification-center'; import { notify } from '../../ui/notification';
import { AuthPageContainer } from './auth-page-container'; import { AuthPageContainer } from './auth-page-container';
import { SetPassword } from './set-password'; import { SetPassword } from './set-password';
import type { User } from './type'; import type { User } from './type';
@ -25,22 +24,19 @@ export const SignUpPage: FC<{
}) => { }) => {
const t = useAFFiNEI18N(); const t = useAFFiNEI18N();
const [hasSetUp, setHasSetUp] = useState(false); const [hasSetUp, setHasSetUp] = useState(false);
const pushNotification = useSetAtom(pushNotificationAtom);
const onSetPassword = useCallback( const onSetPassword = useCallback(
(passWord: string) => { (passWord: string) => {
propsOnSetPassword(passWord) propsOnSetPassword(passWord)
.then(() => setHasSetUp(true)) .then(() => setHasSetUp(true))
.catch(e => .catch(e =>
pushNotification({ notify.error({
title: t['com.affine.auth.password.set-failed'](), title: t['com.affine.auth.password.set-failed'](),
message: String(e), message: String(e),
key: Date.now().toString(),
type: 'error',
}) })
); );
}, },
[propsOnSetPassword, pushNotification, t] [propsOnSetPassword, t]
); );
const onLater = useCallback(() => { const onLater = useCallback(() => {
setHasSetUp(true); setHasSetUp(true);

View File

@ -1,6 +1,9 @@
import { atom } from 'jotai'; import { atom } from 'jotai';
import { nanoid } from 'nanoid'; import { nanoid } from 'nanoid';
/**
* @deprecated use `import type { Notification } from '@affine/component'` instead
*/
export type Notification = { export type Notification = {
key?: string; key?: string;
title: string; title: string;
@ -19,6 +22,9 @@ const notificationsBaseAtom = atom<Notification[]>([]);
const expandNotificationCenterBaseAtom = atom(false); const expandNotificationCenterBaseAtom = atom(false);
const cleanupQueueAtom = atom<(() => unknown)[]>([]); const cleanupQueueAtom = atom<(() => unknown)[]>([]);
/**
* @deprecated use `import { notify } from '@affine/component'` instead
*/
export const expandNotificationCenterAtom = atom<boolean, [boolean], void>( export const expandNotificationCenterAtom = atom<boolean, [boolean], void>(
get => get(expandNotificationCenterBaseAtom), get => get(expandNotificationCenterBaseAtom),
(get, set, value) => { (get, set, value) => {
@ -29,17 +35,24 @@ export const expandNotificationCenterAtom = atom<boolean, [boolean], void>(
set(expandNotificationCenterBaseAtom, value); set(expandNotificationCenterBaseAtom, value);
} }
); );
/**
* @deprecated use `import { notify } from '@affine/component'` instead
*/
export const notificationsAtom = atom<Notification[]>(get => export const notificationsAtom = atom<Notification[]>(get =>
get(notificationsBaseAtom) get(notificationsBaseAtom)
); );
/**
* @deprecated use `import { notify } from '@affine/component'` instead
*/
export const removeNotificationAtom = atom(null, (_, set, key: string) => { export const removeNotificationAtom = atom(null, (_, set, key: string) => {
set(notificationsBaseAtom, notifications => set(notificationsBaseAtom, notifications =>
notifications.filter(notification => notification.key !== key) notifications.filter(notification => notification.key !== key)
); );
}); });
/**
* @deprecated use `import { notify } from '@affine/component'` instead
*/
export const pushNotificationAtom = atom<null, [Notification], void>( export const pushNotificationAtom = atom<null, [Notification], void>(
null, null,
(_, set, newNotification) => { (_, set, newNotification) => {

View File

@ -375,6 +375,9 @@ function NotificationCard(props: NotificationCardProps): ReactNode {
); );
} }
/**
* @deprecated use `import { NotificationCenter } from '@affine/component'` instead
*/
export function NotificationCenter(): ReactNode { export function NotificationCenter(): ReactNode {
const notifications = useAtomValue(notificationsAtom); const notifications = useAtomValue(notificationsAtom);
const [expand, setExpand] = useAtom(expandNotificationCenterAtom); const [expand, setExpand] = useAtom(expandNotificationCenterAtom);

View File

@ -1,3 +1,4 @@
import { SingleSelectSelectSolidIcon } from '@blocksuite/icons';
import { assignInlineVars } from '@vanilla-extract/dynamic'; import { assignInlineVars } from '@vanilla-extract/dynamic';
import { type CSSProperties, type FC, useMemo } from 'react'; import { type CSSProperties, type FC, useMemo } from 'react';
import { type ExternalToast, toast, Toaster } from 'sonner'; import { type ExternalToast, toast, Toaster } from 'sonner';
@ -54,6 +55,29 @@ export function notify(notification: Notification, options?: ExternalToast) {
}, options); }, options);
} }
notify.error = (notification: Notification, options?: ExternalToast) => {
return notify({ style: 'alert', theme: 'error', ...notification }, options);
};
notify.success = (notification: Notification, options?: ExternalToast) => {
return notify(
{
icon: <SingleSelectSelectSolidIcon />,
style: 'alert',
theme: 'success',
...notification,
},
options
);
};
notify.warning = (notification: Notification, options?: ExternalToast) => {
return notify(
{ style: 'information', theme: 'warning', ...notification },
options
);
};
notify.custom = ( notify.custom = (
Component: FC<NotificationCustomRendererProps>, Component: FC<NotificationCustomRendererProps>,
options?: ExternalToast options?: ExternalToast

View File

@ -1,11 +1,10 @@
import { Wrapper } from '@affine/component'; import { notify, Wrapper } from '@affine/component';
import { import {
AuthContent, AuthContent,
AuthInput, AuthInput,
BackButton, BackButton,
ModalHeader, ModalHeader,
} from '@affine/component/auth-components'; } from '@affine/component/auth-components';
import { pushNotificationAtom } from '@affine/component/notification-center';
import { Button } from '@affine/component/ui/button'; import { Button } from '@affine/component/ui/button';
import { useCredentialsRequirement } from '@affine/core/hooks/affine/use-server-config'; import { useCredentialsRequirement } from '@affine/core/hooks/affine/use-server-config';
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks'; import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
@ -17,7 +16,6 @@ import {
sendVerifyEmailMutation, sendVerifyEmailMutation,
} from '@affine/graphql'; } from '@affine/graphql';
import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { useSetAtom } from 'jotai/react';
import { useCallback, useState } from 'react'; import { useCallback, useState } from 'react';
import { useMutation } from '../../../hooks/use-mutation'; import { useMutation } from '../../../hooks/use-mutation';
@ -165,7 +163,6 @@ export const SendEmail = ({
const t = useAFFiNEI18N(); const t = useAFFiNEI18N();
const { password: passwordLimits } = useCredentialsRequirement(); const { password: passwordLimits } = useCredentialsRequirement();
const [hasSentEmail, setHasSentEmail] = useState(false); const [hasSentEmail, setHasSentEmail] = useState(false);
const pushNotification = useSetAtom(pushNotificationAtom);
const title = useEmailTitle(emailType); const title = useEmailTitle(emailType);
const hint = useNotificationHint(emailType); const hint = useNotificationHint(emailType);
@ -177,14 +174,9 @@ export const SendEmail = ({
// TODO: add error handler // TODO: add error handler
await sendEmail(email); await sendEmail(email);
pushNotification({ notify.success({ title: hint });
title: hint,
message: '',
key: Date.now().toString(),
type: 'success',
});
setHasSentEmail(true); setHasSentEmail(true);
}, [email, hint, pushNotification, sendEmail]); }, [email, hint, sendEmail]);
return ( return (
<> <>

View File

@ -1,5 +1,4 @@
import { pushNotificationAtom } from '@affine/component/notification-center'; import { notify } from '@affine/component';
import type { Notification } from '@affine/component/notification-center/index.jotai';
import type { OAuthProviderType } from '@affine/graphql'; import type { OAuthProviderType } from '@affine/graphql';
import { atom, useAtom, useSetAtom } from 'jotai'; import { atom, useAtom, useSetAtom } from 'jotai';
import { useCallback } from 'react'; import { useCallback } from 'react';
@ -10,19 +9,6 @@ import { useSubscriptionSearch } from './use-subscription';
const COUNT_DOWN_TIME = 60; const COUNT_DOWN_TIME = 60;
export const INTERNAL_BETA_URL = `https://community.affine.pro/c/insider-general/`; export const INTERNAL_BETA_URL = `https://community.affine.pro/c/insider-general/`;
function handleSendEmailError(
res: Response | undefined | void,
pushNotification: (notification: Notification) => void
) {
if (!res?.ok) {
pushNotification({
title: 'Send email error',
message: 'Please back to home and try again',
type: 'error',
});
}
}
type AuthStoreAtom = { type AuthStoreAtom = {
allowSendEmail: boolean; allowSendEmail: boolean;
resendCountDown: number; resendCountDown: number;
@ -60,7 +46,6 @@ const countDownAtom = atom(
export const useAuth = () => { export const useAuth = () => {
const subscriptionData = useSubscriptionSearch(); const subscriptionData = useSubscriptionSearch();
const pushNotification = useSetAtom(pushNotificationAtom);
const [authStore, setAuthStore] = useAtom(authStoreAtom); const [authStore, setAuthStore] = useAtom(authStoreAtom);
const startResendCountDown = useSetAtom(countDownAtom); const startResendCountDown = useSetAtom(countDownAtom);
@ -96,7 +81,13 @@ export const useAuth = () => {
} }
).catch(console.error); ).catch(console.error);
handleSendEmailError(res, pushNotification); if (!res?.ok) {
// TODO: i18n
notify.error({
title: 'Send email error',
message: 'Please back to home and try again',
});
}
setAuthStore({ setAuthStore({
isMutating: false, isMutating: false,
@ -104,11 +95,12 @@ export const useAuth = () => {
resendCountDown: COUNT_DOWN_TIME, resendCountDown: COUNT_DOWN_TIME,
}); });
// TODO: when errored, should reset the count down
startResendCountDown(); startResendCountDown();
return res; return res;
}, },
[pushNotification, setAuthStore, startResendCountDown, subscriptionData] [setAuthStore, startResendCountDown, subscriptionData]
); );
const signUp = useCallback( const signUp = useCallback(

View File

@ -1,5 +1,4 @@
import { FlexWrapper, Input } from '@affine/component'; import { FlexWrapper, Input, notify } from '@affine/component';
import { pushNotificationAtom } from '@affine/component/notification-center';
import { import {
SettingHeader, SettingHeader,
SettingRow, SettingRow,
@ -35,7 +34,6 @@ import * as styles from './style.css';
export const UserAvatar = () => { export const UserAvatar = () => {
const t = useAFFiNEI18N(); const t = useAFFiNEI18N();
const user = useCurrentUser(); const user = useCurrentUser();
const pushNotification = useSetAtom(pushNotificationAtom);
const { trigger: avatarTrigger } = useMutation({ const { trigger: avatarTrigger } = useMutation({
mutation: uploadAvatarMutation, mutation: uploadAvatarMutation,
@ -55,19 +53,17 @@ export const UserAvatar = () => {
avatar: reducedFile, // Pass the reducedFile directly to the avatarTrigger avatar: reducedFile, // Pass the reducedFile directly to the avatarTrigger
}); });
user.update({ avatarUrl: data.uploadAvatar.avatarUrl }); user.update({ avatarUrl: data.uploadAvatar.avatarUrl });
pushNotification({ // TODO: i18n
title: 'Update user avatar success', notify.success({ title: 'Update user avatar success' });
type: 'success',
});
} catch (e) { } catch (e) {
pushNotification({ // TODO: i18n
notify.error({
title: 'Update user avatar failed', title: 'Update user avatar failed',
message: String(e), message: String(e),
type: 'error',
}); });
} }
}, },
[avatarTrigger, pushNotification, user] [avatarTrigger, user]
); );
const handleRemoveUserAvatar = useCallback( const handleRemoveUserAvatar = useCallback(
@ -109,7 +105,6 @@ export const AvatarAndName = () => {
const t = useAFFiNEI18N(); const t = useAFFiNEI18N();
const user = useCurrentUser(); const user = useCurrentUser();
const [input, setInput] = useState<string>(user.name); const [input, setInput] = useState<string>(user.name);
const pushNotification = useSetAtom(pushNotificationAtom);
const { trigger: updateProfile } = useMutation({ const { trigger: updateProfile } = useMutation({
mutation: updateUserProfileMutation, mutation: updateUserProfileMutation,
@ -129,13 +124,12 @@ export const AvatarAndName = () => {
}); });
user.update({ name: data.updateProfile.name }); user.update({ name: data.updateProfile.name });
} catch (e) { } catch (e) {
pushNotification({ notify.error({
title: 'Failed to update user name.', title: 'Failed to update user name.',
message: String(e), message: String(e),
type: 'error',
}); });
} }
}, [allowUpdate, input, user, updateProfile, pushNotification]); }, [allowUpdate, input, user, updateProfile]);
return ( return (
<SettingRow <SettingRow

View File

@ -1,5 +1,4 @@
import { RadioButton, RadioButtonGroup } from '@affine/component'; import { notify, RadioButton, RadioButtonGroup } from '@affine/component';
import { pushNotificationAtom } from '@affine/component/notification-center';
import { import {
pricesQuery, pricesQuery,
SubscriptionPlan, SubscriptionPlan,
@ -7,7 +6,8 @@ import {
} from '@affine/graphql'; } from '@affine/graphql';
import { Trans } from '@affine/i18n'; import { Trans } from '@affine/i18n';
import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { useSetAtom } from 'jotai'; import { SingleSelectSelectSolidIcon } from '@blocksuite/icons';
import { cssVar } from '@toeverything/theme';
import { Suspense, useEffect, useRef, useState } from 'react'; import { Suspense, useEffect, useRef, useState } from 'react';
import type { FallbackProps } from 'react-error-boundary'; import type { FallbackProps } from 'react-error-boundary';
@ -36,7 +36,6 @@ const getRecurringLabel = ({
const Settings = () => { const Settings = () => {
const t = useAFFiNEI18N(); const t = useAFFiNEI18N();
const [subscription, mutateSubscription] = useUserSubscription(); const [subscription, mutateSubscription] = useUserSubscription();
const pushNotification = useSetAtom(pushNotificationAtom);
const loggedIn = useCurrentLoginStatus() === 'authenticated'; const loggedIn = useCurrentLoginStatus() === 'authenticated';
const planDetail = getPlanDetail(t); const planDetail = getPlanDetail(t);
@ -165,9 +164,11 @@ const Settings = () => {
key={detail.plan} key={detail.plan}
onSubscriptionUpdate={mutateSubscription} onSubscriptionUpdate={mutateSubscription}
onNotify={({ detail, recurring }) => { onNotify={({ detail, recurring }) => {
pushNotification({ notify({
type: 'success', style: 'normal',
theme: 'default', icon: (
<SingleSelectSelectSolidIcon color={cssVar('primaryColor')} />
),
title: t['com.affine.payment.updated-notify-title'](), title: t['com.affine.payment.updated-notify-title'](),
message: message:
detail.plan === SubscriptionPlan.Free detail.plan === SubscriptionPlan.Free

View File

@ -1,4 +1,4 @@
import { pushNotificationAtom } from '@affine/component/notification-center'; import { notify } from '@affine/component';
import { SettingRow } from '@affine/component/setting-components'; import { SettingRow } from '@affine/component/setting-components';
import { ConfirmModal } from '@affine/component/ui/modal'; import { ConfirmModal } from '@affine/component/ui/modal';
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks'; import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
@ -39,7 +39,6 @@ export const DeleteLeaveWorkspace = ({
const workspaceManager = useService(WorkspaceManager); const workspaceManager = useService(WorkspaceManager);
const workspaceList = useLiveData(workspaceManager.list.workspaceList$); const workspaceList = useLiveData(workspaceManager.list.workspaceList$);
const currentWorkspace = useService(Workspace); const currentWorkspace = useService(Workspace);
const pushNotification = useSetAtom(pushNotificationAtom);
const onLeaveOrDelete = useCallback(() => { const onLeaveOrDelete = useCallback(() => {
if (isOwner) { if (isOwner) {
@ -69,15 +68,11 @@ export const DeleteLeaveWorkspace = ({
} }
await workspaceManager.deleteWorkspace(workspaceMetadata); await workspaceManager.deleteWorkspace(workspaceMetadata);
pushNotification({ notify.success({ title: t['Successfully deleted']() });
title: t['Successfully deleted'](),
type: 'success',
});
}, [ }, [
currentWorkspace?.id, currentWorkspace?.id,
jumpToIndex, jumpToIndex,
jumpToSubPath, jumpToSubPath,
pushNotification,
setSettingModal, setSettingModal,
t, t,
workspaceList, workspaceList,

View File

@ -1,4 +1,4 @@
import { pushNotificationAtom } from '@affine/component/notification-center'; import { notify } from '@affine/component';
import { SettingRow } from '@affine/component/setting-components'; import { SettingRow } from '@affine/component/setting-components';
import { Button } from '@affine/component/ui/button'; import { Button } from '@affine/component/ui/button';
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks'; import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
@ -6,7 +6,6 @@ import { useSystemOnline } from '@affine/core/hooks/use-system-online';
import { apis } from '@affine/electron-api'; import { apis } from '@affine/electron-api';
import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import type { Workspace, WorkspaceMetadata } from '@toeverything/infra'; import type { Workspace, WorkspaceMetadata } from '@toeverything/infra';
import { useSetAtom } from 'jotai';
import { useState } from 'react'; import { useState } from 'react';
interface ExportPanelProps { interface ExportPanelProps {
@ -23,7 +22,6 @@ export const ExportPanel = ({
const [saving, setSaving] = useState(false); const [saving, setSaving] = useState(false);
const isOnline = useSystemOnline(); const isOnline = useSystemOnline();
const pushNotification = useSetAtom(pushNotificationAtom);
const onExport = useAsyncCallback(async () => { const onExport = useAsyncCallback(async () => {
if (saving || !workspace) { if (saving || !workspace) {
return; return;
@ -39,21 +37,14 @@ export const ExportPanel = ({
if (result?.error) { if (result?.error) {
throw new Error(result.error); throw new Error(result.error);
} else if (!result?.canceled) { } else if (!result?.canceled) {
pushNotification({ notify.success({ title: t['Export success']() });
type: 'success',
title: t['Export success'](),
});
} }
} catch (e: any) { } catch (e: any) {
pushNotification({ notify.error({ title: t['Export failed'](), message: e.message });
type: 'error',
title: t['Export failed'](),
message: e.message,
});
} finally { } finally {
setSaving(false); setSaving(false);
} }
}, [isOnline, pushNotification, saving, t, workspace, workspaceId]); }, [isOnline, saving, t, workspace, workspaceId]);
return ( return (
<SettingRow name={t['Export']()} desc={t['Export Description']()}> <SettingRow name={t['Export']()} desc={t['Export Description']()}>

View File

@ -1,3 +1,4 @@
import { notify } from '@affine/component';
import type { import type {
InviteModalProps, InviteModalProps,
PaginationProps, PaginationProps,
@ -7,7 +8,6 @@ import {
MemberLimitModal, MemberLimitModal,
Pagination, Pagination,
} from '@affine/component/member-components'; } from '@affine/component/member-components';
import { pushNotificationAtom } from '@affine/component/notification-center';
import { SettingRow } from '@affine/component/setting-components'; import { SettingRow } from '@affine/component/setting-components';
import { Avatar } from '@affine/component/ui/avatar'; import { Avatar } from '@affine/component/ui/avatar';
import { Button, IconButton } from '@affine/component/ui/button'; import { Button, IconButton } from '@affine/component/ui/button';
@ -90,8 +90,6 @@ export const CloudWorkspaceMembersPanel = ({
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [memberSkip, setMemberSkip] = useState(0); const [memberSkip, setMemberSkip] = useState(0);
const pushNotification = useSetAtom(pushNotificationAtom);
const openModal = useCallback(() => { const openModal = useCallback(() => {
setOpen(true); setOpen(true);
}, []); }, []);
@ -109,15 +107,14 @@ export const CloudWorkspaceMembersPanel = ({
true true
); );
if (success) { if (success) {
pushNotification({ notify.success({
title: t['Invitation sent'](), title: t['Invitation sent'](),
message: t['Invitation sent hint'](), message: t['Invitation sent hint'](),
type: 'success',
}); });
setOpen(false); setOpen(false);
} }
}, },
[invite, pushNotification, t] [invite, t]
); );
const setSettingModalAtom = useSetAtom(openSettingModalAtom); const setSettingModalAtom = useSetAtom(openSettingModalAtom);
@ -146,13 +143,10 @@ export const CloudWorkspaceMembersPanel = ({
async memberId => { async memberId => {
const res = await revokeMemberPermission(memberId); const res = await revokeMemberPermission(memberId);
if (res?.revoke) { if (res?.revoke) {
pushNotification({ notify.success({ title: t['Removed successfully']() });
title: t['Removed successfully'](),
type: 'success',
});
} }
}, },
[pushNotification, revokeMemberPermission, t] [revokeMemberPermission, t]
); );
const desc = useMemo(() => { const desc = useMemo(() => {

View File

@ -1,5 +1,4 @@
import { FlexWrapper, Input, Wrapper } from '@affine/component'; import { FlexWrapper, Input, notify, Wrapper } from '@affine/component';
import { pushNotificationAtom } from '@affine/component/notification-center';
import { Avatar } from '@affine/component/ui/avatar'; import { Avatar } from '@affine/component/ui/avatar';
import { Button } from '@affine/component/ui/button'; import { Button } from '@affine/component/ui/button';
import { Upload } from '@affine/core/components/pure/file-upload'; import { Upload } from '@affine/core/components/pure/file-upload';
@ -11,7 +10,6 @@ import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { CameraIcon } from '@blocksuite/icons'; import { CameraIcon } from '@blocksuite/icons';
import type { Workspace } from '@toeverything/infra'; import type { Workspace } from '@toeverything/infra';
import { useLiveData } from '@toeverything/infra'; import { useLiveData } from '@toeverything/infra';
import { useSetAtom } from 'jotai';
import type { KeyboardEvent, MouseEvent } from 'react'; import type { KeyboardEvent, MouseEvent } from 'react';
import { useCallback, useEffect, useState } from 'react'; import { useCallback, useEffect, useState } from 'react';
@ -24,7 +22,6 @@ export interface ProfilePanelProps extends WorkspaceSettingDetailProps {
export const ProfilePanel = ({ isOwner, workspace }: ProfilePanelProps) => { export const ProfilePanel = ({ isOwner, workspace }: ProfilePanelProps) => {
const t = useAFFiNEI18N(); const t = useAFFiNEI18N();
const pushNotification = useSetAtom(pushNotificationAtom);
const workspaceIsReady = useLiveData(workspace?.engine.rootDocState$)?.ready; const workspaceIsReady = useLiveData(workspace?.engine.rootDocState$)?.ready;
@ -93,12 +90,9 @@ export const ProfilePanel = ({ isOwner, workspace }: ProfilePanelProps) => {
const handleUpdateWorkspaceName = useCallback( const handleUpdateWorkspaceName = useCallback(
(name: string) => { (name: string) => {
setWorkspaceName(name); setWorkspaceName(name);
pushNotification({ notify.success({ title: t['Update workspace name success']() });
title: t['Update workspace name success'](),
type: 'success',
});
}, },
[pushNotification, setWorkspaceName, t] [setWorkspaceName, t]
); );
const handleSetInput = useCallback((value: string) => { const handleSetInput = useCallback((value: string) => {
@ -130,20 +124,16 @@ export const ProfilePanel = ({ isOwner, workspace }: ProfilePanelProps) => {
(file: File) => { (file: File) => {
setWorkspaceAvatar(file) setWorkspaceAvatar(file)
.then(() => { .then(() => {
pushNotification({ notify.success({ title: 'Update workspace avatar success' });
title: 'Update workspace avatar success',
type: 'success',
});
}) })
.catch(error => { .catch(error => {
pushNotification({ notify.error({
title: 'Update workspace avatar failed', title: 'Update workspace avatar failed',
message: error, message: error,
type: 'error',
}); });
}); });
}, },
[pushNotification, setWorkspaceAvatar] [setWorkspaceAvatar]
); );
const canAdjustAvatar = workspaceIsReady && avatarUrl && isOwner; const canAdjustAvatar = workspaceIsReady && avatarUrl && isOwner;

View File

@ -1,5 +1,4 @@
import { Tooltip } from '@affine/component'; import { notify, Tooltip } from '@affine/component';
import { pushNotificationAtom } from '@affine/component/notification-center';
import { Avatar, type AvatarProps } from '@affine/component/ui/avatar'; import { Avatar, type AvatarProps } from '@affine/component/ui/avatar';
import { Loading } from '@affine/component/ui/loading'; import { Loading } from '@affine/component/ui/loading';
import { openSettingModalAtom } from '@affine/core/atoms'; import { openSettingModalAtom } from '@affine/core/atoms';
@ -81,7 +80,6 @@ const OfflineStatus = () => {
const useSyncEngineSyncProgress = () => { const useSyncEngineSyncProgress = () => {
const t = useAFFiNEI18N(); const t = useAFFiNEI18N();
const isOnline = useSystemOnline(); const isOnline = useSystemOnline();
const pushNotification = useSetAtom(pushNotificationAtom);
const { syncing, progress, retrying, errorMessage } = useDocEngineStatus(); const { syncing, progress, retrying, errorMessage } = useDocEngineStatus();
const [isOverCapacity, setIsOverCapacity] = useState(false); const [isOverCapacity, setIsOverCapacity] = useState(false);
@ -89,7 +87,7 @@ const useSyncEngineSyncProgress = () => {
const isOwner = useIsWorkspaceOwner(currentWorkspace.meta); const isOwner = useIsWorkspaceOwner(currentWorkspace.meta);
const setSettingModalAtom = useSetAtom(openSettingModalAtom); const setSettingModalAtom = useSetAtom(openSettingModalAtom);
const jumpToPricePlan = useCallback(async () => { const jumpToPricePlan = useCallback(() => {
setSettingModalAtom({ setSettingModalAtom({
open: true, open: true,
activeTab: 'plans', activeTab: 'plans',
@ -108,17 +106,17 @@ const useSyncEngineSyncProgress = () => {
} }
setIsOverCapacity(true); setIsOverCapacity(true);
if (isOwner) { if (isOwner) {
pushNotification({ notify.warning({
type: 'warning',
title: t['com.affine.payment.storage-limit.title'](), title: t['com.affine.payment.storage-limit.title'](),
message: message:
t['com.affine.payment.storage-limit.description.owner'](), t['com.affine.payment.storage-limit.description.owner'](),
actionLabel: t['com.affine.payment.storage-limit.view'](), action: {
action: jumpToPricePlan, label: t['com.affine.payment.storage-limit.view'](),
onClick: jumpToPricePlan,
},
}); });
} else { } else {
pushNotification({ notify.warning({
type: 'warning',
title: t['com.affine.payment.storage-limit.title'](), title: t['com.affine.payment.storage-limit.title'](),
message: message:
t['com.affine.payment.storage-limit.description.member'](), t['com.affine.payment.storage-limit.description.member'](),
@ -129,7 +127,7 @@ const useSyncEngineSyncProgress = () => {
return () => { return () => {
disposableOverCapacity?.dispose(); disposableOverCapacity?.dispose();
}; };
}, [currentWorkspace, isOwner, jumpToPricePlan, pushNotification, t]); }, [currentWorkspace, isOwner, jumpToPricePlan, t]);
const content = useMemo(() => { const content = useMemo(() => {
// TODO: add i18n // TODO: add i18n

View File

@ -1,8 +1,8 @@
import { notify } from '@affine/component';
import { import {
pushGlobalLoadingEventAtom, pushGlobalLoadingEventAtom,
resolveGlobalLoadingEventAtom, resolveGlobalLoadingEventAtom,
} from '@affine/component/global-loading'; } from '@affine/component/global-loading';
import { pushNotificationAtom } from '@affine/component/notification-center';
import { apis } from '@affine/electron-api'; import { apis } from '@affine/electron-api';
import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import type { PageRootService, RootBlockModel } from '@blocksuite/blocks'; import type { PageRootService, RootBlockModel } from '@blocksuite/blocks';
@ -51,7 +51,6 @@ async function exportHandler({ page, type }: ExportHandlerOptions) {
} }
export const useExportPage = (page: Doc) => { export const useExportPage = (page: Doc) => {
const pushNotification = useSetAtom(pushNotificationAtom);
const pushGlobalLoadingEvent = useSetAtom(pushGlobalLoadingEventAtom); const pushGlobalLoadingEvent = useSetAtom(pushGlobalLoadingEventAtom);
const resolveGlobalLoadingEvent = useSetAtom(resolveGlobalLoadingEventAtom); const resolveGlobalLoadingEvent = useSetAtom(resolveGlobalLoadingEventAtom);
const t = useAFFiNEI18N(); const t = useAFFiNEI18N();
@ -67,29 +66,21 @@ export const useExportPage = (page: Doc) => {
page, page,
type, type,
}); });
pushNotification({ notify.success({
title: t['com.affine.export.success.title'](), title: t['com.affine.export.success.title'](),
message: t['com.affine.export.success.message'](), message: t['com.affine.export.success.message'](),
type: 'success',
}); });
} catch (err) { } catch (err) {
console.error(err); console.error(err);
pushNotification({ notify.error({
title: t['com.affine.export.error.title'](), title: t['com.affine.export.error.title'](),
message: t['com.affine.export.error.message'](), message: t['com.affine.export.error.message'](),
type: 'error',
}); });
} finally { } finally {
resolveGlobalLoadingEvent(globalLoadingID); resolveGlobalLoadingEvent(globalLoadingID);
} }
}, },
[ [page, pushGlobalLoadingEvent, resolveGlobalLoadingEvent, t]
page,
pushGlobalLoadingEvent,
pushNotification,
resolveGlobalLoadingEvent,
t,
]
); );
return onClickHandler; return onClickHandler;

View File

@ -1,4 +1,4 @@
import { pushNotificationAtom } from '@affine/component/notification-center'; import { notify } from '@affine/component';
import { WorkspaceFlavour } from '@affine/env/workspace'; import { WorkspaceFlavour } from '@affine/env/workspace';
import { import {
getWorkspacePublicPagesQuery, getWorkspacePublicPagesQuery,
@ -7,8 +7,9 @@ import {
revokePublicPageMutation, revokePublicPageMutation,
} from '@affine/graphql'; } from '@affine/graphql';
import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { SingleSelectSelectSolidIcon } from '@blocksuite/icons';
import type { PageMode, Workspace } from '@toeverything/infra'; import type { PageMode, Workspace } from '@toeverything/infra';
import { useSetAtom } from 'jotai'; import { cssVar } from '@toeverything/theme';
import { useCallback, useMemo } from 'react'; import { useCallback, useMemo } from 'react';
import { useMutation } from '../use-mutation'; import { useMutation } from '../use-mutation';
@ -69,7 +70,6 @@ export function useIsSharedPage(
enableShare: (mode: PageMode) => void; enableShare: (mode: PageMode) => void;
} { } {
const t = useAFFiNEI18N(); const t = useAFFiNEI18N();
const pushNotification = useSetAtom(pushNotificationAtom);
const { data, mutate } = useQuery({ const { data, mutate } = useQuery({
query: getWorkspacePublicPagesQuery, query: getWorkspacePublicPagesQuery,
variables: { variables: {
@ -103,24 +103,25 @@ export function useIsSharedPage(
enableSharePage({ workspaceId, pageId, mode: publishMode }) enableSharePage({ workspaceId, pageId, mode: publishMode })
.then(() => { .then(() => {
pushNotification({ notify.success({
title: t[notificationToI18nKey['enableSuccessTitle']](), title: t[notificationToI18nKey['enableSuccessTitle']](),
message: t[notificationToI18nKey['enableSuccessMessage']](), message: t[notificationToI18nKey['enableSuccessMessage']](),
type: 'success', style: 'normal',
theme: 'default', icon: (
<SingleSelectSelectSolidIcon color={cssVar('primaryColor')} />
),
}); });
return mutate(); return mutate();
}) })
.catch(e => { .catch(e => {
pushNotification({ notify.error({
title: t[notificationToI18nKey['enableErrorTitle']](), title: t[notificationToI18nKey['enableErrorTitle']](),
message: t[notificationToI18nKey['enableErrorMessage']](), message: t[notificationToI18nKey['enableErrorMessage']](),
type: 'error',
}); });
console.error(e); console.error(e);
}); });
}, },
[enableSharePage, mutate, pageId, pushNotification, t, workspaceId] [enableSharePage, mutate, pageId, t, workspaceId]
); );
const changeShare = useCallback( const changeShare = useCallback(
@ -130,7 +131,7 @@ export function useIsSharedPage(
enableSharePage({ workspaceId, pageId, mode: publishMode }) enableSharePage({ workspaceId, pageId, mode: publishMode })
.then(() => { .then(() => {
pushNotification({ notify.success({
title: t[notificationToI18nKey['changeSuccessTitle']](), title: t[notificationToI18nKey['changeSuccessTitle']](),
message: t[ message: t[
'com.affine.share-menu.confirm-modify-mode.notification.success.message' 'com.affine.share-menu.confirm-modify-mode.notification.success.message'
@ -144,43 +145,43 @@ export function useIsSharedPage(
? t['Edgeless']() ? t['Edgeless']()
: t['Page'](), : t['Page'](),
}), }),
type: 'success', style: 'normal',
theme: 'default', icon: (
<SingleSelectSelectSolidIcon color={cssVar('primaryColor')} />
),
}); });
return mutate(); return mutate();
}) })
.catch(e => { .catch(e => {
pushNotification({ notify.error({
title: t[notificationToI18nKey['changeErrorTitle']](), title: t[notificationToI18nKey['changeErrorTitle']](),
message: t[notificationToI18nKey['changeErrorMessage']](), message: t[notificationToI18nKey['changeErrorMessage']](),
type: 'error',
}); });
console.error(e); console.error(e);
}); });
}, },
[enableSharePage, mutate, pageId, pushNotification, t, workspaceId] [enableSharePage, mutate, pageId, t, workspaceId]
); );
const disableShare = useCallback(() => { const disableShare = useCallback(() => {
disableSharePage({ workspaceId, pageId }) disableSharePage({ workspaceId, pageId })
.then(() => { .then(() => {
pushNotification({ notify.success({
title: t[notificationToI18nKey['disableSuccessTitle']](), title: t[notificationToI18nKey['disableSuccessTitle']](),
message: t[notificationToI18nKey['disableSuccessMessage']](), message: t[notificationToI18nKey['disableSuccessMessage']](),
type: 'success', style: 'normal',
theme: 'default', icon: <SingleSelectSelectSolidIcon color={cssVar('primaryColor')} />,
}); });
return mutate(); return mutate();
}) })
.catch(e => { .catch(e => {
pushNotification({ notify.error({
title: t[notificationToI18nKey['disableErrorTitle']](), title: t[notificationToI18nKey['disableErrorTitle']](),
message: t[notificationToI18nKey['disableErrorMessage']](), message: t[notificationToI18nKey['disableErrorMessage']](),
type: 'error',
}); });
console.error(e); console.error(e);
}); });
}, [disableSharePage, mutate, pageId, pushNotification, t, workspaceId]); }, [disableSharePage, mutate, pageId, t, workspaceId]);
return useMemo( return useMemo(
() => ({ () => ({

View File

@ -1,3 +1,4 @@
import { notify } from '@affine/component';
import { import {
ChangeEmailPage, ChangeEmailPage,
ChangePasswordPage, ChangePasswordPage,
@ -7,7 +8,6 @@ import {
SignInSuccessPage, SignInSuccessPage,
SignUpPage, SignUpPage,
} from '@affine/component/auth-components'; } from '@affine/component/auth-components';
import { pushNotificationAtom } from '@affine/component/notification-center';
import { useCredentialsRequirement } from '@affine/core/hooks/affine/use-server-config'; import { useCredentialsRequirement } from '@affine/core/hooks/affine/use-server-config';
import { import {
changeEmailMutation, changeEmailMutation,
@ -17,7 +17,6 @@ import {
verifyEmailMutation, verifyEmailMutation,
} from '@affine/graphql'; } from '@affine/graphql';
import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { useSetAtom } from 'jotai/react';
import type { ReactElement } from 'react'; import type { ReactElement } from 'react';
import { useCallback } from 'react'; import { useCallback } from 'react';
import type { LoaderFunction } from 'react-router-dom'; import type { LoaderFunction } from 'react-router-dom';
@ -50,7 +49,6 @@ export const AuthPage = (): ReactElement | null => {
const { authType } = useParams(); const { authType } = useParams();
const [searchParams] = useSearchParams(); const [searchParams] = useSearchParams();
const pushNotification = useSetAtom(pushNotificationAtom);
const { trigger: changePassword } = useMutation({ const { trigger: changePassword } = useMutation({
mutation: changePasswordMutation, mutation: changePasswordMutation,
@ -72,20 +70,18 @@ export const AuthPage = (): ReactElement | null => {
// FIXME: There is not notification // FIXME: There is not notification
if (res?.sendVerifyChangeEmail) { if (res?.sendVerifyChangeEmail) {
pushNotification({ notify.success({
title: t['com.affine.auth.sent.verify.email.hint'](), title: t['com.affine.auth.sent.verify.email.hint'](),
type: 'success',
}); });
} else { } else {
pushNotification({ notify.error({
title: t['com.affine.auth.sent.change.email.fail'](), title: t['com.affine.auth.sent.change.email.fail'](),
type: 'error',
}); });
} }
return !!res?.sendVerifyChangeEmail; return !!res?.sendVerifyChangeEmail;
}, },
[pushNotification, searchParams, sendVerifyChangeEmail, t] [searchParams, sendVerifyChangeEmail, t]
); );
const onSetPassword = useCallback( const onSetPassword = useCallback(

View File

@ -1,4 +1,4 @@
import { pushNotificationAtom } from '@affine/component/notification-center'; import { notify } from '@affine/component';
import { import {
AffineShapeIcon, AffineShapeIcon,
useEditCollection, useEditCollection,
@ -17,7 +17,6 @@ import {
ViewLayersIcon, ViewLayersIcon,
} from '@blocksuite/icons'; } from '@blocksuite/icons';
import { useLiveData, useService, Workspace } from '@toeverything/infra'; import { useLiveData, useService, Workspace } from '@toeverything/infra';
import { useSetAtom } from 'jotai';
import { useCallback, useEffect, useState } from 'react'; import { useCallback, useEffect, useState } from 'react';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
@ -70,7 +69,6 @@ export const Component = function CollectionPage() {
const params = useParams(); const params = useParams();
const workspace = useService(Workspace); const workspace = useService(Workspace);
const collection = collections.find(v => v.id === params.collectionId); const collection = collections.find(v => v.id === params.collectionId);
const pushNotification = useSetAtom(pushNotificationAtom);
useEffect(() => { useEffect(() => {
if (!collection) { if (!collection) {
navigate.jumpToSubPath(workspace.id, WorkspaceSubPath.ALL); navigate.jumpToSubPath(workspace.id, WorkspaceSubPath.ALL);
@ -85,17 +83,13 @@ export const Component = function CollectionPage() {
text = `${collection.collection.name} has been deleted`; text = `${collection.collection.name} has been deleted`;
} }
} }
pushNotification({ notify.error({ title: text });
type: 'error',
title: text,
});
} }
}, [ }, [
collection, collection,
collectionService.collectionsTrash$.value, collectionService.collectionsTrash$.value,
navigate, navigate,
params.collectionId, params.collectionId,
pushNotification,
workspace.docCollection, workspace.docCollection,
workspace.id, workspace.id,
]); ]);

View File

@ -1,10 +1,9 @@
import { pushNotificationAtom } from '@affine/component/notification-center'; import { notify } from '@affine/component';
import { useSession } from '@affine/core/hooks/affine/use-current-user'; import { useSession } from '@affine/core/hooks/affine/use-current-user';
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks'; import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
import { affine } from '@affine/electron-api'; import { affine } from '@affine/electron-api';
import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { CLOUD_WORKSPACE_CHANGED_BROADCAST_CHANNEL_KEY } from '@affine/workspace-impl'; import { CLOUD_WORKSPACE_CHANGED_BROADCAST_CHANNEL_KEY } from '@affine/workspace-impl';
import { useSetAtom } from 'jotai';
import type { PropsWithChildren } from 'react'; import type { PropsWithChildren } from 'react';
import { startTransition, useEffect, useRef } from 'react'; import { startTransition, useEffect, useRef } from 'react';
@ -14,7 +13,6 @@ import { mixpanel } from '../utils';
export const CloudSessionProvider = (props: PropsWithChildren) => { export const CloudSessionProvider = (props: PropsWithChildren) => {
const session = useSession(); const session = useSession();
const prevSession = useRef<ReturnType<typeof useSession>>(); const prevSession = useRef<ReturnType<typeof useSession>>();
const pushNotification = useSetAtom(pushNotificationAtom);
const onceSignedInEvents = useOnceSignedInEvents(); const onceSignedInEvents = useOnceSignedInEvents();
const t = useAFFiNEI18N(); const t = useAFFiNEI18N();
@ -39,10 +37,9 @@ export const CloudSessionProvider = (props: PropsWithChildren) => {
session.status === 'authenticated' session.status === 'authenticated'
) { ) {
startTransition(() => refreshAfterSignedInEvents()); startTransition(() => refreshAfterSignedInEvents());
pushNotification({ notify.success({
title: t['com.affine.auth.has.signed'](), title: t['com.affine.auth.has.signed'](),
message: t['com.affine.auth.has.signed.message'](), message: t['com.affine.auth.has.signed.message'](),
type: 'success',
}); });
if (environment.isDesktop) { if (environment.isDesktop) {
@ -51,7 +48,7 @@ export const CloudSessionProvider = (props: PropsWithChildren) => {
} }
prevSession.current = session; prevSession.current = session;
} }
}, [session, prevSession, pushNotification, refreshAfterSignedInEvents, t]); }, [session, prevSession, refreshAfterSignedInEvents, t]);
return props.children; return props.children;
}; };

View File

@ -1,7 +1,6 @@
import { pushNotificationAtom } from '@affine/component/notification-center'; import { notify } from '@affine/component';
import { assertExists } from '@blocksuite/global/utils'; import { assertExists } from '@blocksuite/global/utils';
import { GraphQLError } from 'graphql'; import { GraphQLError } from 'graphql';
import { useSetAtom } from 'jotai';
import type { PropsWithChildren, ReactNode } from 'react'; import type { PropsWithChildren, ReactNode } from 'react';
import { useCallback } from 'react'; import { useCallback } from 'react';
import type { SWRConfiguration } from 'swr'; import type { SWRConfiguration } from 'swr';
@ -11,7 +10,6 @@ const swrConfig: SWRConfiguration = {
suspense: true, suspense: true,
use: [ use: [
useSWRNext => (key, fetcher, config) => { useSWRNext => (key, fetcher, config) => {
const pushNotification = useSetAtom(pushNotificationAtom);
const fetcherWrapper = useCallback( const fetcherWrapper = useCallback(
async (...args: any[]) => { async (...args: any[]) => {
assertExists(fetcher); assertExists(fetcher);
@ -23,18 +21,14 @@ const swrConfig: SWRConfiguration = {
(Array.isArray(e) && e[0] instanceof GraphQLError) (Array.isArray(e) && e[0] instanceof GraphQLError)
) { ) {
const graphQLError = e instanceof GraphQLError ? e : e[0]; const graphQLError = e instanceof GraphQLError ? e : e[0];
pushNotification({ notify.error({
title: 'GraphQL Error', title: 'GraphQL Error',
message: graphQLError.toString(), message: graphQLError.toString(),
key: Date.now().toString(),
type: 'error',
}); });
} else { } else {
pushNotification({ notify.error({
title: 'Error', title: 'Error',
message: e.toString(), message: e.toString(),
key: Date.now().toString(),
type: 'error',
}); });
} }
throw e; throw e;
@ -42,7 +36,7 @@ const swrConfig: SWRConfiguration = {
} }
return d; return d;
}, },
[fetcher, pushNotification] [fetcher]
); );
return useSWRNext(key, fetcher ? fetcherWrapper : fetcher, config); return useSWRNext(key, fetcher ? fetcherWrapper : fetcher, config);
}, },

View File

@ -1,9 +1,9 @@
import '@affine/component/theme/global.css'; import '@affine/component/theme/global.css';
import '@affine/component/theme/theme.css'; import '@affine/component/theme/theme.css';
import { NotificationCenter } from '@affine/component';
import { AffineContext } from '@affine/component/context'; import { AffineContext } from '@affine/component/context';
import { GlobalLoading } from '@affine/component/global-loading'; import { GlobalLoading } from '@affine/component/global-loading';
import { NotificationCenter } from '@affine/component/notification-center';
import { WorkspaceFallback } from '@affine/core/components/workspace'; import { WorkspaceFallback } from '@affine/core/components/workspace';
import { GlobalScopeProvider } from '@affine/core/modules/infra-web/global-scope'; import { GlobalScopeProvider } from '@affine/core/modules/infra-web/global-scope';
import { CloudSessionProvider } from '@affine/core/providers/session-provider'; import { CloudSessionProvider } from '@affine/core/providers/session-provider';

View File

@ -1,9 +1,9 @@
import '@affine/component/theme/global.css'; import '@affine/component/theme/global.css';
import '@affine/component/theme/theme.css'; import '@affine/component/theme/theme.css';
import { NotificationCenter } from '@affine/component';
import { AffineContext } from '@affine/component/context'; import { AffineContext } from '@affine/component/context';
import { GlobalLoading } from '@affine/component/global-loading'; import { GlobalLoading } from '@affine/component/global-loading';
import { NotificationCenter } from '@affine/component/notification-center';
import { WorkspaceFallback } from '@affine/core/components/workspace'; import { WorkspaceFallback } from '@affine/core/components/workspace';
import { GlobalScopeProvider } from '@affine/core/modules/infra-web/global-scope'; import { GlobalScopeProvider } from '@affine/core/modules/infra-web/global-scope';
import { CloudSessionProvider } from '@affine/core/providers/session-provider'; import { CloudSessionProvider } from '@affine/core/providers/session-provider';

View File

@ -27,7 +27,7 @@ test('Create new workspace, then delete it', async ({ page, workspace }) => {
await openWorkspaceSettingPanel(page, 'Test Workspace'); await openWorkspaceSettingPanel(page, 'Test Workspace');
await page.getByTestId('delete-workspace-button').click(); await page.getByTestId('delete-workspace-button').click();
await expect( await expect(
page.getByTestId('affine-notification').first() page.locator('.affine-notification-center').first()
).not.toBeVisible(); ).not.toBeVisible();
const workspaceNameDom = page.getByTestId('workspace-name'); const workspaceNameDom = page.getByTestId('workspace-name');
const currentWorkspaceName = (await workspaceNameDom.evaluate( const currentWorkspaceName = (await workspaceNameDom.evaluate(
@ -38,7 +38,8 @@ test('Create new workspace, then delete it', async ({ page, workspace }) => {
.getByTestId('delete-workspace-input') .getByTestId('delete-workspace-input')
.pressSequentially(currentWorkspaceName); .pressSequentially(currentWorkspaceName);
const promise = page const promise = page
.getByTestId('affine-notification') .locator('.affine-notification-center')
.first()
.waitFor({ state: 'attached' }); .waitFor({ state: 'attached' });
await page.getByTestId('delete-workspace-confirm-button').click(); await page.getByTestId('delete-workspace-confirm-button').click();
await promise; await promise;