Bluesky app fork with some witchin' additions 馃挮
at linkat-integration 201 lines 6.1 kB view raw
1import {useMemo} from 'react' 2import {View} from 'react-native' 3import {type AppBskyNotificationDefs} from '@atproto/api' 4import {msg, Trans} from '@lingui/macro' 5import {useLingui} from '@lingui/react' 6 7import {useNotificationSettingsUpdateMutation} from '#/state/queries/notifications/settings' 8import {atoms as a, platform, useTheme} from '#/alf' 9import * as Toggle from '#/components/forms/Toggle' 10import {Loader} from '#/components/Loader' 11import {Text} from '#/components/Typography' 12import {useAnalytics} from '#/analytics' 13import {Divider} from '../../components/SettingsList' 14 15export function PreferenceControls({ 16 name, 17 syncOthers, 18 preference, 19 allowDisableInApp = true, 20}: { 21 name: Exclude<keyof AppBskyNotificationDefs.Preferences, '$type'> 22 /** 23 * Keep other prefs in sync with `name`. For use in the "everything else" category 24 * which groups starterpack joins + verified + unverified notifications into a single toggle. 25 */ 26 syncOthers?: Exclude<keyof AppBskyNotificationDefs.Preferences, '$type'>[] 27 preference?: 28 | AppBskyNotificationDefs.Preference 29 | AppBskyNotificationDefs.FilterablePreference 30 allowDisableInApp?: boolean 31}) { 32 if (!preference) 33 return ( 34 <View style={[a.w_full, a.pt_5xl, a.align_center]}> 35 <Loader size="xl" /> 36 </View> 37 ) 38 39 return ( 40 <Inner 41 name={name} 42 syncOthers={syncOthers} 43 preference={preference} 44 allowDisableInApp={allowDisableInApp} 45 /> 46 ) 47} 48 49export function Inner({ 50 name, 51 syncOthers = [], 52 preference, 53 allowDisableInApp, 54}: { 55 name: Exclude<keyof AppBskyNotificationDefs.Preferences, '$type'> 56 syncOthers?: Exclude<keyof AppBskyNotificationDefs.Preferences, '$type'>[] 57 preference: 58 | AppBskyNotificationDefs.Preference 59 | AppBskyNotificationDefs.FilterablePreference 60 allowDisableInApp: boolean 61}) { 62 const t = useTheme() 63 const {_} = useLingui() 64 const ax = useAnalytics() 65 const {mutate} = useNotificationSettingsUpdateMutation() 66 67 const channels = useMemo(() => { 68 const arr = [] 69 if (preference.list) arr.push('list') 70 if (preference.push) arr.push('push') 71 return arr 72 }, [preference]) 73 74 const onChangeChannels = (change: string[]) => { 75 const newPreference = { 76 ...preference, 77 list: change.includes('list'), 78 push: change.includes('push'), 79 } satisfies typeof preference 80 81 ax.metric('activityPreference:changeChannels', { 82 name, 83 push: newPreference.push, 84 list: newPreference.list, 85 }) 86 87 mutate({ 88 [name]: newPreference, 89 ...Object.fromEntries(syncOthers.map(key => [key, newPreference])), 90 }) 91 } 92 93 const onChangeFilter = ([change]: string[]) => { 94 if (change !== 'all' && change !== 'follows') 95 throw new Error('Invalid filter') 96 97 const newPreference = { 98 ...preference, 99 include: change, 100 } satisfies typeof preference 101 102 ax.metric('activityPreference:changeFilter', {name, value: change}) 103 104 mutate({ 105 [name]: newPreference, 106 ...Object.fromEntries(syncOthers.map(key => [key, newPreference])), 107 }) 108 } 109 110 return ( 111 <View style={[a.px_xl, a.pt_md, a.gap_sm]}> 112 <Toggle.Group 113 type="checkbox" 114 label={_(msg`Select your preferred notification channels`)} 115 values={channels} 116 onChange={onChangeChannels}> 117 <View style={[a.gap_sm]}> 118 <Toggle.Item 119 label={_(msg`Receive push notifications`)} 120 name="push" 121 style={[ 122 a.py_xs, 123 platform({ 124 native: [a.justify_between], 125 web: [a.flex_row_reverse, a.gap_sm], 126 }), 127 ]}> 128 <Toggle.LabelText 129 style={[t.atoms.text, a.font_normal, a.text_md, a.flex_1]}> 130 <Trans>Push notifications</Trans> 131 </Toggle.LabelText> 132 <Toggle.Platform /> 133 </Toggle.Item> 134 {allowDisableInApp && ( 135 <Toggle.Item 136 label={_(msg`Receive in-app notifications`)} 137 name="list" 138 style={[ 139 a.py_xs, 140 platform({ 141 native: [a.justify_between], 142 web: [a.flex_row_reverse, a.gap_sm], 143 }), 144 ]}> 145 <Toggle.LabelText 146 style={[t.atoms.text, a.font_normal, a.text_md, a.flex_1]}> 147 <Trans>In-app notifications</Trans> 148 </Toggle.LabelText> 149 <Toggle.Platform /> 150 </Toggle.Item> 151 )} 152 </View> 153 </Toggle.Group> 154 {'include' in preference && ( 155 <> 156 <Divider /> 157 <Text style={[a.font_semi_bold, a.text_md]}> 158 <Trans>From</Trans> 159 </Text> 160 <Toggle.Group 161 type="radio" 162 label={_(msg`Filter who you receive notifications from`)} 163 values={[preference.include]} 164 onChange={onChangeFilter} 165 disabled={channels.length === 0}> 166 <View style={[a.gap_sm]}> 167 <Toggle.Item 168 label={_(msg`Everyone`)} 169 name="all" 170 style={[a.flex_row, a.py_xs, a.gap_sm]}> 171 <Toggle.Radio /> 172 <Toggle.LabelText 173 style={[ 174 channels.length > 0 && t.atoms.text, 175 a.font_normal, 176 a.text_md, 177 ]}> 178 <Trans>Everyone</Trans> 179 </Toggle.LabelText> 180 </Toggle.Item> 181 <Toggle.Item 182 label={_(msg`People I follow`)} 183 name="follows" 184 style={[a.flex_row, a.py_xs, a.gap_sm]}> 185 <Toggle.Radio /> 186 <Toggle.LabelText 187 style={[ 188 channels.length > 0 && t.atoms.text, 189 a.font_normal, 190 a.text_md, 191 ]}> 192 <Trans>People I follow</Trans> 193 </Toggle.LabelText> 194 </Toggle.Item> 195 </View> 196 </Toggle.Group> 197 </> 198 )} 199 </View> 200 ) 201}