feat(core): add notifications settings (#11004)

This commit is contained in:
EYHN
2025-03-20 03:52:56 +00:00
parent 46ed76ecb0
commit 120e193c58
14 changed files with 334 additions and 6 deletions
@@ -8,6 +8,7 @@ import {
FolderIcon,
InformationIcon,
KeyboardIcon,
NotificationIcon,
PenIcon,
} from '@blocksuite/icons/rc';
import { useLiveData, useServices } from '@toeverything/infra';
@@ -22,6 +23,7 @@ import { BillingSettings } from './billing';
import { EditorSettings } from './editor';
import { ExperimentalFeatures } from './experimental-features';
import { PaymentIcon, UpgradeIcon } from './icons';
import { NotificationSettings } from './notifications';
import { AFFiNEPricingPlans } from './plans';
import { Shortcuts } from './shortcuts';
@@ -37,6 +39,7 @@ export const useGeneralSettingList = (): GeneralSettingList => {
FeatureFlagService,
});
const status = useLiveData(authService.session.status$);
const loggedIn = status === 'authenticated';
const hasPaymentFeature = useLiveData(
serverService.server.features$.map(f => f?.payment)
);
@@ -62,6 +65,14 @@ export const useGeneralSettingList = (): GeneralSettingList => {
testId: 'shortcuts-panel-trigger',
},
];
if (loggedIn) {
settings.push({
key: 'notifications',
title: t['com.affine.setting.notifications'](),
icon: <NotificationIcon />,
testId: 'notifications-panel-trigger',
});
}
if (enableEditorSettings) {
// add editor settings to second position
settings.splice(1, 0, {
@@ -73,14 +84,14 @@ export const useGeneralSettingList = (): GeneralSettingList => {
}
if (hasPaymentFeature) {
settings.splice(3, 0, {
settings.splice(4, 0, {
key: 'plans',
title: t['com.affine.payment.title'](),
icon: <UpgradeIcon />,
testId: 'plans-panel-trigger',
});
if (status === 'authenticated') {
settings.splice(3, 0, {
if (loggedIn) {
settings.splice(4, 0, {
key: 'billing',
title: t['com.affine.payment.billing-setting.title'](),
icon: <PaymentIcon />,
@@ -130,6 +141,8 @@ export const GeneralSetting = ({
switch (activeTab) {
case 'shortcuts':
return <Shortcuts />;
case 'notifications':
return <NotificationSettings />;
case 'editor':
return <EditorSettings />;
case 'appearance':
@@ -0,0 +1,101 @@
import { notify, Switch } from '@affine/component';
import {
SettingHeader,
SettingRow,
SettingWrapper,
} from '@affine/component/setting-components';
import {
type UserSettings,
UserSettingsService,
} from '@affine/core/modules/cloud';
import { UserFriendlyError } from '@affine/error';
import { useI18n } from '@affine/i18n';
import { useLiveData, useService } from '@toeverything/infra';
import { useCallback, useEffect, useMemo, useState } from 'react';
import * as styles from './style.css';
export const NotificationSettings = () => {
const t = useI18n();
const userSettingsService = useService(UserSettingsService);
useEffect(() => {
userSettingsService.revalidate();
}, [userSettingsService]);
const userSettings = useLiveData(userSettingsService.userSettings$);
const [isMutating, setIsMutating] = useState(false);
const error = useLiveData(userSettingsService.error$);
const errorMessage = useMemo(() => {
if (error) {
console.log('error', error);
const userFriendlyError = UserFriendlyError.fromAny(error);
return t[`error.${userFriendlyError.name}`](userFriendlyError.data);
}
return null;
}, [error, t]);
const disable = !userSettings || isMutating;
const handleUpdate = useCallback(
(key: keyof UserSettings, value: boolean) => {
setIsMutating(true);
userSettingsService
.updateUserSettings({
[key]: value,
})
.catch(err => {
const userFriendlyError = UserFriendlyError.fromAny(err);
notify.error({
title: t[`error.${userFriendlyError.name}`](userFriendlyError.data),
});
})
.finally(() => {
setIsMutating(false);
});
},
[userSettingsService, t]
);
return (
<>
<SettingHeader
title={t['com.affine.setting.notifications.header.title']()}
/>
<SettingWrapper
title={t['com.affine.setting.notifications.email.title']()}
>
{errorMessage && (
<>
<div className={styles.errorMessage}>{errorMessage}</div>
<br />
</>
)}
<SettingRow
name={t['com.affine.setting.notifications.email.mention.title']()}
desc={t['com.affine.setting.notifications.email.mention.subtitle']()}
>
<Switch
data-testid="notification-email-mention-trigger"
checked={userSettings?.receiveMentionEmail ?? false}
disabled={disable}
onChange={checked => handleUpdate('receiveMentionEmail', checked)}
/>
</SettingRow>
<SettingRow
name={t['com.affine.setting.notifications.email.invites.title']()}
desc={t['com.affine.setting.notifications.email.invites.subtitle']()}
>
<Switch
data-testid="notification-email-invites-trigger"
checked={userSettings?.receiveInvitationEmail ?? false}
disabled={disable}
onChange={checked =>
handleUpdate('receiveInvitationEmail', checked)
}
/>
</SettingRow>
</SettingWrapper>
</>
);
};
@@ -0,0 +1,6 @@
import { cssVar } from '@toeverything/theme';
import { style } from '@vanilla-extract/css';
export const errorMessage = style({
color: cssVar('errorColor'),
});