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

Configure Feed

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

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