import { Check, Email, Language, Smartphone, WarningRounded, } from '@mui/icons-material' import { useRouter } from 'next/router' import { useEffect, useRef, useState } from 'react' import { useForm } from 'react-hook-form' import toast from 'react-hot-toast' import { useTranslation } from 'react-i18next' import { InboxApi, InboxItemType, InboxItemTypeMethod, InboxItemTypeMethodData, InboxUpdateConfig, ModalProps, } from '@dao-dao/types' import { validateEmail } from '@dao-dao/utils' import { useUpdatingRef } from '../../hooks' import { Button } from '../buttons' import { IconButton } from '../icon_buttons' import { Checkbox, InputLabel, Switch, TextInput } from '../inputs' import { Loader } from '../logo' import { Tooltip } from '../tooltip' import { Modal } from './Modal' const TYPE_METHODS: InboxItemTypeMethodData[] = [ { method: InboxItemTypeMethod.Website, i18nKey: 'title.website', Icon: Language, }, { method: InboxItemTypeMethod.Email, i18nKey: 'title.email', Icon: Email, }, { method: InboxItemTypeMethod.Push, i18nKey: 'title.push', Icon: Smartphone, }, ] export type InboxSettingsModalProps = Pick< ModalProps, 'visible' | 'onClose' > & { api: InboxApi // If defined, shows verify button. verify?: () => void } export const InboxSettingsModal = ({ api: { updating, config, loadConfig, updateConfig, resendVerificationEmail, push, }, verify, ...props }: InboxSettingsModalProps) => { const { t } = useTranslation() const router = useRouter() const { register, reset, setValue, getValues, watch } = useForm() const types = watch('types') // Prompt to load config if not loaded yet. const loadingRef = useRef(false) const routerPush = router.push // Memoize so we only load config once. const loadConfigRef = useUpdatingRef(loadConfig) useEffect(() => { ;(async () => { if (props.visible && !config && !loadingRef.current) { loadingRef.current = true try { // Load config. On failure, close modal. if (!(await loadConfigRef.current())) { routerPush('/notifications') toast.error(t('error.loadingData')) } } finally { loadingRef.current = false } } })() }, [props.visible, config, routerPush, t, loadConfigRef]) // Once config is loaded, populate form with config values. useEffect(() => { if (config) { reset({ email: config.email, types: config.types, }) } }, [config, reset]) const [needsSave, setNeedsSave] = useState(false) return ( { if (!config) { return } const data = getValues() const success = await updateConfig({ // Only save email if changed so it doesn't reverify each time. email: data.email !== config.email ? data.email : undefined, types: data.types, }) if (success) { toast.success(t('success.saved')) setNeedsSave(false) } }} > {t('button.save')} ) } header={{ title: t('title.notificationSettings'), }} titleClassName="mb-2" > {config ? ( <>
{push.ready && !push.supported && (

{t('error.browserNotSupported')}

)} {config.pushSubscriptions === 0 ? (

{t('info.noPushNotificationSubscriptions', { count: config.pushSubscriptions, })}

) : ( <>

{t('info.activePushNotificationSubscriptions', { count: config.pushSubscriptions, })}

{config.pushSubscriptions > 1 && ( )} )}
{(!push.ready || push.supported) && ( )}
{config.verified ? ( ) : config.email ? ( { await resendVerificationEmail() toast.success(t('info.emailVerificationResent')) }} variant="ghost" /> ) : null}
setNeedsSave(true)} placeholder="Email..." register={register} type="email" validation={[validateEmail]} />
{verify && !config.verified && ( )}

{t('title.preferences')}

{t('info.inboxConfigPreferencesDescription')}

{Object.values(InboxItemType).map((type) => (

{t(`inboxItemType.${type}.title`)}

{t(`inboxItemType.${type}.description`)}

{TYPE_METHODS.filter( ({ method }) => type in config.typeAllowedMethods && config.typeAllowedMethods[type].includes(method) ).map(({ method, i18nKey, Icon }) => (
{ setValue( `types.${type}`, (types?.[type] ?? 0) ^ method ) setNeedsSave(true) }} readOnly={updating} />
))}
))}
) : ( updating && ( ) )}
) }