From 46b6687d1f015eba52fca4cc9b9fa0efcea3580b Mon Sep 17 00:00:00 2001 From: Elias Nahum Date: Thu, 20 Sep 2018 13:58:21 -0300 Subject: [PATCH] Show local notification when replying to PN fails (#2142) --- .../rnbeta/CustomPushNotification.java | 2 +- app/actions/views/root.js | 15 ++++-- app/i18n/index.js | 6 +++ .../push_notifications.android.js | 7 ++- .../push_notifications.ios.js | 10 ++++ app/utils/push_notifications.js | 50 +++++++++++-------- assets/base/i18n/en.json | 1 + 7 files changed, 62 insertions(+), 29 deletions(-) diff --git a/android/app/src/main/java/com/mattermost/rnbeta/CustomPushNotification.java b/android/app/src/main/java/com/mattermost/rnbeta/CustomPushNotification.java index c693f6a7d..104eaf92f 100644 --- a/android/app/src/main/java/com/mattermost/rnbeta/CustomPushNotification.java +++ b/android/app/src/main/java/com/mattermost/rnbeta/CustomPushNotification.java @@ -149,7 +149,7 @@ public class CustomPushNotification extends PushNotification { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, - NotificationManager.IMPORTANCE_DEFAULT); + NotificationManager.IMPORTANCE_HIGH); final NotificationManager notificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); notificationManager.createNotificationChannel(channel); notification.setChannelId(CHANNEL_ID); diff --git a/app/actions/views/root.js b/app/actions/views/root.js index 8fe81acd4..f34c82e15 100644 --- a/app/actions/views/root.js +++ b/app/actions/views/root.js @@ -105,7 +105,7 @@ export function purgeOfflineStore() { // A non-optimistic version of the createPost action in mattermost-redux with the file handling // removed since it's not needed. export function createPostForNotificationReply(post) { - return (dispatch, getState) => { + return async (dispatch, getState) => { const state = getState(); const currentUserId = state.entities.users.currentUserId; @@ -119,18 +119,23 @@ export function createPostForNotificationReply(post) { update_at: timestamp, }; - return Client4.createPost({...newPost, create_at: 0}).then((payload) => { + try { + const data = await Client4.createPost({...newPost, create_at: 0}); dispatch({ type: PostTypes.RECEIVED_POSTS, data: { order: [], posts: { - [payload.id]: payload, + [data.id]: data, }, }, - channelId: payload.channel_id, + channelId: data.channel_id, }); - }); + + return {data}; + } catch (error) { + return {error}; + } }; } diff --git a/app/i18n/index.js b/app/i18n/index.js index d6591de64..1987bccb0 100644 --- a/app/i18n/index.js +++ b/app/i18n/index.js @@ -89,3 +89,9 @@ export function getTranslations(locale) { } return TRANSLATIONS[locale] || TRANSLATIONS[DEFAULT_LOCALE]; } + +export function getLocalizedMessage(locale, id) { + const translations = getTranslations(locale); + + return translations[id] || TRANSLATIONS[DEFAULT_LOCALE][id]; +} diff --git a/app/push_notifications/push_notifications.android.js b/app/push_notifications/push_notifications.android.js index 3b466f118..9a93d2886 100644 --- a/app/push_notifications/push_notifications.android.js +++ b/app/push_notifications/push_notifications.android.js @@ -5,6 +5,8 @@ import {AppRegistry, AppState} from 'react-native'; import {NotificationsAndroid, PendingNotifications} from 'react-native-notifications'; import Notification from 'react-native-notifications/notification.android'; +import {emptyFunction} from 'app/utils/general'; + class PushNotification { constructor() { this.onRegister = null; @@ -37,15 +39,16 @@ class PushNotification { AppRegistry.registerHeadlessTask('notificationReplied', () => async (deviceNotification) => { const notification = new Notification(deviceNotification); const data = notification.getData(); + const completed = emptyFunction; if (this.onReply) { - this.onReply(data, data.text, parseInt(data.badge, 10) - parseInt(data.msg_count, 10)); + this.onReply(data, data.text, parseInt(data.badge, 10) - parseInt(data.msg_count, 10), completed); } else { this.deviceNotification = { data, text: data.text, badge: parseInt(data.badge, 10) - parseInt(data.msg_count, 10), - completed: true, // used to identify that the notification belongs to a reply + completed, // used to identify that the notification belongs to a reply }; } }); diff --git a/app/push_notifications/push_notifications.ios.js b/app/push_notifications/push_notifications.ios.js index e62af1807..b1e5d2b4c 100644 --- a/app/push_notifications/push_notifications.ios.js +++ b/app/push_notifications/push_notifications.ios.js @@ -122,6 +122,16 @@ class PushNotification { } } + localNotification(notification) { + const deviceNotification = { + alertBody: notification.message, + alertAction: '', + userInfo: notification.userInfo, + }; + + NotificationsIOS.localNotification(deviceNotification); + } + cancelAllLocalNotifications() { NotificationsIOS.cancelAllLocalNotifications(); } diff --git a/app/utils/push_notifications.js b/app/utils/push_notifications.js index d590e9d97..1f151c369 100644 --- a/app/utils/push_notifications.js +++ b/app/utils/push_notifications.js @@ -2,28 +2,28 @@ // See LICENSE.txt for license information. import {Platform} from 'react-native'; - -import PushNotifications from 'app/push_notifications'; import DeviceInfo from 'react-native-device-info'; -import {Client4} from 'mattermost-redux/client'; import {markChannelAsRead} from 'mattermost-redux/actions/channels'; import {setDeviceToken} from 'mattermost-redux/actions/general'; import {getPosts} from 'mattermost-redux/actions/posts'; +import {Client4} from 'mattermost-redux/client'; import {General} from 'mattermost-redux/constants'; +import {getCurrentUser} from 'mattermost-redux/selectors/entities/users'; import EventEmitter from 'mattermost-redux/utils/event_emitter'; -import {ViewTypes} from 'app/constants'; import {retryGetPostsAction} from 'app/actions/views/channel'; import { createPostForNotificationReply, loadFromPushNotification, } from 'app/actions/views/root'; - +import {ViewTypes} from 'app/constants'; +import {DEFAULT_LOCALE, getLocalizedMessage} from 'app/i18n'; import { app, store, } from 'app/mattermost'; +import PushNotifications from 'app/push_notifications'; const onRegisterDevice = (data) => { app.setIsNotificationsConfigured(true); @@ -105,14 +105,14 @@ const onPushNotification = async (deviceNotification) => { } }; -export const onPushNotificationReply = (data, text, badge, completed) => { +export const onPushNotificationReply = async (data, text, badge, completed) => { const {dispatch, getState} = store; const state = getState(); - const {currentUserId: reduxCurrentUserId} = state.entities.users; + const reduxCurrentUser = getCurrentUser(state); const reduxCredentialsUrl = state.entities.general.credentials.url; const reduxCredentialsToken = state.entities.general.credentials.token; - const currentUserId = reduxCurrentUserId || app.currentUserId; + const currentUserId = reduxCurrentUser ? reduxCurrentUser.id : app.currentUserId; const url = reduxCredentialsUrl || app.url; const token = reduxCredentialsToken || app.token; @@ -138,20 +138,28 @@ export const onPushNotificationReply = (data, text, badge, completed) => { } retryGetPostsAction(getPosts(data.channel_id), dispatch, getState); - dispatch(createPostForNotificationReply(post)). - then(() => { - dispatch(markChannelAsRead(data.channel_id)); - - if (badge >= 0) { - PushNotifications.setApplicationIconBadgeNumber(badge); - } - - app.setReplyNotificationData(null); - }). - then(completed). - catch((e) => { - console.warn('Failed to send reply to push notification', e); // eslint-disable-line no-console + const result = await dispatch(createPostForNotificationReply(post)); + if (result.error) { + const locale = reduxCurrentUser ? reduxCurrentUser.locale : DEFAULT_LOCALE; + PushNotifications.localNotification({ + message: getLocalizedMessage(locale, 'mobile.reply_post.failed'), + userInfo: { + localNotification: true, + localTest: true, + }, }); + console.warn('Failed to send reply to push notification', result.error); // eslint-disable-line no-console + completed(); + return; + } + + if (badge >= 0) { + PushNotifications.setApplicationIconBadgeNumber(badge); + } + + dispatch(markChannelAsRead(data.channel_id)); + app.setReplyNotificationData(null); + completed(); } else { app.setReplyNotificationData({ data, diff --git a/assets/base/i18n/en.json b/assets/base/i18n/en.json index 3f4b02cc2..20f95b24b 100644 --- a/assets/base/i18n/en.json +++ b/assets/base/i18n/en.json @@ -2579,6 +2579,7 @@ "mobile.rename_channel.name_maxLength": "URL must be less than {maxLength, number} characters", "mobile.rename_channel.name_minLength": "URL must be {minLength, number} or more characters", "mobile.rename_channel.name_required": "URL is required", + "mobile.reply_post.failed": "Message failed to send.", "mobile.request.invalid_response": "Received invalid response from the server.", "mobile.reset_status.alert_cancel": "Cancel", "mobile.reset_status.alert_ok": "Ok",