mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
1import {useEffect} from 'react'
2import * as Notifications from 'expo-notifications'
3import {BskyAgent} from '@atproto/api'
4import {QueryClient} from '@tanstack/react-query'
5
6import {logger} from '#/logger'
7import {RQKEY as RQKEY_NOTIFS} from '#/state/queries/notifications/feed'
8import {invalidateCachedUnreadPage} from '#/state/queries/notifications/unread'
9import {truncateAndInvalidate} from '#/state/queries/util'
10import {SessionAccount} from '#/state/session'
11import {track} from 'lib/analytics/analytics'
12import {devicePlatform, isIOS} from 'platform/detection'
13import {resetToTab} from '../../Navigation'
14import {logEvent} from '../statsig/statsig'
15
16const SERVICE_DID = (serviceUrl?: string) =>
17 serviceUrl?.includes('staging')
18 ? 'did:web:api.staging.bsky.dev'
19 : 'did:web:api.bsky.app'
20
21export async function requestPermissionsAndRegisterToken(
22 getAgent: () => BskyAgent,
23 account: SessionAccount,
24) {
25 // request notifications permission once the user has logged in
26 const perms = await Notifications.getPermissionsAsync()
27 if (!perms.granted) {
28 await Notifications.requestPermissionsAsync()
29 }
30
31 // register the push token with the server
32 const token = await Notifications.getDevicePushTokenAsync()
33 try {
34 await getAgent().api.app.bsky.notification.registerPush({
35 serviceDid: SERVICE_DID(account.service),
36 platform: devicePlatform,
37 token: token.data,
38 appId: 'xyz.blueskyweb.app',
39 })
40 logger.debug(
41 'Notifications: Sent push token (init)',
42 {
43 tokenType: token.type,
44 token: token.data,
45 },
46 logger.DebugContext.notifications,
47 )
48 } catch (error) {
49 logger.error('Notifications: Failed to set push token', {message: error})
50 }
51}
52
53export function registerTokenChangeHandler(
54 getAgent: () => BskyAgent,
55 account: SessionAccount,
56): () => void {
57 // listens for new changes to the push token
58 // In rare situations, a push token may be changed by the push notification service while the app is running. When a token is rolled, the old one becomes invalid and sending notifications to it will fail. A push token listener will let you handle this situation gracefully by registering the new token with your backend right away.
59 const sub = Notifications.addPushTokenListener(async newToken => {
60 logger.debug(
61 'Notifications: Push token changed',
62 {tokenType: newToken.data, token: newToken.type},
63 logger.DebugContext.notifications,
64 )
65 try {
66 await getAgent().api.app.bsky.notification.registerPush({
67 serviceDid: SERVICE_DID(account.service),
68 platform: devicePlatform,
69 token: newToken.data,
70 appId: 'xyz.blueskyweb.app',
71 })
72 logger.debug(
73 'Notifications: Sent push token (event)',
74 {
75 tokenType: newToken.type,
76 token: newToken.data,
77 },
78 logger.DebugContext.notifications,
79 )
80 } catch (error) {
81 logger.error('Notifications: Failed to set push token', {message: error})
82 }
83 })
84 return () => {
85 sub.remove()
86 }
87}
88
89export function useNotificationsListener(queryClient: QueryClient) {
90 useEffect(() => {
91 // handle notifications that are received, both in the foreground or background
92 // NOTE: currently just here for debug logging
93 const sub1 = Notifications.addNotificationReceivedListener(event => {
94 invalidateCachedUnreadPage()
95 logger.debug(
96 'Notifications: received',
97 {event},
98 logger.DebugContext.notifications,
99 )
100 if (event.request.trigger.type === 'push') {
101 // handle payload-based deeplinks
102 let payload
103 if (isIOS) {
104 payload = event.request.trigger.payload
105 } else {
106 // TODO: handle android payload deeplink
107 }
108 if (payload) {
109 logger.debug(
110 'Notifications: received payload',
111 payload,
112 logger.DebugContext.notifications,
113 )
114 // TODO: deeplink notif here
115 }
116 }
117 })
118
119 // handle notifications that are tapped on
120 const sub2 = Notifications.addNotificationResponseReceivedListener(
121 response => {
122 logger.debug(
123 'Notifications: response received',
124 {
125 actionIdentifier: response.actionIdentifier,
126 },
127 logger.DebugContext.notifications,
128 )
129 if (
130 response.actionIdentifier === Notifications.DEFAULT_ACTION_IDENTIFIER
131 ) {
132 logger.debug(
133 'User pressed a notification, opening notifications tab',
134 {},
135 logger.DebugContext.notifications,
136 )
137 track('Notificatons:OpenApp')
138 logEvent('notifications:openApp', {})
139 invalidateCachedUnreadPage()
140 truncateAndInvalidate(queryClient, RQKEY_NOTIFS())
141 resetToTab('NotificationsTab') // open notifications tab
142 }
143 },
144 )
145
146 return () => {
147 sub1.remove()
148 sub2.remove()
149 }
150 }, [queryClient])
151}