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;
};
--- /dev/null
+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>
+ );
+}
--- /dev/null
+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];
+};