about summary refs log tree commit diff
path: root/src/lib/notifications/notifications.ts
blob: dfc9a42b137b5dc181b7eff01eec946b0b6a250f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import * as Notifications from 'expo-notifications'
import {RootStoreModel} from '../../state'
import {resetToTab} from '../../Navigation'
import {devicePlatform, isIOS} from 'platform/detection'
import {track} from 'lib/analytics/analytics'

const SERVICE_DID = (serviceUrl?: string) =>
  serviceUrl?.includes('staging')
    ? 'did:web:api.staging.bsky.dev'
    : 'did:web:api.bsky.app'

export function init(store: RootStoreModel) {
  store.onUnreadNotifications(count => Notifications.setBadgeCountAsync(count))

  store.onSessionLoaded(async () => {
    // request notifications permission once the user has logged in
    const perms = await Notifications.getPermissionsAsync()
    if (!perms.granted) {
      await Notifications.requestPermissionsAsync()
    }

    // register the push token with the server
    const token = await getPushToken()
    if (token) {
      try {
        await store.agent.api.app.bsky.notification.registerPush({
          serviceDid: SERVICE_DID(store.session.data?.service),
          platform: devicePlatform,
          token: token.data,
          appId: 'xyz.blueskyweb.app',
        })
        store.log.debug('Notifications: Sent push token (init)', {
          type: token.type,
          token: token.data,
        })
      } catch (error) {
        store.log.error('Notifications: Failed to set push token', error)
      }
    }

    // listens for new changes to the push token
    // 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.
    Notifications.addPushTokenListener(async ({data: t, type}) => {
      store.log.debug('Notifications: Push token changed', {t, type})
      if (t) {
        try {
          await store.agent.api.app.bsky.notification.registerPush({
            serviceDid: SERVICE_DID(store.session.data?.service),
            platform: devicePlatform,
            token: t,
            appId: 'xyz.blueskyweb.app',
          })
          store.log.debug('Notifications: Sent push token (event)', {
            type,
            token: t,
          })
        } catch (error) {
          store.log.error('Notifications: Failed to set push token', error)
        }
      }
    })
  })

  // handle notifications that are received, both in the foreground or background
  Notifications.addNotificationReceivedListener(event => {
    store.log.debug('Notifications: received', event)
    if (event.request.trigger.type === 'push') {
      // refresh notifications in the background
      store.me.notifications.syncQueue()
      // handle payload-based deeplinks
      let payload
      if (isIOS) {
        payload = event.request.trigger.payload
      } else {
        // TODO: handle android payload deeplink
      }
      if (payload) {
        store.log.debug('Notifications: received payload', payload)
        // TODO: deeplink notif here
      }
    }
  })

  // handle notifications that are tapped on
  const sub = Notifications.addNotificationResponseReceivedListener(
    response => {
      store.log.debug(
        'Notifications: response received',
        response.actionIdentifier,
      )
      if (
        response.actionIdentifier === Notifications.DEFAULT_ACTION_IDENTIFIER
      ) {
        store.log.debug(
          'User pressed a notification, opening notifications tab',
        )
        track('Notificatons:OpenApp')
        store.me.notifications.refresh() // refresh notifications
        resetToTab('NotificationsTab') // open notifications tab
      }
    },
  )

  return () => {
    sub.remove()
  }
}

export function getPushToken() {
  return Notifications.getDevicePushTokenAsync()
}