* Refactor custom push notification code * Use react-native-notifications 2.0.6 and patch for scheduled notifs * Fix patch * iOS changes * Fix delete * Fix setting of badge number on Android * Undo Reflect removal * Undo removal of didReceiveRemoteNotification * Use min importance for push notifs received while app is active * Correctly set badge number after push notificaiton reply * Fix tests * Localize reply action text * Add getDeliveredNotifications * Fix identifier check and failing test * Fix local push notif test for Android > 9
231 lines
7.1 KiB
JavaScript
231 lines
7.1 KiB
JavaScript
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
import {AppState} from 'react-native';
|
|
import NotificationsIOS, {
|
|
NotificationAction,
|
|
NotificationCategory,
|
|
DEVICE_REMOTE_NOTIFICATIONS_REGISTERED_EVENT,
|
|
DEVICE_NOTIFICATION_RECEIVED_FOREGROUND_EVENT,
|
|
DEVICE_NOTIFICATION_OPENED_EVENT,
|
|
} from 'react-native-notifications';
|
|
|
|
import {getBadgeCount} from 'app/selectors/views';
|
|
import ephemeralStore from 'app/store/ephemeral_store';
|
|
import {getCurrentLocale} from 'app/selectors/i18n';
|
|
import {getLocalizedMessage} from 'app/i18n';
|
|
import {t} from 'app/utils/i18n';
|
|
|
|
const CATEGORY = 'CAN_REPLY';
|
|
const REPLY_ACTION = 'REPLY_ACTION';
|
|
|
|
const replies = new Set();
|
|
|
|
class PushNotification {
|
|
constructor() {
|
|
this.deviceNotification = null;
|
|
this.onRegister = null;
|
|
this.onNotification = null;
|
|
this.onReply = null;
|
|
this.reduxStore = null;
|
|
|
|
NotificationsIOS.addEventListener(DEVICE_REMOTE_NOTIFICATIONS_REGISTERED_EVENT, this.onRemoteNotificationsRegistered);
|
|
NotificationsIOS.addEventListener(DEVICE_NOTIFICATION_RECEIVED_FOREGROUND_EVENT, this.onNotificationReceivedForeground);
|
|
NotificationsIOS.addEventListener(DEVICE_NOTIFICATION_OPENED_EVENT, this.onNotificationOpened);
|
|
}
|
|
|
|
handleNotification = (data, foreground, userInteraction) => {
|
|
this.deviceNotification = {
|
|
data,
|
|
foreground,
|
|
message: data.message,
|
|
userInfo: data.userInfo,
|
|
userInteraction,
|
|
};
|
|
|
|
if (this.onNotification) {
|
|
this.onNotification(this.deviceNotification);
|
|
}
|
|
};
|
|
|
|
handleReply = (notification, text, completion) => {
|
|
const data = notification.getData();
|
|
|
|
if (this.onReply && !replies.has(data.identifier)) {
|
|
replies.add(data.identifier);
|
|
this.onReply(data, text, completion);
|
|
} else {
|
|
completion();
|
|
}
|
|
};
|
|
|
|
configure(options) {
|
|
this.reduxStore = options.reduxStore;
|
|
this.onRegister = options.onRegister;
|
|
this.onNotification = options.onNotification;
|
|
this.onReply = options.onReply;
|
|
|
|
this.requestNotificationReplyPermissions();
|
|
}
|
|
|
|
requestNotificationReplyPermissions = () => {
|
|
const replyCategory = this.createReplyCategory();
|
|
this.requestPermissions([replyCategory]);
|
|
}
|
|
|
|
createReplyCategory = () => {
|
|
const {getState} = this.reduxStore;
|
|
const state = getState();
|
|
const locale = getCurrentLocale(state);
|
|
|
|
const replyTitle = getLocalizedMessage(locale, t('mobile.push_notification_reply.title'));
|
|
const replyButton = getLocalizedMessage(locale, t('mobile.push_notification_reply.button'));
|
|
const replyPlaceholder = getLocalizedMessage(locale, t('mobile.push_notification_reply.placeholder'));
|
|
|
|
const replyAction = new NotificationAction({
|
|
activationMode: 'background',
|
|
title: replyTitle,
|
|
textInput: {
|
|
buttonTitle: replyButton,
|
|
placeholder: replyPlaceholder,
|
|
},
|
|
authenticationRequired: true,
|
|
identifier: REPLY_ACTION,
|
|
});
|
|
|
|
return new NotificationCategory({
|
|
identifier: CATEGORY,
|
|
actions: [replyAction],
|
|
context: 'default',
|
|
});
|
|
}
|
|
|
|
requestPermissions = (permissions) => {
|
|
NotificationsIOS.requestPermissions(permissions);
|
|
};
|
|
|
|
localNotificationSchedule(notification) {
|
|
if (notification.date) {
|
|
const deviceNotification = {
|
|
fireDate: notification.date.toISOString(),
|
|
body: notification.message,
|
|
alertAction: '',
|
|
userInfo: notification.userInfo,
|
|
};
|
|
|
|
NotificationsIOS.localNotification(deviceNotification);
|
|
}
|
|
}
|
|
|
|
localNotification(notification) {
|
|
this.deviceNotification = {
|
|
body: notification.message,
|
|
alertAction: '',
|
|
userInfo: notification.userInfo,
|
|
};
|
|
|
|
NotificationsIOS.localNotification(this.deviceNotification);
|
|
}
|
|
|
|
cancelAllLocalNotifications() {
|
|
NotificationsIOS.cancelAllLocalNotifications();
|
|
}
|
|
|
|
onNotificationReceivedBackground = (notification) => {
|
|
const userInteraction = AppState.currentState === 'active';
|
|
|
|
// mark the app as started as soon as possible
|
|
if (userInteraction) {
|
|
ephemeralStore.appStartedFromPushNotification = true;
|
|
}
|
|
|
|
const info = {
|
|
...notification.getData(),
|
|
message: notification.getMessage(),
|
|
};
|
|
|
|
if (!userInteraction) {
|
|
this.handleNotification(info, false, userInteraction);
|
|
}
|
|
};
|
|
|
|
onNotificationReceivedForeground = (notification) => {
|
|
const info = {
|
|
...notification.getData(),
|
|
message: notification.getMessage(),
|
|
};
|
|
this.handleNotification(info, true, false);
|
|
};
|
|
|
|
onNotificationOpened = (notification, completion, action) => {
|
|
if (action.identifier === REPLY_ACTION) {
|
|
this.handleReply(notification, action.text, completion);
|
|
} else {
|
|
const info = {
|
|
...notification.getData(),
|
|
message: notification.getMessage(),
|
|
};
|
|
this.handleNotification(info, false, true);
|
|
completion();
|
|
}
|
|
};
|
|
|
|
onRemoteNotificationsRegistered = (deviceToken) => {
|
|
if (this.onRegister) {
|
|
this.onRegister({token: deviceToken});
|
|
}
|
|
};
|
|
|
|
setApplicationIconBadgeNumber(number) {
|
|
const count = number < 0 ? 0 : number;
|
|
NotificationsIOS.setBadgesCount(count);
|
|
}
|
|
|
|
getNotification() {
|
|
return this.deviceNotification;
|
|
}
|
|
|
|
resetNotification() {
|
|
this.deviceNotification = null;
|
|
}
|
|
|
|
getDeliveredNotifications(callback) {
|
|
NotificationsIOS.getDeliveredNotifications(callback);
|
|
}
|
|
|
|
clearChannelNotifications(channelId) {
|
|
NotificationsIOS.getDeliveredNotifications((notifications) => {
|
|
const ids = [];
|
|
let badgeCount = notifications.length;
|
|
|
|
if (this.reduxStore) {
|
|
const totalMentions = getBadgeCount(this.reduxStore.getState());
|
|
if (totalMentions > -1) {
|
|
badgeCount = totalMentions;
|
|
}
|
|
}
|
|
|
|
for (let i = 0; i < notifications.length; i++) {
|
|
const notification = notifications[i];
|
|
|
|
if (notification.channel_id === channelId) {
|
|
ids.push(notification.identifier);
|
|
}
|
|
}
|
|
|
|
if (ids.length) {
|
|
badgeCount -= ids.length;
|
|
NotificationsIOS.removeDeliveredNotifications(ids);
|
|
}
|
|
|
|
this.setApplicationIconBadgeNumber(badgeCount);
|
|
});
|
|
}
|
|
|
|
clearNotifications = () => {
|
|
this.setApplicationIconBadgeNumber(0);
|
|
this.cancelAllLocalNotifications(); // TODO: Only cancel the local notifications that belong to this server
|
|
}
|
|
}
|
|
|
|
export default new PushNotification();
|