mirror of
https://github.com/cinnyapp/cinny.git
synced 2025-02-26 07:03:05 +01:00
Add email notification toggle (#2223)
* refactor system notification to dedicated file * add hook for email notification status * add toogle for email notifications in settings
This commit is contained in:
parent
1b200eb676
commit
59e8d66255
3 changed files with 215 additions and 72 deletions
|
@ -1,82 +1,12 @@
|
|||
import React from 'react';
|
||||
import { Box, Text, IconButton, Icon, Icons, Scroll, Switch, Button, color } from 'folds';
|
||||
import { Box, Text, IconButton, Icon, Icons, Scroll } from 'folds';
|
||||
import { Page, PageContent, PageHeader } from '../../../components/page';
|
||||
import { SequenceCard } from '../../../components/sequence-card';
|
||||
import { SequenceCardStyle } from '../styles.css';
|
||||
import { SettingTile } from '../../../components/setting-tile';
|
||||
import { useSetting } from '../../../state/hooks/settings';
|
||||
import { settingsAtom } from '../../../state/settings';
|
||||
import { getNotificationState, usePermissionState } from '../../../hooks/usePermission';
|
||||
import { SystemNotification } from './SystemNotification';
|
||||
import { AllMessagesNotifications } from './AllMessages';
|
||||
import { SpecialMessagesNotifications } from './SpecialMessages';
|
||||
import { KeywordMessagesNotifications } from './KeywordMessages';
|
||||
import { IgnoredUserList } from './IgnoredUserList';
|
||||
|
||||
function SystemNotification() {
|
||||
const notifPermission = usePermissionState('notifications', getNotificationState());
|
||||
const [showNotifications, setShowNotifications] = useSetting(settingsAtom, 'showNotifications');
|
||||
const [isNotificationSounds, setIsNotificationSounds] = useSetting(
|
||||
settingsAtom,
|
||||
'isNotificationSounds'
|
||||
);
|
||||
|
||||
const requestNotificationPermission = () => {
|
||||
window.Notification.requestPermission();
|
||||
};
|
||||
|
||||
return (
|
||||
<Box direction="Column" gap="100">
|
||||
<Text size="L400">System</Text>
|
||||
<SequenceCard
|
||||
className={SequenceCardStyle}
|
||||
variant="SurfaceVariant"
|
||||
direction="Column"
|
||||
gap="400"
|
||||
>
|
||||
<SettingTile
|
||||
title="Desktop Notifications"
|
||||
description={
|
||||
notifPermission === 'denied' ? (
|
||||
<Text as="span" style={{ color: color.Critical.Main }} size="T200">
|
||||
{'Notification' in window
|
||||
? 'Notification permission is blocked. Please allow notification permission from browser address bar.'
|
||||
: 'Notifications are not supported by the system.'}
|
||||
</Text>
|
||||
) : (
|
||||
<span>Show desktop notifications when message arrive.</span>
|
||||
)
|
||||
}
|
||||
after={
|
||||
notifPermission === 'prompt' ? (
|
||||
<Button size="300" radii="300" onClick={requestNotificationPermission}>
|
||||
<Text size="B300">Enable</Text>
|
||||
</Button>
|
||||
) : (
|
||||
<Switch
|
||||
disabled={notifPermission !== 'granted'}
|
||||
value={showNotifications}
|
||||
onChange={setShowNotifications}
|
||||
/>
|
||||
)
|
||||
}
|
||||
/>
|
||||
</SequenceCard>
|
||||
<SequenceCard
|
||||
className={SequenceCardStyle}
|
||||
variant="SurfaceVariant"
|
||||
direction="Column"
|
||||
gap="400"
|
||||
>
|
||||
<SettingTile
|
||||
title="Notification Sound"
|
||||
description="Play sound when new message arrive."
|
||||
after={<Switch value={isNotificationSounds} onChange={setIsNotificationSounds} />}
|
||||
/>
|
||||
</SequenceCard>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
type NotificationsProps = {
|
||||
requestClose: () => void;
|
||||
};
|
||||
|
|
158
src/app/features/settings/notifications/SystemNotification.tsx
Normal file
158
src/app/features/settings/notifications/SystemNotification.tsx
Normal file
|
@ -0,0 +1,158 @@
|
|||
import React, { useCallback } from 'react';
|
||||
import { Box, Text, Switch, Button, color, Spinner } from 'folds';
|
||||
import { IPusherRequest } from 'matrix-js-sdk';
|
||||
import { SequenceCard } from '../../../components/sequence-card';
|
||||
import { SequenceCardStyle } from '../styles.css';
|
||||
import { SettingTile } from '../../../components/setting-tile';
|
||||
import { useSetting } from '../../../state/hooks/settings';
|
||||
import { settingsAtom } from '../../../state/settings';
|
||||
import { getNotificationState, usePermissionState } from '../../../hooks/usePermission';
|
||||
import { useEmailNotifications } from '../../../hooks/useEmailNotifications';
|
||||
import { AsyncStatus, useAsyncCallback } from '../../../hooks/useAsyncCallback';
|
||||
import { useMatrixClient } from '../../../hooks/useMatrixClient';
|
||||
|
||||
function EmailNotification() {
|
||||
const mx = useMatrixClient();
|
||||
const [result, refreshResult] = useEmailNotifications();
|
||||
|
||||
const [setState, setEnable] = useAsyncCallback(
|
||||
useCallback(
|
||||
async (email: string, enable: boolean) => {
|
||||
if (enable) {
|
||||
await mx.setPusher({
|
||||
kind: 'email',
|
||||
app_id: 'm.email',
|
||||
pushkey: email,
|
||||
app_display_name: 'Email Notifications',
|
||||
device_display_name: email,
|
||||
lang: 'en',
|
||||
data: {
|
||||
brand: 'Cinny',
|
||||
},
|
||||
append: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
await mx.setPusher({
|
||||
pushkey: email,
|
||||
app_id: 'm.email',
|
||||
kind: null,
|
||||
} as unknown as IPusherRequest);
|
||||
},
|
||||
[mx]
|
||||
)
|
||||
);
|
||||
|
||||
const handleChange = (value: boolean) => {
|
||||
if (result && result.email) {
|
||||
setEnable(result.email, value).then(() => {
|
||||
refreshResult();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<SettingTile
|
||||
title="Email Notification"
|
||||
description={
|
||||
<>
|
||||
{result && !result.email && (
|
||||
<Text as="span" style={{ color: color.Critical.Main }} size="T200">
|
||||
Your account does not have any email attached.
|
||||
</Text>
|
||||
)}
|
||||
{result && result.email && <>Send notification to your email. {`("${result.email}")`}</>}
|
||||
{result === null && (
|
||||
<Text as="span" style={{ color: color.Critical.Main }} size="T200">
|
||||
Unexpected Error!
|
||||
</Text>
|
||||
)}
|
||||
{result === undefined && 'Send notification to your email.'}
|
||||
</>
|
||||
}
|
||||
after={
|
||||
<>
|
||||
{setState.status !== AsyncStatus.Loading &&
|
||||
typeof result === 'object' &&
|
||||
result?.email && <Switch value={result.enabled} onChange={handleChange} />}
|
||||
{(setState.status === AsyncStatus.Loading || result === undefined) && (
|
||||
<Spinner variant="Secondary" />
|
||||
)}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function SystemNotification() {
|
||||
const notifPermission = usePermissionState('notifications', getNotificationState());
|
||||
const [showNotifications, setShowNotifications] = useSetting(settingsAtom, 'showNotifications');
|
||||
const [isNotificationSounds, setIsNotificationSounds] = useSetting(
|
||||
settingsAtom,
|
||||
'isNotificationSounds'
|
||||
);
|
||||
|
||||
const requestNotificationPermission = () => {
|
||||
window.Notification.requestPermission();
|
||||
};
|
||||
|
||||
return (
|
||||
<Box direction="Column" gap="100">
|
||||
<Text size="L400">System</Text>
|
||||
<SequenceCard
|
||||
className={SequenceCardStyle}
|
||||
variant="SurfaceVariant"
|
||||
direction="Column"
|
||||
gap="400"
|
||||
>
|
||||
<SettingTile
|
||||
title="Desktop Notifications"
|
||||
description={
|
||||
notifPermission === 'denied' ? (
|
||||
<Text as="span" style={{ color: color.Critical.Main }} size="T200">
|
||||
{'Notification' in window
|
||||
? 'Notification permission is blocked. Please allow notification permission from browser address bar.'
|
||||
: 'Notifications are not supported by the system.'}
|
||||
</Text>
|
||||
) : (
|
||||
<span>Show desktop notifications when message arrive.</span>
|
||||
)
|
||||
}
|
||||
after={
|
||||
notifPermission === 'prompt' ? (
|
||||
<Button size="300" radii="300" onClick={requestNotificationPermission}>
|
||||
<Text size="B300">Enable</Text>
|
||||
</Button>
|
||||
) : (
|
||||
<Switch
|
||||
disabled={notifPermission !== 'granted'}
|
||||
value={showNotifications}
|
||||
onChange={setShowNotifications}
|
||||
/>
|
||||
)
|
||||
}
|
||||
/>
|
||||
</SequenceCard>
|
||||
<SequenceCard
|
||||
className={SequenceCardStyle}
|
||||
variant="SurfaceVariant"
|
||||
direction="Column"
|
||||
gap="400"
|
||||
>
|
||||
<SettingTile
|
||||
title="Notification Sound"
|
||||
description="Play sound when new message arrive."
|
||||
after={<Switch value={isNotificationSounds} onChange={setIsNotificationSounds} />}
|
||||
/>
|
||||
</SequenceCard>
|
||||
<SequenceCard
|
||||
className={SequenceCardStyle}
|
||||
variant="SurfaceVariant"
|
||||
direction="Column"
|
||||
gap="400"
|
||||
>
|
||||
<EmailNotification />
|
||||
</SequenceCard>
|
||||
</Box>
|
||||
);
|
||||
}
|
55
src/app/hooks/useEmailNotifications.ts
Normal file
55
src/app/hooks/useEmailNotifications.ts
Normal file
|
@ -0,0 +1,55 @@
|
|||
import { useCallback } from 'react';
|
||||
import { AsyncStatus, useAsyncCallbackValue } from './useAsyncCallback';
|
||||
import { useMatrixClient } from './useMatrixClient';
|
||||
|
||||
type RefreshHandler = () => void;
|
||||
|
||||
type EmailNotificationResult = {
|
||||
enabled: boolean;
|
||||
email?: string;
|
||||
};
|
||||
|
||||
export const useEmailNotifications = (): [
|
||||
EmailNotificationResult | undefined | null,
|
||||
RefreshHandler
|
||||
] => {
|
||||
const mx = useMatrixClient();
|
||||
|
||||
const [emailState, refresh] = useAsyncCallbackValue<EmailNotificationResult, Error>(
|
||||
useCallback(async () => {
|
||||
const tpIDs = (await mx.getThreePids())?.threepids;
|
||||
const emailAddresses = tpIDs.filter((id) => id.medium === 'email').map((id) => id.address);
|
||||
if (emailAddresses.length === 0)
|
||||
return {
|
||||
enabled: false,
|
||||
};
|
||||
|
||||
const pushers = (await mx.getPushers())?.pushers;
|
||||
const emailPusher = pushers.find(
|
||||
(pusher) => pusher.app_id === 'm.email' && emailAddresses.includes(pusher.pushkey)
|
||||
);
|
||||
|
||||
if (emailPusher?.pushkey) {
|
||||
return {
|
||||
enabled: true,
|
||||
email: emailPusher.pushkey,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
enabled: false,
|
||||
email: emailAddresses[0],
|
||||
};
|
||||
}, [mx])
|
||||
);
|
||||
|
||||
if (emailState.status === AsyncStatus.Success) {
|
||||
return [emailState.data, refresh];
|
||||
}
|
||||
|
||||
if (emailState.status === AsyncStatus.Error) {
|
||||
return [null, refresh];
|
||||
}
|
||||
|
||||
return [undefined, refresh];
|
||||
};
|
Loading…
Reference in a new issue