@aws-amplify/pushnotification
Version:
Push notifications category of aws-amplify
564 lines (516 loc) • 19.1 kB
text/typescript
// 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;
}
}