Changeset View
Changeset View
Standalone View
Standalone View
native/push/push-handler.react.js
Show All 25 Lines | import { | ||||
useDispatchActionPromise, | useDispatchActionPromise, | ||||
type DispatchActionPromise, | type DispatchActionPromise, | ||||
} from 'lib/utils/action-utils.js'; | } from 'lib/utils/action-utils.js'; | ||||
import { | import { | ||||
type NotifPermissionAlertInfo, | type NotifPermissionAlertInfo, | ||||
recordNotifPermissionAlertActionType, | recordNotifPermissionAlertActionType, | ||||
shouldSkipPushPermissionAlert, | shouldSkipPushPermissionAlert, | ||||
} from 'lib/utils/push-alerts.js'; | } from 'lib/utils/push-alerts.js'; | ||||
import sleep from 'lib/utils/sleep.js'; | |||||
import { | import { | ||||
androidNotificationChannelID, | androidNotificationChannelID, | ||||
handleAndroidMessage, | handleAndroidMessage, | ||||
getCommAndroidNotificationsEventEmitter, | getCommAndroidNotificationsEventEmitter, | ||||
type AndroidForegroundMessage, | type AndroidForegroundMessage, | ||||
CommAndroidNotifications, | CommAndroidNotifications, | ||||
} from './android.js'; | } from './android.js'; | ||||
▲ Show 20 Lines • Show All 66 Lines • ▼ Show 20 Lines | |||||
class PushHandler extends React.PureComponent<Props, State> { | class PushHandler extends React.PureComponent<Props, State> { | ||||
state: State = { | state: State = { | ||||
inAppNotifProps: null, | inAppNotifProps: null, | ||||
}; | }; | ||||
currentState: ?string = getCurrentLifecycleState(); | currentState: ?string = getCurrentLifecycleState(); | ||||
appStarted = 0; | appStarted = 0; | ||||
androidNotificationsEventSubscriptions: Array<EventSubscription> = []; | androidNotificationsEventSubscriptions: Array<EventSubscription> = []; | ||||
androidNotificationsPermissionPromise: ?Promise<boolean> = undefined; | |||||
initialAndroidNotifHandled = false; | initialAndroidNotifHandled = false; | ||||
openThreadOnceReceived: Set<string> = new Set(); | openThreadOnceReceived: Set<string> = new Set(); | ||||
lifecycleSubscription: ?EventSubscription; | lifecycleSubscription: ?EventSubscription; | ||||
iosNotificationEventSubscriptions: Array<EventSubscription> = []; | iosNotificationEventSubscriptions: Array<EventSubscription> = []; | ||||
componentDidMount() { | componentDidMount() { | ||||
this.appStarted = Date.now(); | this.appStarted = Date.now(); | ||||
this.lifecycleSubscription = addLifecycleListener( | this.lifecycleSubscription = addLifecycleListener( | ||||
this.handleAppStateChange, | this.handleAppStateChange, | ||||
); | ); | ||||
this.onForeground(); | this.onForeground(); | ||||
if (Platform.OS === 'ios') { | if (Platform.OS === 'ios') { | ||||
const commIOSNotificationsEventEmitter = | const commIOSNotificationsEventEmitter = | ||||
getCommIOSNotificationsEventEmitter(); | getCommIOSNotificationsEventEmitter(); | ||||
this.iosNotificationEventSubscriptions.push( | this.iosNotificationEventSubscriptions.push( | ||||
commIOSNotificationsEventEmitter.addListener( | commIOSNotificationsEventEmitter.addListener( | ||||
'remoteNotificationsRegistered', | 'remoteNotificationsRegistered', | ||||
registration => | registration => | ||||
this.registerPushPermissions(registration?.deviceToken), | this.registerPushPermissions(registration?.deviceToken), | ||||
), | ), | ||||
commIOSNotificationsEventEmitter.addListener( | commIOSNotificationsEventEmitter.addListener( | ||||
'remoteNotificationsRegistrationFailed', | 'remoteNotificationsRegistrationFailed', | ||||
this.failedToRegisterPushPermissions, | this.failedToRegisterPushPermissionsIOS, | ||||
), | ), | ||||
commIOSNotificationsEventEmitter.addListener( | commIOSNotificationsEventEmitter.addListener( | ||||
'notificationReceivedForeground', | 'notificationReceivedForeground', | ||||
this.iosForegroundNotificationReceived, | this.iosForegroundNotificationReceived, | ||||
), | ), | ||||
commIOSNotificationsEventEmitter.addListener( | commIOSNotificationsEventEmitter.addListener( | ||||
'notificationOpened', | 'notificationOpened', | ||||
this.iosNotificationOpened, | this.iosNotificationOpened, | ||||
▲ Show 20 Lines • Show All 171 Lines • ▼ Show 20 Lines | if (Platform.OS === 'ios') { | ||||
this.props.deviceToken === null || this.props.deviceToken === undefined; | this.props.deviceToken === null || this.props.deviceToken === undefined; | ||||
await requestIOSPushPermissions(missingDeviceToken); | await requestIOSPushPermissions(missingDeviceToken); | ||||
} else if (Platform.OS === 'android') { | } else if (Platform.OS === 'android') { | ||||
await this.ensureAndroidPushNotifsEnabled(); | await this.ensureAndroidPushNotifsEnabled(); | ||||
} | } | ||||
} | } | ||||
async ensureAndroidPushNotifsEnabled() { | async ensureAndroidPushNotifsEnabled() { | ||||
const hasPermission = await CommAndroidNotifications.hasPermission(); | const permissionPromisesResult = await Promise.all([ | ||||
CommAndroidNotifications.hasPermission(), | |||||
CommAndroidNotifications.canRequestNotificationsPermissionFromUser(), | |||||
]); | |||||
let [hasPermission] = permissionPromisesResult; | |||||
const [, canRequestPermission] = permissionPromisesResult; | |||||
if (!hasPermission && canRequestPermission) { | |||||
const permissionResponse = await (async () => { | |||||
// We issue a call to sleep to match iOS behavior where prompt | |||||
// doesn't appear immediately but after logged-out modal disappears | |||||
await sleep(10); | |||||
await this.requestAndroidNotificationsPermission(); | |||||
})(); | |||||
hasPermission = permissionResponse; | |||||
} | |||||
if (!hasPermission) { | if (!hasPermission) { | ||||
this.failedToRegisterPushPermissions(); | this.failedToRegisterPushPermissionsAndroid(!canRequestPermission); | ||||
return; | return; | ||||
} | } | ||||
try { | try { | ||||
const fcmToken = await CommAndroidNotifications.getToken(); | const fcmToken = await CommAndroidNotifications.getToken(); | ||||
await this.handleAndroidDeviceToken(fcmToken); | await this.handleAndroidDeviceToken(fcmToken); | ||||
} catch (e) { | } catch (e) { | ||||
this.failedToRegisterPushPermissions(); | this.failedToRegisterPushPermissionsAndroid(!canRequestPermission); | ||||
} | } | ||||
} | } | ||||
requestAndroidNotificationsPermission = () => { | |||||
if (!this.androidNotificationsPermissionPromise) { | |||||
this.androidNotificationsPermissionPromise = (async () => { | |||||
const notifPermission = | |||||
await CommAndroidNotifications.requestNotificationsPermission(); | |||||
this.androidNotificationsPermissionPromise = undefined; | |||||
return notifPermission; | |||||
})(); | |||||
} | |||||
return this.androidNotificationsPermissionPromise; | |||||
}; | |||||
handleAndroidDeviceToken = async (deviceToken: string) => { | handleAndroidDeviceToken = async (deviceToken: string) => { | ||||
this.registerPushPermissions(deviceToken); | this.registerPushPermissions(deviceToken); | ||||
await this.handleInitialAndroidNotification(); | await this.handleInitialAndroidNotification(); | ||||
}; | }; | ||||
async handleInitialAndroidNotification() { | async handleInitialAndroidNotification() { | ||||
if (this.initialAndroidNotifHandled) { | if (this.initialAndroidNotifHandled) { | ||||
return; | return; | ||||
Show All 21 Lines | class PushHandler extends React.PureComponent<Props, State> { | ||||
setDeviceToken(deviceToken: ?string) { | setDeviceToken(deviceToken: ?string) { | ||||
this.props.dispatchActionPromise( | this.props.dispatchActionPromise( | ||||
setDeviceTokenActionTypes, | setDeviceTokenActionTypes, | ||||
this.props.setDeviceToken(deviceToken), | this.props.setDeviceToken(deviceToken), | ||||
); | ); | ||||
} | } | ||||
failedToRegisterPushPermissions = () => { | failedToRegisterPushPermissionsIOS = () => { | ||||
this.setDeviceToken(null); | this.setDeviceToken(null); | ||||
if (!this.props.loggedIn) { | if (!this.props.loggedIn) { | ||||
return; | return; | ||||
} | } | ||||
const deviceType = Platform.OS; | |||||
if (deviceType === 'ios') { | |||||
iosPushPermissionResponseReceived(); | iosPushPermissionResponseReceived(); | ||||
} else { | }; | ||||
failedToRegisterPushPermissionsAndroid = ( | |||||
shouldShowAlertOnAndroid: boolean, | |||||
) => { | |||||
this.setDeviceToken(null); | |||||
if (!this.props.loggedIn) { | |||||
return; | |||||
} | |||||
if (shouldShowAlertOnAndroid) { | |||||
this.showNotifAlertOnAndroid(); | this.showNotifAlertOnAndroid(); | ||||
} | } | ||||
}; | }; | ||||
showNotifAlertOnAndroid() { | showNotifAlertOnAndroid() { | ||||
const alertInfo = this.props.notifPermissionAlertInfo; | const alertInfo = this.props.notifPermissionAlertInfo; | ||||
if (shouldSkipPushPermissionAlert(alertInfo)) { | if (shouldSkipPushPermissionAlert(alertInfo)) { | ||||
return; | return; | ||||
▲ Show 20 Lines • Show All 215 Lines • Show Last 20 Lines |