mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
1import React from 'react'
2import * as Notifications from 'expo-notifications'
3import {getBadgeCountAsync, setBadgeCountAsync} from 'expo-notifications'
4import {BskyAgent} from '@atproto/api'
5
6import {logger} from '#/logger'
7import {SessionAccount, useAgent, useSession} from '#/state/session'
8import {logEvent, useGate} from 'lib/statsig/statsig'
9import {devicePlatform, isNative} from 'platform/detection'
10
11const SERVICE_DID = (serviceUrl?: string) =>
12 serviceUrl?.includes('staging')
13 ? 'did:web:api.staging.bsky.dev'
14 : 'did:web:api.bsky.app'
15
16async function registerPushToken(
17 agent: BskyAgent,
18 account: SessionAccount,
19 token: Notifications.DevicePushToken,
20) {
21 try {
22 await agent.api.app.bsky.notification.registerPush({
23 serviceDid: SERVICE_DID(account.service),
24 platform: devicePlatform,
25 token: token.data,
26 appId: 'xyz.blueskyweb.app',
27 })
28 logger.debug(
29 'Notifications: Sent push token (init)',
30 {
31 tokenType: token.type,
32 token: token.data,
33 },
34 logger.DebugContext.notifications,
35 )
36 } catch (error) {
37 logger.error('Notifications: Failed to set push token', {message: error})
38 }
39}
40
41async function getPushToken(skipPermissionCheck = false) {
42 const granted =
43 skipPermissionCheck || (await Notifications.getPermissionsAsync()).granted
44 if (granted) {
45 Notifications.getDevicePushTokenAsync()
46 }
47}
48
49export function useNotificationsRegistration() {
50 const agent = useAgent()
51 const {currentAccount} = useSession()
52
53 React.useEffect(() => {
54 if (!currentAccount) {
55 return
56 }
57
58 getPushToken()
59
60 // According to the Expo docs, there is a chance that the token will change while the app is open in some rare
61 // cases. This will fire `registerPushToken` whenever that happens.
62 const subscription = Notifications.addPushTokenListener(async newToken => {
63 registerPushToken(agent, currentAccount, newToken)
64 })
65
66 return () => {
67 subscription.remove()
68 }
69 }, [currentAccount, agent])
70}
71
72export function useRequestNotificationsPermission() {
73 const gate = useGate()
74
75 return async (context: 'StartOnboarding' | 'AfterOnboarding' | 'Login') => {
76 const permissions = await Notifications.getPermissionsAsync()
77
78 if (
79 !isNative ||
80 permissions?.status === 'granted' ||
81 permissions?.status === 'denied'
82 ) {
83 return
84 }
85 if (
86 context === 'StartOnboarding' &&
87 gate('request_notifications_permission_after_onboarding_v2')
88 ) {
89 return
90 }
91 if (
92 context === 'AfterOnboarding' &&
93 !gate('request_notifications_permission_after_onboarding_v2')
94 ) {
95 return
96 }
97
98 const res = await Notifications.requestPermissionsAsync()
99 logEvent('notifications:request', {
100 context: context,
101 status: res.status,
102 })
103
104 if (res.granted) {
105 // This will fire a pushTokenEvent, which will handle registration of the token
106 getPushToken(true)
107 }
108 }
109}
110
111export async function decrementBadgeCount(by: number | 'reset' = 1) {
112 if (!isNative) return
113
114 const currCount = await getBadgeCountAsync()
115
116 if (by === 'reset') {
117 await setBadgeCountAsync(0)
118 return
119 }
120
121 let newCount = currCount - by
122 if (newCount < 0) {
123 newCount = 0
124 }
125 await setBadgeCountAsync(newCount)
126}