import {useEffect, useState} from 'react'; import {produce} from 'immer'; import {useNotificationSubscriptions} from './requests/notification-subscriptions'; import {Navbar} from '../../ui/navigation/navbar/navbar'; import {ProgressCircle} from '../../ui/progress/progress-circle'; import {Checkbox} from '../../ui/forms/toggle/checkbox'; import {useUpdateNotificationSettings} from './requests/update-notification-settings'; import {Button} from '../../ui/buttons/button'; import {NotificationSubscriptionGroup} from './notification-subscription'; import {toast} from '../../ui/toast/toast'; import {Trans} from '../../i18n/trans'; import {message} from '../../i18n/message'; type Selection = Record; // {email: true, mobile: true, browser: false} type ChannelSelection = Record; export function NotificationSettingsPage() { const updateSettings = useUpdateNotificationSettings(); const {data, isFetched} = useNotificationSubscriptions(); const [selection, setSelection] = useState(); useEffect(() => { if (data && !selection) { const initialSelection: Selection = {}; const initialValue: ChannelSelection = {}; data.available_channels.forEach(channel => { initialValue[channel] = false; }); data.subscriptions.forEach(group => { group.subscriptions.forEach(subscription => { const backendValue = data.user_selections.find( s => s.notif_id === subscription.notif_id, ); initialSelection[subscription.notif_id] = backendValue?.channels || { ...initialValue, }; }); }); setSelection(initialSelection); } }, [data, selection]); return (
{!isFetched || !data || !selection ? (
) : (
{data.subscriptions.map(group => (
{group.subscriptions.map(subscription => ( ))}
))}
)}
); } interface GroupRowProps { group: NotificationSubscriptionGroup; allChannels: string[]; selection: Selection; setSelection: (value: Selection) => void; } function GroupRow({ group, allChannels, selection, setSelection, }: GroupRowProps) { const toggleAll = (channelName: string, value: boolean) => { const nextState = produce(selection, draftState => { Object.keys(selection).forEach(notifId => { draftState[notifId][channelName] = value; }); }); setSelection(nextState); }; const checkboxes = (
{allChannels.map(channelName => { const allSelected = Object.values(selection).every(s => s[channelName]); const someSelected = !allSelected && Object.values(selection).some(s => s[channelName]); return ( { if (channelName === 'browser') { const granted = await requestBrowserPermission(); toggleAll(channelName, !granted ? false : !allSelected); } else { toggleAll(channelName, !allSelected); } }} > ); })}
); return (
{checkboxes}
); } interface SubscriptionRowProps { subscription: {name: string; notif_id: string}; allChannels: string[]; selection: Selection; setSelection: (value: Selection) => void; } function SubscriptionRow({ subscription, allChannels, selection, setSelection, }: SubscriptionRowProps) { const notifId = subscription.notif_id; const toggleChannel = (channelName: string, value: boolean) => { const nextState = produce(selection, draftState => { draftState[subscription.notif_id][channelName] = value; }); setSelection(nextState); }; return (
{allChannels.map(channelName => ( { const newValue = !selection[notifId][channelName]; if (channelName === 'browser') { const granted = await requestBrowserPermission(); toggleChannel(channelName, !granted ? false : newValue); } else { toggleChannel(channelName, newValue); } }} aria-label={channelName} >
))}
); } function requestBrowserPermission(): Promise { if (Notification.permission === 'granted') { return Promise.resolve(true); } if (Notification.permission === 'denied') { toast.danger( message( 'Notifications blocked. Please enable them for this site from browser settings.', ), ); return Promise.resolve(false); } return Notification.requestPermission().then(permission => { return permission === 'granted'; }); }