Granular notification settings (#8484)
* add mockup screen * add notification index screen * add redirect screen * upgrade sdk * new icons * add new screens * make router typesafe, finish adding screens * add routes to go server * load settings * push notif settings * improve web * fix lockfile lint * no $type on preferences * prompt to enable push notifications * fix reply prefs * space out options * fix copy error * Update RepostsOnRepostsNotificationSettings.tsx * only send minimal diff to putPrefs * fix yarn.lock * Update Navigation.tsx Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * Update src/screens/Settings/NotificationSettings/index.tsx Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * add description to `syncOthers` --------- Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,194 @@
|
||||
import {useMemo} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {type AppBskyNotificationDefs} from '@atproto/api'
|
||||
import {type FilterablePreference} from '@atproto/api/dist/client/types/app/bsky/notification/defs'
|
||||
import {msg, Trans} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
import {useNotificationSettingsUpdateMutation} from '#/state/queries/notifications/settings'
|
||||
import {atoms as a, platform, useTheme} from '#/alf'
|
||||
import * as Toggle from '#/components/forms/Toggle'
|
||||
import {Loader} from '#/components/Loader'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {Divider} from '../../components/SettingsList'
|
||||
|
||||
export function PreferenceControls({
|
||||
name,
|
||||
syncOthers,
|
||||
preference,
|
||||
allowDisableInApp = true,
|
||||
}: {
|
||||
name: Exclude<keyof AppBskyNotificationDefs.Preferences, '$type'>
|
||||
/**
|
||||
* Keep other prefs in sync with `name`. For use in the "everything else" category
|
||||
* which groups starterpack joins + verified + unverified notifications into a single toggle.
|
||||
*/
|
||||
syncOthers?: Exclude<keyof AppBskyNotificationDefs.Preferences, '$type'>[]
|
||||
preference?: AppBskyNotificationDefs.Preference | FilterablePreference
|
||||
allowDisableInApp?: boolean
|
||||
}) {
|
||||
if (!preference)
|
||||
return (
|
||||
<View style={[a.w_full, a.pt_5xl, a.align_center]}>
|
||||
<Loader size="xl" />
|
||||
</View>
|
||||
)
|
||||
|
||||
return (
|
||||
<Inner
|
||||
name={name}
|
||||
syncOthers={syncOthers}
|
||||
preference={preference}
|
||||
allowDisableInApp={allowDisableInApp}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export function Inner({
|
||||
name,
|
||||
syncOthers = [],
|
||||
preference,
|
||||
allowDisableInApp,
|
||||
}: {
|
||||
name: Exclude<keyof AppBskyNotificationDefs.Preferences, '$type'>
|
||||
syncOthers?: Exclude<keyof AppBskyNotificationDefs.Preferences, '$type'>[]
|
||||
preference: AppBskyNotificationDefs.Preference | FilterablePreference
|
||||
allowDisableInApp: boolean
|
||||
}) {
|
||||
const t = useTheme()
|
||||
const {_} = useLingui()
|
||||
const {mutate} = useNotificationSettingsUpdateMutation()
|
||||
|
||||
const channels = useMemo(() => {
|
||||
const arr = []
|
||||
if (preference.list) arr.push('list')
|
||||
if (preference.push) arr.push('push')
|
||||
return arr
|
||||
}, [preference])
|
||||
|
||||
const onChangeChannels = (change: string[]) => {
|
||||
const newPreference = {
|
||||
...preference,
|
||||
list: change.includes('list'),
|
||||
push: change.includes('push'),
|
||||
} satisfies typeof preference
|
||||
|
||||
mutate({
|
||||
[name]: newPreference,
|
||||
...Object.fromEntries(syncOthers.map(key => [key, newPreference])),
|
||||
})
|
||||
}
|
||||
|
||||
const onChangeFilter = ([change]: string[]) => {
|
||||
if (change !== 'all' && change !== 'follows')
|
||||
throw new Error('Invalid filter')
|
||||
|
||||
const newPreference = {
|
||||
...preference,
|
||||
filter: change,
|
||||
} satisfies typeof preference
|
||||
|
||||
mutate({
|
||||
[name]: newPreference,
|
||||
...Object.fromEntries(syncOthers.map(key => [key, newPreference])),
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={[a.px_xl, a.pt_md, a.gap_sm]}>
|
||||
<Toggle.Group
|
||||
type="checkbox"
|
||||
label={_(`Select your preferred notification channels`)}
|
||||
values={channels}
|
||||
onChange={onChangeChannels}>
|
||||
<View style={[a.gap_sm]}>
|
||||
<Toggle.Item
|
||||
label={_(msg`Receive push notifications`)}
|
||||
name="push"
|
||||
style={[
|
||||
a.py_xs,
|
||||
platform({
|
||||
native: [a.justify_between],
|
||||
web: [a.flex_row_reverse, a.gap_md],
|
||||
}),
|
||||
]}>
|
||||
<Toggle.LabelText
|
||||
style={[t.atoms.text, a.font_normal, a.text_md, a.flex_1]}>
|
||||
<Trans>Push notifications</Trans>
|
||||
</Toggle.LabelText>
|
||||
<Toggle.Platform />
|
||||
</Toggle.Item>
|
||||
{allowDisableInApp && (
|
||||
<Toggle.Item
|
||||
label={_(msg`Receive in-app notifications`)}
|
||||
name="list"
|
||||
style={[
|
||||
a.py_xs,
|
||||
platform({
|
||||
native: [a.justify_between],
|
||||
web: [a.flex_row_reverse, a.gap_md],
|
||||
}),
|
||||
]}>
|
||||
<Toggle.LabelText
|
||||
style={[t.atoms.text, a.font_normal, a.text_md, a.flex_1]}>
|
||||
<Trans>In-app notifications</Trans>
|
||||
</Toggle.LabelText>
|
||||
<Toggle.Platform />
|
||||
</Toggle.Item>
|
||||
)}
|
||||
</View>
|
||||
</Toggle.Group>
|
||||
{'filter' in preference && (
|
||||
<>
|
||||
<Divider />
|
||||
<Text style={[a.font_bold, a.text_md]}>From</Text>
|
||||
<Toggle.Group
|
||||
type="radio"
|
||||
label={_('Filter who you receive notifications from')}
|
||||
values={[preference.filter]}
|
||||
onChange={onChangeFilter}
|
||||
disabled={channels.length === 0}>
|
||||
<View style={[a.gap_sm]}>
|
||||
<Toggle.Item
|
||||
label={_(msg`Everyone`)}
|
||||
name="all"
|
||||
style={[
|
||||
a.flex_row,
|
||||
a.py_xs,
|
||||
platform({native: [a.gap_sm], web: [a.gap_md]}),
|
||||
]}>
|
||||
<Toggle.Radio />
|
||||
<Toggle.LabelText
|
||||
style={[
|
||||
channels.length > 0 && t.atoms.text,
|
||||
a.font_normal,
|
||||
a.text_md,
|
||||
]}>
|
||||
<Trans>Everyone</Trans>
|
||||
</Toggle.LabelText>
|
||||
</Toggle.Item>
|
||||
<Toggle.Item
|
||||
label={_(msg`People I follow`)}
|
||||
name="follows"
|
||||
style={[
|
||||
a.flex_row,
|
||||
a.py_xs,
|
||||
platform({native: [a.gap_sm], web: [a.gap_md]}),
|
||||
]}>
|
||||
<Toggle.Radio />
|
||||
<Toggle.LabelText
|
||||
style={[
|
||||
channels.length > 0 && t.atoms.text,
|
||||
a.font_normal,
|
||||
a.text_md,
|
||||
]}>
|
||||
<Trans>People I follow</Trans>
|
||||
</Toggle.LabelText>
|
||||
</Toggle.Item>
|
||||
</View>
|
||||
</Toggle.Group>
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user