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