Align handling across native devices
This commit is contained in:
@@ -6,17 +6,22 @@ import {type AtpAgent} from '@atproto/api'
|
|||||||
import debounce from 'lodash.debounce'
|
import debounce from 'lodash.debounce'
|
||||||
|
|
||||||
import {Logger} from '#/logger'
|
import {Logger} from '#/logger'
|
||||||
import {isAndroid, isNative} from '#/platform/detection'
|
import {isNative} from '#/platform/detection'
|
||||||
import {type SessionAccount, useAgent, useSession} from '#/state/session'
|
import {type SessionAccount, useAgent, useSession} from '#/state/session'
|
||||||
import BackgroundNotificationHandler from '../../../modules/expo-background-notification-handler'
|
import BackgroundNotificationHandler from '../../../modules/expo-background-notification-handler'
|
||||||
|
|
||||||
const logger = Logger.create(Logger.Context.Notifications)
|
const logger = Logger.create(Logger.Context.Notifications)
|
||||||
|
|
||||||
async function registerPushToken(
|
/**
|
||||||
|
* Registers the device's push notification token with the Bluesky server.
|
||||||
|
*/
|
||||||
|
async function _registerPushToken(
|
||||||
agent: AtpAgent,
|
agent: AtpAgent,
|
||||||
account: SessionAccount,
|
account: SessionAccount,
|
||||||
token: Notifications.DevicePushToken,
|
token: Notifications.DevicePushToken,
|
||||||
) {
|
) {
|
||||||
|
logger.debug(`registerPushToken`)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await agent.app.bsky.notification.registerPush({
|
await agent.app.bsky.notification.registerPush({
|
||||||
serviceDid: account.service?.includes('staging')
|
serviceDid: account.service?.includes('staging')
|
||||||
@@ -31,7 +36,7 @@ async function registerPushToken(
|
|||||||
appId: 'xyz.blueskyweb.app',
|
appId: 'xyz.blueskyweb.app',
|
||||||
})
|
})
|
||||||
|
|
||||||
logger.debug(`registerPushToken`, {
|
logger.debug(`registerPushToken: success`, {
|
||||||
tokenType: token.type,
|
tokenType: token.type,
|
||||||
token: token.data,
|
token: token.data,
|
||||||
})
|
})
|
||||||
@@ -40,16 +45,64 @@ async function registerPushToken(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const registerPushTokenDebounced = debounce(registerPushToken, 100)
|
/**
|
||||||
|
* Debounced version of `_registerPushToken` to prevent multiple calls. Use this
|
||||||
|
* instead of using `_registerPushToken` directly.
|
||||||
|
*/
|
||||||
|
const registerPushToken = debounce(_registerPushToken, 100)
|
||||||
|
|
||||||
async function getPushToken(skipPermissionCheck = false) {
|
/**
|
||||||
const granted =
|
* Retreive the device's push notification token, if permissions are granted.
|
||||||
skipPermissionCheck || (await Notifications.getPermissionsAsync()).granted
|
*/
|
||||||
|
async function getPushToken() {
|
||||||
|
const granted = (await Notifications.getPermissionsAsync()).granted
|
||||||
if (granted) {
|
if (granted) {
|
||||||
return Notifications.getDevicePushTokenAsync()
|
return Notifications.getDevicePushTokenAsync()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the device push token and registers it with the Bluesky server.
|
||||||
|
*
|
||||||
|
* N.B. A previous regression in `expo-notifications` caused
|
||||||
|
* `addPushTokenListener` to not fire on Android after calling
|
||||||
|
* `getPushToken()`. Therefore, as insurance, we also call
|
||||||
|
* `registerPushToken` here.
|
||||||
|
*
|
||||||
|
* Because `registerPushToken` is debounced, we can safely call it on every
|
||||||
|
* platform and only a single call will be made to the server.
|
||||||
|
*
|
||||||
|
* @see https://github.com/bluesky-social/social-app/pull/4467
|
||||||
|
* @see https://github.com/expo/expo/issues/28656
|
||||||
|
* @see https://github.com/expo/expo/issues/29909
|
||||||
|
*/
|
||||||
|
async function getAndRegisterPushToken(
|
||||||
|
agent: AtpAgent,
|
||||||
|
currentAccount: SessionAccount,
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
/**
|
||||||
|
* This will also fire the listener added via `addPushTokenListener`. That
|
||||||
|
* listener also handles registration.
|
||||||
|
*/
|
||||||
|
const token = await getPushToken()
|
||||||
|
|
||||||
|
logger.debug(`getAndRegisterPushToken`, {token})
|
||||||
|
|
||||||
|
if (token) {
|
||||||
|
/**
|
||||||
|
* The listener should have registered the token already, but just in
|
||||||
|
* case, call the debounced function again.
|
||||||
|
*/
|
||||||
|
registerPushToken(agent, currentAccount, token)
|
||||||
|
}
|
||||||
|
|
||||||
|
return token
|
||||||
|
} catch (e: any) {
|
||||||
|
logger.error(`getPushToken: failed`, {safeMessage: e.message})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function useNotificationsRegistration() {
|
export function useNotificationsRegistration() {
|
||||||
const agent = useAgent()
|
const agent = useAgent()
|
||||||
const {currentAccount} = useSession()
|
const {currentAccount} = useSession()
|
||||||
@@ -57,26 +110,10 @@ export function useNotificationsRegistration() {
|
|||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (!currentAccount) return
|
if (!currentAccount) return
|
||||||
|
|
||||||
/**
|
logger.debug(`useNotificationsRegistration`)
|
||||||
* HACK — An apparent regression in expo-notifications causes
|
|
||||||
* `addPushTokenListener` to not fire on Android whenever the token changes
|
|
||||||
* by calling `getPushToken()`. This is a workaround to ensure we register
|
|
||||||
* the token once it is generated on Android.
|
|
||||||
*
|
|
||||||
* @see https://github.com/bluesky-social/social-app/pull/4467
|
|
||||||
*/
|
|
||||||
if (isAndroid) {
|
|
||||||
;(async () => {
|
|
||||||
const token = await getPushToken()
|
|
||||||
|
|
||||||
// Token will be undefined if we don't have notifications permission
|
// init push token
|
||||||
if (token) {
|
getAndRegisterPushToken(agent, currentAccount)
|
||||||
registerPushTokenDebounced(agent, currentAccount, token)
|
|
||||||
}
|
|
||||||
})()
|
|
||||||
} else {
|
|
||||||
getPushToken()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* According to the Expo docs, there is a chance that the token will change
|
* According to the Expo docs, there is a chance that the token will change
|
||||||
@@ -86,7 +123,7 @@ export function useNotificationsRegistration() {
|
|||||||
* @see https://docs.expo.dev/versions/latest/sdk/notifications/#addpushtokenlistenerlistener
|
* @see https://docs.expo.dev/versions/latest/sdk/notifications/#addpushtokenlistenerlistener
|
||||||
*/
|
*/
|
||||||
const subscription = Notifications.addPushTokenListener(async newToken => {
|
const subscription = Notifications.addPushTokenListener(async newToken => {
|
||||||
registerPushTokenDebounced(agent, currentAccount, newToken)
|
registerPushToken(agent, currentAccount, newToken)
|
||||||
logger.debug(`addPushTokenListener callback`, {newToken})
|
logger.debug(`addPushTokenListener callback`, {newToken})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -120,22 +157,14 @@ export function useRequestNotificationsPermission() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const res = await Notifications.requestPermissionsAsync()
|
const res = await Notifications.requestPermissionsAsync()
|
||||||
|
|
||||||
logger.metric(`notifications:request`, {
|
logger.metric(`notifications:request`, {
|
||||||
context: context,
|
context: context,
|
||||||
status: res.status,
|
status: res.status,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (res.granted) {
|
|
||||||
/*
|
|
||||||
* This will fire a pushTokenEvent, which will handle registration of the
|
|
||||||
* token
|
|
||||||
*/
|
|
||||||
const token = await getPushToken(true)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Same hack as above. We cannot rely on the `addPushTokenListener` to
|
* Note that this will run:
|
||||||
* fire on Android due to an Expo bug, so we will manually register it
|
|
||||||
* here. Note that this will occur only:
|
|
||||||
*
|
*
|
||||||
* 1) Right after the user signs in, leading to no `currentAccount`
|
* 1) Right after the user signs in, leading to no `currentAccount`
|
||||||
* account being available - this will be instead picked up from the
|
* account being available - this will be instead picked up from the
|
||||||
@@ -148,12 +177,10 @@ export function useRequestNotificationsPermission() {
|
|||||||
* registration will succeed. We should remove this once
|
* registration will succeed. We should remove this once
|
||||||
* expo-notifications (and possibly FCMv1) is fixed and the
|
* expo-notifications (and possibly FCMv1) is fixed and the
|
||||||
* `addPushTokenListener` is working again.
|
* `addPushTokenListener` is working again.
|
||||||
*
|
|
||||||
* @see https://github.com/expo/expo/issues/28656
|
|
||||||
*/
|
*/
|
||||||
if (isAndroid && currentAccount && token) {
|
if (res.granted && currentAccount) {
|
||||||
registerPushTokenDebounced(agent, currentAccount, token)
|
logger.debug(`useRequestNotificationsPermission`, {context})
|
||||||
}
|
getAndRegisterPushToken(agent, currentAccount)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user