UNPKG

@aws-amplify/pushnotification

Version:

Push notifications category of aws-amplify

564 lines (516 loc) • 19.1 kB
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import { NativeModules, DeviceEventEmitter, Platform, AppState, } from 'react-native'; import PushNotificationIOS from '@react-native-community/push-notification-ios'; import AsyncStorage from '@react-native-async-storage/async-storage'; import { Amplify, ConsoleLogger as Logger, isEmpty } from '@aws-amplify/core'; const logger = new Logger('Notification'); const RNPushNotification = NativeModules.RNPushNotification; const REMOTE_NOTIFICATION_RECEIVED = 'remoteNotificationReceived'; const REMOTE_TOKEN_RECEIVED = 'remoteTokenReceived'; const REMOTE_NOTIFICATION_OPENED = 'remoteNotificationOpened'; /** * @deprecated This package (@aws-amplify/pushnotification) is on a deprecation path. Please see the following link for * instructions on how to migrate to a new version of Amplify Push Notifications. * * https://docs.amplify.aws/lib/push-notifications/migrate-from-previous-version/q/platform/react-native/ */ export default class PushNotification { private _config; private _currentState; private _androidInitialized: boolean; private _iosInitialized: boolean; private _notificationOpenedHandlers: Function[]; /** * @deprecated This package (@aws-amplify/pushnotification) is on a deprecation path. Please see the following link for * instructions on how to migrate to a new version of Amplify Push Notifications. * * https://docs.amplify.aws/lib/push-notifications/migrate-from-previous-version/q/platform/react-native/ */ constructor(config) { if (config) { this.configure(config); } else { this._config = {}; } this.updateEndpoint = this.updateEndpoint.bind(this); this.handleNotificationReceived = this.handleNotificationReceived.bind(this); this.handleNotificationOpened = this.handleNotificationOpened.bind(this); this._checkIfOpenedByNotification = this._checkIfOpenedByNotification.bind(this); this.addEventListenerForIOS = this.addEventListenerForIOS.bind(this); this._currentState = AppState.currentState; this._androidInitialized = false; this._iosInitialized = false; this._notificationOpenedHandlers = []; } /** * @deprecated This package (@aws-amplify/pushnotification) is on a deprecation path. Please see the following link for * instructions on how to migrate to a new version of Amplify Push Notifications. * * https://docs.amplify.aws/lib/push-notifications/migrate-from-previous-version/q/platform/react-native/ */ getModuleName() { return 'Pushnotification'; } /** * @deprecated This package (@aws-amplify/pushnotification) is on a deprecation path. Please see the following link for * instructions on how to migrate to a new version of Amplify Push Notifications. * * https://docs.amplify.aws/lib/push-notifications/migrate-from-previous-version/q/platform/react-native/ */ configure(config) { if (isEmpty(config)) return this._config; let conf = config ? config.PushNotification || config : {}; if (config['aws_mobile_analytics_app_id']) { conf = { appId: config['aws_mobile_analytics_app_id'], ...conf, }; } this._config = Object.assign( { // defaults requestIOSPermissions: true, // for backwards compatibility }, this._config, conf ); if (Platform.OS === 'android' && !this._androidInitialized) { this.initializeAndroid(); this._androidInitialized = true; } else if (Platform.OS === 'ios' && !this._iosInitialized) { this.initializeIOS(); this._iosInitialized = true; } return this._config; } /** * @deprecated This package (@aws-amplify/pushnotification) is on a deprecation path. Please see the following link for * instructions on how to migrate to a new version of Amplify Push Notifications. * * https://docs.amplify.aws/lib/push-notifications/migrate-from-previous-version/q/platform/react-native/ */ onNotification(handler) { if (typeof handler === 'function') { // check platform if (Platform.OS === 'ios') { this.addEventListenerForIOS(REMOTE_NOTIFICATION_RECEIVED, handler); } else { this.addEventListenerForAndroid(REMOTE_NOTIFICATION_RECEIVED, handler); } } } /** * @deprecated This package (@aws-amplify/pushnotification) is on a deprecation path. Please see the following link for * instructions on how to migrate to a new version of Amplify Push Notifications. * * https://docs.amplify.aws/lib/push-notifications/migrate-from-previous-version/q/platform/react-native/ */ onNotificationOpened(handler) { if (typeof handler === 'function') { this._notificationOpenedHandlers = [ ...this._notificationOpenedHandlers, handler, ]; } } /** * @deprecated This package (@aws-amplify/pushnotification) is on a deprecation path. Please see the following link for * instructions on how to migrate to a new version of Amplify Push Notifications. * * https://docs.amplify.aws/lib/push-notifications/migrate-from-previous-version/q/platform/react-native/ */ onRegister(handler) { if (typeof handler === 'function') { // check platform if (Platform.OS === 'ios') { this.addEventListenerForIOS(REMOTE_TOKEN_RECEIVED, handler); } else { this.addEventListenerForAndroid(REMOTE_TOKEN_RECEIVED, handler); } } } /** * @deprecated This package (@aws-amplify/pushnotification) is on a deprecation path. Please see the following link for * instructions on how to migrate to a new version of Amplify Push Notifications. * * https://docs.amplify.aws/lib/push-notifications/migrate-from-previous-version/q/platform/react-native/ */ async initializeAndroid() { this.addEventListenerForAndroid(REMOTE_TOKEN_RECEIVED, this.updateEndpoint); this.addEventListenerForAndroid( REMOTE_NOTIFICATION_OPENED, this.handleNotificationOpened ); this.addEventListenerForAndroid( REMOTE_NOTIFICATION_RECEIVED, this.handleNotificationReceived ); // check if the token is cached properly if (!(await this._registerTokenCached())) { const { appId } = this._config; const cacheKey = 'push_token' + appId; RNPushNotification.getToken( token => { logger.debug('Get the token from Firebase Service', token); // resend the token in case it's missing in the Pinpoint service // the token will also be cached locally this.updateEndpoint(token); }, error => { logger.error('Error getting the token from Firebase Service', error); } ); } } /** * @deprecated This package (@aws-amplify/pushnotification) is on a deprecation path. Please see the following link for * instructions on how to migrate to a new version of Amplify Push Notifications. * * https://docs.amplify.aws/lib/push-notifications/migrate-from-previous-version/q/platform/react-native/ */ async _registerTokenCached(): Promise<boolean> { const { appId } = this._config; const cacheKey = 'push_token' + appId; return AsyncStorage.getItem(cacheKey).then(lastToken => { if (lastToken) return true; else return false; }); } /** * @deprecated This package (@aws-amplify/pushnotification) is on a deprecation path. Please see the following link for * instructions on how to migrate to a new version of Amplify Push Notifications. * * https://docs.amplify.aws/lib/push-notifications/migrate-from-previous-version/q/platform/react-native/ */ requestIOSPermissions(options = { alert: true, badge: true, sound: true }) { PushNotificationIOS.requestPermissions(options); } /** * @deprecated This package (@aws-amplify/pushnotification) is on a deprecation path. Please see the following link for * instructions on how to migrate to a new version of Amplify Push Notifications. * * https://docs.amplify.aws/lib/push-notifications/migrate-from-previous-version/q/platform/react-native/ */ initializeIOS() { if (this._config.requestIOSPermissions) { this.requestIOSPermissions(); } this.addEventListenerForIOS(REMOTE_TOKEN_RECEIVED, this.updateEndpoint); this.addEventListenerForIOS( REMOTE_NOTIFICATION_RECEIVED, this.handleNotificationReceived ); this.addEventListenerForIOS( REMOTE_NOTIFICATION_OPENED, this.handleNotificationOpened ); } /** * This function handles the React Native AppState change event * And checks if the app was launched by a Push Notification * Note: Current AppState will be null or 'unknown' if the app is coming from killed state to active * @param nextAppState The next state the app is changing to as part of the event * @deprecated This package (@aws-amplify/pushnotification) is on a deprecation path. Please see the following link for * instructions on how to migrate to a new version of Amplify Push Notifications. * * https://docs.amplify.aws/lib/push-notifications/migrate-from-previous-version/q/platform/react-native/ */ _checkIfOpenedByNotification(nextAppState, handler) { // the App state changes from killed state to active if ( (!this._currentState || this._currentState === 'unknown') && nextAppState === 'active' ) { // If the app was launched with a notification (launched means from killed state) // getInitialNotification() returns the notification object data every time its called // Thus calling it when moving from background to foreground subsequently will lead to extra // events being logged with the payload of the initial notification that launched the app PushNotificationIOS.getInitialNotification() .then(data => { if (data) { handler(data); } }) .catch(e => { logger.debug('Failed to get the initial notification.', e); }); } this._currentState = nextAppState; } /** * @deprecated This package (@aws-amplify/pushnotification) is on a deprecation path. Please see the following link for * instructions on how to migrate to a new version of Amplify Push Notifications. * * https://docs.amplify.aws/lib/push-notifications/migrate-from-previous-version/q/platform/react-native/ */ parseMessageData = rawMessage => { let eventSource = null; let eventSourceAttributes = {}; if (Platform.OS === 'ios') { const message = this.parseMessageFromIOS(rawMessage); const pinpointData = message && message.data ? message.data.pinpoint : null; if (pinpointData && pinpointData.campaign) { eventSource = 'campaign'; eventSourceAttributes = pinpointData.campaign; } else if (pinpointData && pinpointData.journey) { eventSource = 'journey'; eventSourceAttributes = pinpointData.journey; } } else if (Platform.OS === 'android') { const { data } = rawMessage; const pinpointData = data && data.pinpoint ? JSON.parse(data.pinpoint) : null; if (pinpointData && pinpointData.journey) { eventSource = 'journey'; eventSourceAttributes = pinpointData.journey; } // Check if it is a campaign in Android by looking for the Campaign ID key // Android campaign payload is flat and differs from Journey and iOS payloads // TODO: The service should provide data in a consistent format similar to iOS else if (data && data['pinpoint.campaign.campaign_id']) { eventSource = 'campaign'; eventSourceAttributes = { campaign_id: data['pinpoint.campaign.campaign_id'], campaign_activity_id: data['pinpoint.campaign.campaign_activity_id'], treatment_id: data['pinpoint.campaign.treatment_id'], }; } } return { eventSource, eventSourceAttributes, }; }; /** * @deprecated This package (@aws-amplify/pushnotification) is on a deprecation path. Please see the following link for * instructions on how to migrate to a new version of Amplify Push Notifications. * * https://docs.amplify.aws/lib/push-notifications/migrate-from-previous-version/q/platform/react-native/ */ handleNotificationReceived(rawMessage) { logger.debug('handleNotificationReceived, raw data', rawMessage); const { eventSource, eventSourceAttributes } = this.parseMessageData(rawMessage); if (!eventSource) { logger.debug('message received is not from a pinpoint eventSource'); return; } const isAppInForeground = Platform.OS === 'ios' ? this._currentState === 'active' : rawMessage.foreground; const attributes = { ...eventSourceAttributes, isAppInForeground, }; const eventType = isAppInForeground ? `_${eventSource}.received_foreground` : `_${eventSource}.received_background`; if (Amplify.Analytics && typeof Amplify.Analytics.record === 'function') { Amplify.Analytics.record({ name: eventType, attributes, immediate: false, }); } else { logger.debug('Analytics module is not registered into Amplify'); } } /** * @deprecated This package (@aws-amplify/pushnotification) is on a deprecation path. Please see the following link for * instructions on how to migrate to a new version of Amplify Push Notifications. * * https://docs.amplify.aws/lib/push-notifications/migrate-from-previous-version/q/platform/react-native/ */ handleNotificationOpened(rawMessage) { this._notificationOpenedHandlers.forEach(handler => { handler(rawMessage); }); logger.debug('handleNotificationOpened, raw data', rawMessage); const { eventSource, eventSourceAttributes } = this.parseMessageData(rawMessage); if (!eventSource) { logger.debug('message received is not from a pinpoint eventSource'); return; } const attributes = { ...eventSourceAttributes, }; const eventType = `_${eventSource}.opened_notification`; if (Amplify.Analytics && typeof Amplify.Analytics.record === 'function') { Amplify.Analytics.record({ name: eventType, attributes, immediate: false, }); } else { logger.debug('Analytics module is not registered into Amplify'); } } /** * @deprecated This package (@aws-amplify/pushnotification) is on a deprecation path. Please see the following link for * instructions on how to migrate to a new version of Amplify Push Notifications. * * https://docs.amplify.aws/lib/push-notifications/migrate-from-previous-version/q/platform/react-native/ */ updateEndpoint(token) { if (!token) { logger.debug('no device token recieved on register'); return; } const { appId } = this._config; const cacheKey = 'push_token' + appId; logger.debug('update endpoint in push notification', token); AsyncStorage.getItem(cacheKey) .then(lastToken => { if (!lastToken || lastToken !== token) { logger.debug('refresh the device token with', token); const config = { Address: token, OptOut: 'NONE', }; if ( Amplify.Analytics && typeof Amplify.Analytics.updateEndpoint === 'function' ) { Amplify.Analytics.updateEndpoint(config) .then(data => { logger.debug( 'update endpoint success, setting token into cache' ); AsyncStorage.setItem(cacheKey, token); }) .catch(e => { // ........ logger.debug('update endpoint failed', e); }); } else { logger.debug('Analytics module is not registered into Amplify'); } } }) .catch(e => { logger.debug('set device token in cache failed', e); }); } /** * @deprecated This package (@aws-amplify/pushnotification) is on a deprecation path. Please see the following link for * instructions on how to migrate to a new version of Amplify Push Notifications. * * https://docs.amplify.aws/lib/push-notifications/migrate-from-previous-version/q/platform/react-native/ */ addEventListenerForAndroid(event, handler) { const that = this; const listener = DeviceEventEmitter.addListener(event, data => { // for on notification if (event === REMOTE_NOTIFICATION_RECEIVED) { handler(that.parseMessagefromAndroid(data)); return; } if (event === REMOTE_TOKEN_RECEIVED) { const dataObj = data.dataJSON ? JSON.parse(data.dataJSON) : {}; handler(dataObj.refreshToken); return; } if (event === REMOTE_NOTIFICATION_OPENED) { handler(that.parseMessagefromAndroid(data, 'opened')); return; } }); } /** * @deprecated This package (@aws-amplify/pushnotification) is on a deprecation path. Please see the following link for * instructions on how to migrate to a new version of Amplify Push Notifications. * * https://docs.amplify.aws/lib/push-notifications/migrate-from-previous-version/q/platform/react-native/ */ addEventListenerForIOS(event, handler) { if (event === REMOTE_TOKEN_RECEIVED) { PushNotificationIOS.addEventListener('register', data => { handler(data); }); } if (event === REMOTE_NOTIFICATION_RECEIVED) { PushNotificationIOS.addEventListener('notification', handler); } if (event === REMOTE_NOTIFICATION_OPENED) { PushNotificationIOS.addEventListener('localNotification', handler); AppState.addEventListener('change', nextAppState => this._checkIfOpenedByNotification(nextAppState, handler) ); } } /** * @deprecated This package (@aws-amplify/pushnotification) is on a deprecation path. Please see the following link for * instructions on how to migrate to a new version of Amplify Push Notifications. * * https://docs.amplify.aws/lib/push-notifications/migrate-from-previous-version/q/platform/react-native/ */ parseMessagefromAndroid(message, from?) { let dataObj = null; try { dataObj = message.dataJSON ? JSON.parse(message.dataJSON) : null; } catch (e) { logger.debug('Failed to parse the data object', e); return; } if (!dataObj) { logger.debug('no notification payload received'); return dataObj; } // In the case of opened notifications, // the message object should be nested under the 'data' key // so as to have the same format as received notifications // before it is parsed further in parseMessageData() if (from === 'opened') { return { data: dataObj, }; } let ret = dataObj; const dataPayload = dataObj.data || {}; // Consider removing this logic as title and body attributes are not used if (dataPayload['pinpoint.campaign.campaign_id']) { ret = { title: dataPayload['pinpoint.notification.title'], body: dataPayload['pinpoint.notification.body'], data: dataPayload, foreground: dataObj.foreground, }; } return ret; } /** * @deprecated This package (@aws-amplify/pushnotification) is on a deprecation path. Please see the following link for * instructions on how to migrate to a new version of Amplify Push Notifications. * * https://docs.amplify.aws/lib/push-notifications/migrate-from-previous-version/q/platform/react-native/ */ parseMessageFromIOS(message) { const _data = message && message._data ? message._data : null; const _alert = message && message._alert ? message._alert : {}; if (!_data && !_alert) { logger.debug('no notification payload received'); return {}; } const data = _data.data; const title = _alert.title; const body = _alert.body; let ret = null; ret = { title, body, data, }; return ret; } }