mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
0
fork

Configure Feed

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

at rm-patch-drawer 153 lines 5.1 kB view raw
1import React from 'react' 2import * as Notifications from 'expo-notifications' 3import {getBadgeCountAsync, setBadgeCountAsync} from 'expo-notifications' 4import {BskyAgent} from '@atproto/api' 5 6import {logEvent} from '#/lib/statsig/statsig' 7import {logger} from '#/logger' 8import {devicePlatform, isAndroid, isNative} from '#/platform/detection' 9import {SessionAccount, useAgent, useSession} from '#/state/session' 10import BackgroundNotificationHandler from '../../../modules/expo-background-notification-handler' 11 12const SERVICE_DID = (serviceUrl?: string) => 13 serviceUrl?.includes('staging') 14 ? 'did:web:api.staging.bsky.dev' 15 : 'did:web:api.bsky.app' 16 17async function registerPushToken( 18 agent: BskyAgent, 19 account: SessionAccount, 20 token: Notifications.DevicePushToken, 21) { 22 try { 23 await agent.api.app.bsky.notification.registerPush({ 24 serviceDid: SERVICE_DID(account.service), 25 platform: devicePlatform, 26 token: token.data, 27 appId: 'xyz.blueskyweb.app', 28 }) 29 logger.debug( 30 'Notifications: Sent push token (init)', 31 { 32 tokenType: token.type, 33 token: token.data, 34 }, 35 logger.DebugContext.notifications, 36 ) 37 } catch (error) { 38 logger.error('Notifications: Failed to set push token', {message: error}) 39 } 40} 41 42async function getPushToken(skipPermissionCheck = false) { 43 const granted = 44 skipPermissionCheck || (await Notifications.getPermissionsAsync()).granted 45 if (granted) { 46 return Notifications.getDevicePushTokenAsync() 47 } 48} 49 50export function useNotificationsRegistration() { 51 const agent = useAgent() 52 const {currentAccount} = useSession() 53 54 React.useEffect(() => { 55 if (!currentAccount) { 56 return 57 } 58 59 // HACK - see https://github.com/bluesky-social/social-app/pull/4467 60 // An apparent regression in expo-notifications causes `addPushTokenListener` to not fire on Android whenever the 61 // token changes by calling `getPushToken()`. This is a workaround to ensure we register the token once it is 62 // generated on Android. 63 if (isAndroid) { 64 ;(async () => { 65 const token = await getPushToken() 66 67 // Token will be undefined if we don't have notifications permission 68 if (token) { 69 registerPushToken(agent, currentAccount, token) 70 } 71 })() 72 } else { 73 getPushToken() 74 } 75 76 // According to the Expo docs, there is a chance that the token will change while the app is open in some rare 77 // cases. This will fire `registerPushToken` whenever that happens. 78 const subscription = Notifications.addPushTokenListener(async newToken => { 79 registerPushToken(agent, currentAccount, newToken) 80 }) 81 82 return () => { 83 subscription.remove() 84 } 85 }, [currentAccount, agent]) 86} 87 88export function useRequestNotificationsPermission() { 89 const {currentAccount} = useSession() 90 const agent = useAgent() 91 92 return async ( 93 context: 'StartOnboarding' | 'AfterOnboarding' | 'Login' | 'Home', 94 ) => { 95 const permissions = await Notifications.getPermissionsAsync() 96 97 if ( 98 !isNative || 99 permissions?.status === 'granted' || 100 (permissions?.status === 'denied' && !permissions.canAskAgain) 101 ) { 102 return 103 } 104 if (context === 'AfterOnboarding') { 105 return 106 } 107 if (context === 'Home' && !currentAccount) { 108 return 109 } 110 111 const res = await Notifications.requestPermissionsAsync() 112 logEvent('notifications:request', { 113 context: context, 114 status: res.status, 115 }) 116 117 if (res.granted) { 118 // This will fire a pushTokenEvent, which will handle registration of the token 119 const token = await getPushToken(true) 120 121 // Same hack as above. We cannot rely on the `addPushTokenListener` to fire on Android due to an Expo bug, so we 122 // will manually register it here. Note that this will occur only: 123 // 1. right after the user signs in, leading to no `currentAccount` account being available - this will be instead 124 // picked up from the useEffect above on `currentAccount` change 125 // 2. right after onboarding. In this case, we _need_ this registration, since `currentAccount` will not change 126 // and we need to ensure the token is registered right after permission is granted. `currentAccount` will already 127 // be available in this case, so the registration will succeed. 128 // We should remove this once expo-notifications (and possibly FCMv1) is fixed and the `addPushTokenListener` is 129 // working again. See https://github.com/expo/expo/issues/28656 130 if (isAndroid && currentAccount && token) { 131 registerPushToken(agent, currentAccount, token) 132 } 133 } 134 } 135} 136 137export async function decrementBadgeCount(by: number) { 138 if (!isNative) return 139 140 let count = await getBadgeCountAsync() 141 count -= by 142 if (count < 0) { 143 count = 0 144 } 145 146 await BackgroundNotificationHandler.setBadgeCountAsync(count) 147 await setBadgeCountAsync(count) 148} 149 150export async function resetBadgeCount() { 151 await BackgroundNotificationHandler.setBadgeCountAsync(0) 152 await setBadgeCountAsync(0) 153}