Bluesky app fork with some witchin' additions 馃挮
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 293 lines 12 kB view raw
1import {useEffect} from 'react' 2import {Linking, View} from 'react-native' 3import * as Notification from 'expo-notifications' 4import {type AppBskyNotificationDefs} from '@atproto/api' 5import {msg} from '@lingui/core/macro' 6import {useLingui} from '@lingui/react' 7import {Trans} from '@lingui/react/macro' 8import {useQuery, useQueryClient} from '@tanstack/react-query' 9 10import {useAppState} from '#/lib/appState' 11import { 12 type AllNavigatorParams, 13 type NativeStackScreenProps, 14} from '#/lib/routes/types' 15import {useNotificationSettingsQuery} from '#/state/queries/notifications/settings' 16import {atoms as a} from '#/alf' 17import {Admonition} from '#/components/Admonition' 18import {At_Stroke2_Corner2_Rounded as AtIcon} from '#/components/icons/At' 19import {BellRinging_Stroke2_Corner0_Rounded as BellRingingIcon} from '#/components/icons/BellRinging' 20import {Bubble_Stroke2_Corner2_Rounded as BubbleIcon} from '#/components/icons/Bubble' 21import {Haptic_Stroke2_Corner2_Rounded as HapticIcon} from '#/components/icons/Haptic' 22import { 23 Heart2_Stroke2_Corner0_Rounded as HeartIcon, 24 LikeRepost_Stroke2_Corner2_Rounded as LikeRepostIcon, 25} from '#/components/icons/Heart2' 26import {PersonPlus_Stroke2_Corner2_Rounded as PersonPlusIcon} from '#/components/icons/Person' 27import {CloseQuote_Stroke2_Corner0_Rounded as CloseQuoteIcon} from '#/components/icons/Quote' 28import { 29 Repost_Stroke2_Corner2_Rounded as RepostIcon, 30 RepostRepost_Stroke2_Corner2_Rounded as RepostRepostIcon, 31} from '#/components/icons/Repost' 32import {Shapes_Stroke2_Corner0_Rounded as ShapesIcon} from '#/components/icons/Shapes' 33import * as Layout from '#/components/Layout' 34import {IS_ANDROID, IS_IOS, IS_WEB} from '#/env' 35import * as SettingsList from '../components/SettingsList' 36import {ItemTextWithSubtitle} from './components/ItemTextWithSubtitle' 37 38const RQKEY = ['notification-permissions'] 39 40type Props = NativeStackScreenProps<AllNavigatorParams, 'NotificationSettings'> 41export function NotificationSettingsScreen({}: Props) { 42 const {_} = useLingui() 43 const queryClient = useQueryClient() 44 const {data: settings, isError} = useNotificationSettingsQuery() 45 46 const {data: permissions, refetch} = useQuery({ 47 queryKey: RQKEY, 48 queryFn: async () => { 49 if (IS_WEB) return null 50 return await Notification.getPermissionsAsync() 51 }, 52 }) 53 54 const appState = useAppState() 55 useEffect(() => { 56 if (appState === 'active') { 57 refetch() 58 } 59 }, [appState, refetch]) 60 61 const onRequestPermissions = async () => { 62 if (IS_WEB) return 63 if (permissions?.canAskAgain) { 64 const response = await Notification.requestPermissionsAsync() 65 queryClient.setQueryData(RQKEY, response) 66 } else { 67 if (IS_ANDROID) { 68 try { 69 await Linking.sendIntent( 70 'android.settings.APP_NOTIFICATION_SETTINGS', 71 [ 72 { 73 key: 'android.provider.extra.APP_PACKAGE', 74 value: 'app.witchsky', 75 }, 76 ], 77 ) 78 } catch { 79 Linking.openSettings() 80 } 81 } else if (IS_IOS) { 82 Linking.openSettings() 83 } 84 } 85 } 86 87 return ( 88 <Layout.Screen> 89 <Layout.Header.Outer> 90 <Layout.Header.BackButton /> 91 <Layout.Header.Content> 92 <Layout.Header.TitleText> 93 <Trans>Notifications</Trans> 94 </Layout.Header.TitleText> 95 </Layout.Header.Content> 96 <Layout.Header.Slot /> 97 </Layout.Header.Outer> 98 <Layout.Content> 99 <SettingsList.Container> 100 {permissions && !permissions.granted && ( 101 <> 102 <SettingsList.PressableItem 103 label={_(msg`Enable push notifications`)} 104 onPress={onRequestPermissions}> 105 <SettingsList.ItemIcon icon={HapticIcon} /> 106 <SettingsList.ItemText> 107 <Trans>Enable push notifications</Trans> 108 </SettingsList.ItemText> 109 </SettingsList.PressableItem> 110 <SettingsList.Divider /> 111 </> 112 )} 113 {isError && ( 114 <View style={[a.px_lg, a.pb_md]}> 115 <Admonition type="error"> 116 <Trans>Failed to load notification settings.</Trans> 117 </Admonition> 118 </View> 119 )} 120 <View style={[a.gap_sm]}> 121 <SettingsList.LinkItem 122 label={_(msg`Settings for like notifications`)} 123 to={{screen: 'LikeNotificationSettings'}} 124 contentContainerStyle={[a.align_start]}> 125 <SettingsList.ItemIcon icon={HeartIcon} /> 126 <ItemTextWithSubtitle 127 titleText={<Trans>Likes</Trans>} 128 subtitleText={<SettingPreview preference={settings?.like} />} 129 showSkeleton={!settings} 130 /> 131 </SettingsList.LinkItem> 132 <SettingsList.LinkItem 133 label={_(msg`Settings for new follower notifications`)} 134 to={{screen: 'NewFollowerNotificationSettings'}} 135 contentContainerStyle={[a.align_start]}> 136 <SettingsList.ItemIcon icon={PersonPlusIcon} /> 137 <ItemTextWithSubtitle 138 titleText={<Trans>New followers</Trans>} 139 subtitleText={<SettingPreview preference={settings?.follow} />} 140 showSkeleton={!settings} 141 /> 142 </SettingsList.LinkItem> 143 <SettingsList.LinkItem 144 label={_(msg`Settings for reply notifications`)} 145 to={{screen: 'ReplyNotificationSettings'}} 146 contentContainerStyle={[a.align_start]}> 147 <SettingsList.ItemIcon icon={BubbleIcon} /> 148 <ItemTextWithSubtitle 149 titleText={<Trans>Replies</Trans>} 150 subtitleText={<SettingPreview preference={settings?.reply} />} 151 showSkeleton={!settings} 152 /> 153 </SettingsList.LinkItem> 154 <SettingsList.LinkItem 155 label={_(msg`Settings for mention notifications`)} 156 to={{screen: 'MentionNotificationSettings'}} 157 contentContainerStyle={[a.align_start]}> 158 <SettingsList.ItemIcon icon={AtIcon} /> 159 <ItemTextWithSubtitle 160 titleText={<Trans>Mentions</Trans>} 161 subtitleText={<SettingPreview preference={settings?.mention} />} 162 showSkeleton={!settings} 163 /> 164 </SettingsList.LinkItem> 165 <SettingsList.LinkItem 166 label={_(msg`Settings for quote notifications`)} 167 to={{screen: 'QuoteNotificationSettings'}} 168 contentContainerStyle={[a.align_start]}> 169 <SettingsList.ItemIcon icon={CloseQuoteIcon} /> 170 <ItemTextWithSubtitle 171 titleText={<Trans>Quotes</Trans>} 172 subtitleText={<SettingPreview preference={settings?.quote} />} 173 showSkeleton={!settings} 174 /> 175 </SettingsList.LinkItem> 176 <SettingsList.LinkItem 177 label={_(msg`Settings for repost notifications`)} 178 to={{screen: 'RepostNotificationSettings'}} 179 contentContainerStyle={[a.align_start]}> 180 <SettingsList.ItemIcon icon={RepostIcon} /> 181 <ItemTextWithSubtitle 182 titleText={<Trans>Reposts</Trans>} 183 subtitleText={<SettingPreview preference={settings?.repost} />} 184 showSkeleton={!settings} 185 /> 186 </SettingsList.LinkItem> 187 <SettingsList.LinkItem 188 label={_(msg`Settings for activity from others`)} 189 to={{screen: 'ActivityNotificationSettings'}} 190 contentContainerStyle={[a.align_start]}> 191 <SettingsList.ItemIcon icon={BellRingingIcon} /> 192 <ItemTextWithSubtitle 193 titleText={<Trans>Activity from others</Trans>} 194 subtitleText={ 195 <SettingPreview preference={settings?.subscribedPost} /> 196 } 197 showSkeleton={!settings} 198 /> 199 </SettingsList.LinkItem> 200 <SettingsList.LinkItem 201 label={_( 202 msg`Settings for notifications for likes of your reposts`, 203 )} 204 to={{screen: 'LikesOnRepostsNotificationSettings'}} 205 contentContainerStyle={[a.align_start]}> 206 <SettingsList.ItemIcon icon={LikeRepostIcon} /> 207 <ItemTextWithSubtitle 208 titleText={<Trans>Likes of your reposts</Trans>} 209 subtitleText={ 210 <SettingPreview preference={settings?.likeViaRepost} /> 211 } 212 showSkeleton={!settings} 213 /> 214 </SettingsList.LinkItem> 215 <SettingsList.LinkItem 216 label={_( 217 msg`Settings for notifications for reposts of your reposts`, 218 )} 219 to={{screen: 'RepostsOnRepostsNotificationSettings'}} 220 contentContainerStyle={[a.align_start]}> 221 <SettingsList.ItemIcon icon={RepostRepostIcon} /> 222 <ItemTextWithSubtitle 223 titleText={<Trans>Reposts of your reposts</Trans>} 224 subtitleText={ 225 <SettingPreview preference={settings?.repostViaRepost} /> 226 } 227 showSkeleton={!settings} 228 /> 229 </SettingsList.LinkItem> 230 <SettingsList.LinkItem 231 label={_(msg`Settings for notifications for everything else`)} 232 to={{screen: 'MiscellaneousNotificationSettings'}} 233 contentContainerStyle={[a.align_start]}> 234 <SettingsList.ItemIcon icon={ShapesIcon} /> 235 <ItemTextWithSubtitle 236 titleText={<Trans>Everything else</Trans>} 237 // technically a bundle of several settings, but since they're set together 238 // and are most likely in sync we'll just show the state of one of them 239 subtitleText={ 240 <SettingPreview preference={settings?.starterpackJoined} /> 241 } 242 showSkeleton={!settings} 243 /> 244 </SettingsList.LinkItem> 245 </View> 246 </SettingsList.Container> 247 </Layout.Content> 248 </Layout.Screen> 249 ) 250} 251 252function SettingPreview({ 253 preference, 254}: { 255 preference?: 256 | AppBskyNotificationDefs.Preference 257 | AppBskyNotificationDefs.FilterablePreference 258}) { 259 const {_} = useLingui() 260 if (!preference) { 261 return null 262 } else { 263 if ('include' in preference) { 264 if (preference.include === 'all') { 265 if (preference.list && preference.push) { 266 return _(msg`In-app, Push, Everyone`) 267 } else if (preference.list) { 268 return _(msg`In-app, Everyone`) 269 } else if (preference.push) { 270 return _(msg`Push, Everyone`) 271 } 272 } else if (preference.include === 'follows') { 273 if (preference.list && preference.push) { 274 return _(msg`In-app, Push, People you follow`) 275 } else if (preference.list) { 276 return _(msg`In-app, People you follow`) 277 } else if (preference.push) { 278 return _(msg`Push, People you follow`) 279 } 280 } 281 } else { 282 if (preference.list && preference.push) { 283 return _(msg`In-app, Push`) 284 } else if (preference.list) { 285 return _(msg`In-app`) 286 } else if (preference.push) { 287 return _(msg`Push`) 288 } 289 } 290 } 291 292 return _(msg`Off`) 293}