From 6a9ae4756405bc715de02a58740815ab464ea047 Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Tue, 14 Mar 2023 16:47:00 -0400 Subject: [PATCH 01/20] pull out notification checks into helper function --- src/libs/actions/Report.js | 42 +++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index c4549b6cc121..5bf803c86346 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1134,49 +1134,62 @@ function setIsComposerFullSize(reportID, isComposerFullSize) { /** * @param {String} reportID * @param {Object} action + * @returns {Boolean} */ -function showReportActionNotification(reportID, action) { +function shouldShowReportActionNotification(reportID, action) { if (ReportActionsUtils.isDeletedAction(action)) { - Log.info('[LOCAL_NOTIFICATION] Skipping notification because the action was deleted', false, {reportID, action}); - return; + Log.info('[Notification] Skipping notification because the action was deleted', false, {reportID, action}); + return false; } if (!ActiveClientManager.isClientTheLeader()) { - Log.info('[LOCAL_NOTIFICATION] Skipping notification because this client is not the leader'); - return; + Log.info('[Notification] Skipping notification because this client is not the leader'); + return false; } // We don't want to send a local notification if the user preference is daily or mute const notificationPreference = lodashGet(allReports, [reportID, 'notificationPreference'], CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS); if (notificationPreference === CONST.REPORT.NOTIFICATION_PREFERENCE.MUTE || notificationPreference === CONST.REPORT.NOTIFICATION_PREFERENCE.DAILY) { - Log.info(`[LOCAL_NOTIFICATION] No notification because user preference is to be notified: ${notificationPreference}`); - return; + Log.info(`[Notification] No notification because user preference is to be notified: ${notificationPreference}`); + return false; } // If this comment is from the current user we don't want to parrot whatever they wrote back to them. if (action.actorAccountID === currentUserAccountID) { - Log.info('[LOCAL_NOTIFICATION] No notification because comment is from the currently logged in user'); - return; + Log.info('[Notification] No notification because comment is from the currently logged in user'); + return false; } // If we are currently viewing this report do not show a notification. if (reportID === Navigation.getReportIDFromRoute() && Visibility.isVisible()) { - Log.info('[LOCAL_NOTIFICATION] No notification because it was a comment for the current report', false, {currentReport: Navigation.getReportIDFromRoute(), reportID, action}); - return; + Log.info('[Notification] No notification because it was a comment for the current report', false, {currentReport: Navigation.getReportIDFromRoute(), reportID, action}); + return false; } // If the comment came from Concierge let's not show a notification since we already show one for expensify.com if (lodashGet(action, 'actorEmail') === CONST.EMAIL.CONCIERGE) { - return; + return false; } // Don't show a notification if no comment exists if (!_.some(action.message, f => f.type === 'COMMENT')) { - Log.info('[LOCAL_NOTIFICATION] No notification because no comments exist for the current action'); + Log.info('[Notification] No notification because no comments exist for the current action'); + return false; + } + + return true; +} + +/** + * @param {String} reportID + * @param {Object} action + */ +function showReportActionNotification(reportID, action) { + if (!shouldShowReportActionNotification(reportID, action)) { return; } - Log.info('[LOCAL_NOTIFICATION] Creating notification'); + Log.info('[LocalNotification] Creating notification'); LocalNotification.showCommentNotification({ reportAction: action, onClick: () => { @@ -1401,4 +1414,5 @@ export { removeEmojiReaction, toggleEmojiReaction, hasAccountIDReacted, + shouldShowReportActionNotification, }; From 3c3dc2437257609328784355c10491322e926965 Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Tue, 14 Mar 2023 16:50:54 -0400 Subject: [PATCH 02/20] use shouldShowReportActionNotification for push notifications on android --- .../PushNotification/index.native.js | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/libs/Notification/PushNotification/index.native.js b/src/libs/Notification/PushNotification/index.native.js index d6d8e136175e..d971506153b9 100644 --- a/src/libs/Notification/PushNotification/index.native.js +++ b/src/libs/Notification/PushNotification/index.native.js @@ -6,6 +6,7 @@ import Log from '../../Log'; import NotificationType from './NotificationType'; import * as PushNotification from '../../actions/PushNotification'; import ONYXKEYS from '../../../ONYXKEYS'; +import * as Report from '../../actions/Report'; let isUserOptedInToPushNotifications = false; Onyx.connect({ @@ -105,6 +106,26 @@ function init() { iOS.ForegroundPresentationOption.Sound, iOS.ForegroundPresentationOption.Badge, ]); + + // Control when we should show notifications + Airship.push.android.setForegroundDisplayPredicate((payload) => { + let pushData = {}; + try { + pushData = JSON.parse(payload.extras.payload); + } catch (error) { + Log.hmmm('[PushNotification] Failed to parse payload for push notification', {error, payload: payload.extras.payload}); + return; + } + + if (!pushData.reportID || !pushData.reportAction) { + Log.info('[PushNotification] Not a report action notification. Showing...', {reportID: pushData.reportID, reportAction: pushData.reportAction}); + return Promise.resolve(true); + } + + const shouldShow = Report.shouldShowReportActionNotification(String(pushData.reportID), pushData.reportAction); + Log.info(`[PushNotification] ${shouldShow ? 'Showing' : 'Not showing'} notification`); + return Promise.resolve(shouldShow); + }); } /** From e0626ea2b8f67f7b422639806f9be0f1a34a7a04 Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Tue, 14 Mar 2023 16:55:00 -0400 Subject: [PATCH 03/20] show notifications from concierge --- src/libs/actions/Report.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 5bf803c86346..93df98d18e0e 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1166,11 +1166,6 @@ function shouldShowReportActionNotification(reportID, action) { return false; } - // If the comment came from Concierge let's not show a notification since we already show one for expensify.com - if (lodashGet(action, 'actorEmail') === CONST.EMAIL.CONCIERGE) { - return false; - } - // Don't show a notification if no comment exists if (!_.some(action.message, f => f.type === 'COMMENT')) { Log.info('[Notification] No notification because no comments exist for the current action'); From be57390fcc2851027ea67f070b3d01ffaab1261f Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Tue, 14 Mar 2023 17:13:32 -0400 Subject: [PATCH 04/20] use casing consistent with other logging --- .../Notification/PushNotification/index.native.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libs/Notification/PushNotification/index.native.js b/src/libs/Notification/PushNotification/index.native.js index d971506153b9..201fbf2c8448 100644 --- a/src/libs/Notification/PushNotification/index.native.js +++ b/src/libs/Notification/PushNotification/index.native.js @@ -31,21 +31,21 @@ function pushNotificationEventCallback(eventType, notification) { payload = JSON.parse(payload); } - Log.info(`[PUSH_NOTIFICATION] Callback triggered for ${eventType}`); + Log.info(`[PushNotification] Callback triggered for ${eventType}`); if (!payload) { - Log.warn('[PUSH_NOTIFICATION] Notification has null or undefined payload, not executing any callback.'); + Log.warn('[PushNotification] Notification has null or undefined payload, not executing any callback.'); return; } if (!payload.type) { - Log.warn('[PUSH_NOTIFICATION] No type value provided in payload, not executing any callback.'); + Log.warn('[PushNotification] No type value provided in payload, not executing any callback.'); return; } const action = actionMap[payload.type]; if (!action) { - Log.warn('[PUSH_NOTIFICATION] No callback set up: ', { + Log.warn('[PushNotification] No callback set up: ', { event: eventType, notificationType: payload.type, }); @@ -146,12 +146,12 @@ function register(accountID) { return; } - Log.info('[PUSH_NOTIFICATIONS] User has disabled visible push notifications for this app.'); + Log.info('[PushNotification] User has disabled visible push notifications for this app.'); }); // Register this device as a named user in AirshipAPI. // Regardless of the user's opt-in status, we still want to receive silent push notifications. - Log.info(`[PUSH_NOTIFICATIONS] Subscribing to notifications for account ID ${accountID}`); + Log.info(`[PushNotification] Subscribing to notifications for account ID ${accountID}`); Airship.contact.identify(accountID.toString()); // Refresh notification opt-in status NVP for the new user. @@ -162,7 +162,7 @@ function register(accountID) { * Deregister this device from push notifications. */ function deregister() { - Log.info('[PUSH_NOTIFICATIONS] Unsubscribing from push notifications.'); + Log.info('[PushNotification] Unsubscribing from push notifications.'); Airship.contact.reset(); Airship.removeAllListeners(EventType.PushReceived); Airship.removeAllListeners(EventType.NotificationResponse); From d2834917508870c38b26f577efaaa3c42bcabbea Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Wed, 15 Mar 2023 12:09:43 -0400 Subject: [PATCH 05/20] don't show notifications for report actions that were already read --- src/libs/actions/Report.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 93df98d18e0e..ed09cca275bb 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1166,6 +1166,13 @@ function shouldShowReportActionNotification(reportID, action) { return false; } + // If this notification was delayed and the user saw the message already, don't show it + const report = allReports[reportID]; + if (report && report.lastReadTime >= action.created) { + Log.info('[Notification] No notification because the comment was already read', false, {created: action.created, lastReadTime: report.lastReadTime}); + return false; + } + // Don't show a notification if no comment exists if (!_.some(action.message, f => f.type === 'COMMENT')) { Log.info('[Notification] No notification because no comments exist for the current action'); From 8916cdbc9e188ba95ccfc0db7e1eeab7e216c830 Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Wed, 15 Mar 2023 14:21:05 -0400 Subject: [PATCH 06/20] fix camera-roll import --- src/libs/fileDownload/index.ios.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/fileDownload/index.ios.js b/src/libs/fileDownload/index.ios.js index 82b0b3acdc59..30af92d4c791 100644 --- a/src/libs/fileDownload/index.ios.js +++ b/src/libs/fileDownload/index.ios.js @@ -1,5 +1,5 @@ import RNFetchBlob from 'react-native-blob-util'; -import {CameraRoll} from '@react-native-camera-roll/cameraroll'; +import {CameraRoll} from '@react-native-camera-roll/camera-roll'; import lodashGet from 'lodash/get'; import * as FileUtils from './FileUtils'; import CONST from '../../CONST'; From f03189f3557e28c3c7569c82fb9d2feb7e557900 Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Wed, 15 Mar 2023 14:21:33 -0400 Subject: [PATCH 07/20] fix iOS notification presentation options for v15 --- src/libs/Notification/PushNotification/index.native.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libs/Notification/PushNotification/index.native.js b/src/libs/Notification/PushNotification/index.native.js index 201fbf2c8448..a9e9447eaff8 100644 --- a/src/libs/Notification/PushNotification/index.native.js +++ b/src/libs/Notification/PushNotification/index.native.js @@ -102,7 +102,8 @@ function init() { // By default, the push notifications are silenced on iOS if the App is in foreground. // More info here https://developer.apple.com/documentation/usernotifications/unusernotificationcenterdelegate/1649518-usernotificationcenter Airship.push.iOS.setForegroundPresentationOptions([ - iOS.ForegroundPresentationOption.Alert, + iOS.ForegroundPresentationOption.List, + iOS.ForegroundPresentationOption.Banner, iOS.ForegroundPresentationOption.Sound, iOS.ForegroundPresentationOption.Badge, ]); From 6abdfcd6ae381c49d1e6c9454c80cf1420863644 Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Wed, 15 Mar 2023 15:00:49 -0400 Subject: [PATCH 08/20] use context awareness for Android and iOS push notifications --- .../PushNotification/index.native.js | 45 +++++++++++-------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/src/libs/Notification/PushNotification/index.native.js b/src/libs/Notification/PushNotification/index.native.js index a9e9447eaff8..142f93b0b9b7 100644 --- a/src/libs/Notification/PushNotification/index.native.js +++ b/src/libs/Notification/PushNotification/index.native.js @@ -70,6 +70,29 @@ function refreshNotificationOptInStatus() { }); } +/** + * Returns whether the given Airship notification should be shown depending on the current state of the app + * @param {PushPayload} pushPayload + * @returns {Boolean} + */ +function shouldShowPushNotification(pushPayload) { + let pushData = pushPayload.extras.payload; + + // The payload is string encoded on Android + if (_.isString(pushData)) { + pushData = JSON.parse(pushData); + } + + if (!pushData.reportID || !pushData.reportAction) { + Log.info('[PushNotification] Not a report action notification. Showing notification', {reportID: pushData.reportID, reportAction: pushData.reportAction}); + return true; + } + + const shouldShow = Report.shouldShowReportActionNotification(String(pushData.reportID), pushData.reportAction); + Log.info(`[PushNotification] ${shouldShow ? 'Showing' : 'Not showing'} notification`); + return shouldShow; +} + /** * Register push notification callbacks. This is separate from namedUser registration because it needs to be executed * from a headless JS process, outside of any react lifecycle. @@ -108,25 +131,9 @@ function init() { iOS.ForegroundPresentationOption.Badge, ]); - // Control when we should show notifications - Airship.push.android.setForegroundDisplayPredicate((payload) => { - let pushData = {}; - try { - pushData = JSON.parse(payload.extras.payload); - } catch (error) { - Log.hmmm('[PushNotification] Failed to parse payload for push notification', {error, payload: payload.extras.payload}); - return; - } - - if (!pushData.reportID || !pushData.reportAction) { - Log.info('[PushNotification] Not a report action notification. Showing...', {reportID: pushData.reportID, reportAction: pushData.reportAction}); - return Promise.resolve(true); - } - - const shouldShow = Report.shouldShowReportActionNotification(String(pushData.reportID), pushData.reportAction); - Log.info(`[PushNotification] ${shouldShow ? 'Showing' : 'Not showing'} notification`); - return Promise.resolve(shouldShow); - }); + // Control when we should show notifications on Android and iOS + Airship.push.android.setForegroundDisplayPredicate(pushPayload => Promise.resolve(shouldShowPushNotification(pushPayload))); + Airship.push.iOS.setForegroundPresentationOptionsCallback(pushPayload => Promise.resolve(shouldShowPushNotification(pushPayload) ? null : [])); } /** From c074d493e0f5442a206e994a575b6d2f5ba67e6c Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Wed, 15 Mar 2023 15:08:46 -0400 Subject: [PATCH 09/20] update comments --- src/libs/Notification/PushNotification/index.native.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/libs/Notification/PushNotification/index.native.js b/src/libs/Notification/PushNotification/index.native.js index 142f93b0b9b7..0c55ce3342bb 100644 --- a/src/libs/Notification/PushNotification/index.native.js +++ b/src/libs/Notification/PushNotification/index.native.js @@ -94,7 +94,7 @@ function shouldShowPushNotification(pushPayload) { } /** - * Register push notification callbacks. This is separate from namedUser registration because it needs to be executed + * Configure push notifications and register callbacks. This is separate from namedUser registration because it needs to be executed * from a headless JS process, outside of any react lifecycle. * * WARNING: Moving or changing this code could break Push Notification processing in non-obvious ways. @@ -120,9 +120,7 @@ function init() { // Keep track of which users have enabled push notifications via an NVP. Airship.addListener(EventType.NotificationOptInStatus, refreshNotificationOptInStatus); - // This statement has effect on iOS only. - // It enables the App to display push notifications when the App is in foreground. - // By default, the push notifications are silenced on iOS if the App is in foreground. + // Set our default iOS foreground presentation to be loud with a banner // More info here https://developer.apple.com/documentation/usernotifications/unusernotificationcenterdelegate/1649518-usernotificationcenter Airship.push.iOS.setForegroundPresentationOptions([ iOS.ForegroundPresentationOption.List, @@ -131,7 +129,7 @@ function init() { iOS.ForegroundPresentationOption.Badge, ]); - // Control when we should show notifications on Android and iOS + // Override foreground presentation per notification depending on the app's state Airship.push.android.setForegroundDisplayPredicate(pushPayload => Promise.resolve(shouldShowPushNotification(pushPayload))); Airship.push.iOS.setForegroundPresentationOptionsCallback(pushPayload => Promise.resolve(shouldShowPushNotification(pushPayload) ? null : [])); } From a9647a10aabe61b7aaa633de9540e0817c3ddabf Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Wed, 15 Mar 2023 15:13:05 -0400 Subject: [PATCH 10/20] update pods --- ios/Podfile.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index dfc7c1b41235..6ef1802c543f 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -473,7 +473,7 @@ PODS: - React-Core - react-native-blob-util (0.16.2): - React-Core - - react-native-cameraroll (4.1.2): + - react-native-cameraroll (5.0.4): - React-Core - react-native-config (1.4.6): - react-native-config/App (= 1.4.6) @@ -712,7 +712,7 @@ DEPENDENCIES: - React-logger (from `../node_modules/react-native/ReactCommon/logger`) - "react-native-airship (from `../node_modules/@ua/react-native-airship`)" - react-native-blob-util (from `../node_modules/react-native-blob-util`) - - "react-native-cameraroll (from `../node_modules/@react-native-community/cameraroll`)" + - "react-native-cameraroll (from `../node_modules/@react-native-camera-roll/camera-roll`)" - react-native-config (from `../node_modules/react-native-config`) - react-native-document-picker (from `../node_modules/react-native-document-picker`) - react-native-flipper (from `../node_modules/react-native-flipper`) @@ -858,7 +858,7 @@ EXTERNAL SOURCES: react-native-blob-util: :path: "../node_modules/react-native-blob-util" react-native-cameraroll: - :path: "../node_modules/@react-native-community/cameraroll" + :path: "../node_modules/@react-native-camera-roll/camera-roll" react-native-config: :path: "../node_modules/react-native-config" react-native-document-picker: @@ -1011,7 +1011,7 @@ SPEC CHECKSUMS: React-logger: b2c47bb0829c3c45ba3a04add9a2b4aea3f3dd4e react-native-airship: 8690a133d791068741855d4f20ea83ec4a7761d1 react-native-blob-util: c3b0faecd2919db568e9d552084396f3e50b57c7 - react-native-cameraroll: 2957f2bce63ae896a848fbe0d5352c1bd4d20866 + react-native-cameraroll: 38b40d9033e4077b6c603f92f95c6d05fa7907df react-native-config: 7cd105e71d903104e8919261480858940a6b9c0e react-native-document-picker: f68191637788994baed5f57d12994aa32cf8bf88 react-native-flipper: dc5290261fbeeb2faec1bdc57ae6dd8d562e1de4 From 1616a4756e8dc7e471427844b9775af54fd3a0c2 Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Wed, 15 Mar 2023 16:37:11 -0400 Subject: [PATCH 11/20] clearer logging for debugging --- .../PushNotification/index.native.js | 6 ++++-- src/libs/actions/Report.js | 19 +++++++++++-------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/libs/Notification/PushNotification/index.native.js b/src/libs/Notification/PushNotification/index.native.js index 0c55ce3342bb..8153a53ed09e 100644 --- a/src/libs/Notification/PushNotification/index.native.js +++ b/src/libs/Notification/PushNotification/index.native.js @@ -65,7 +65,7 @@ function refreshNotificationOptInStatus() { return; } - Log.info('[PUSH_NOTIFICATION] Push notification opt-in status changed.', false, {isOptedIn}); + Log.info('[PushNotification] Push notification opt-in status changed.', false, {isOptedIn}); PushNotification.setPushNotificationOptInStatus(isOptedIn); }); } @@ -76,6 +76,8 @@ function refreshNotificationOptInStatus() { * @returns {Boolean} */ function shouldShowPushNotification(pushPayload) { + Log.info('[PushNotification] push notification received', false, {pushPayload}); + let pushData = pushPayload.extras.payload; // The payload is string encoded on Android @@ -88,7 +90,7 @@ function shouldShowPushNotification(pushPayload) { return true; } - const shouldShow = Report.shouldShowReportActionNotification(String(pushData.reportID), pushData.reportAction); + const shouldShow = Report.shouldShowReportActionNotification(String(pushData.reportID), pushData.reportAction, true); Log.info(`[PushNotification] ${shouldShow ? 'Showing' : 'Not showing'} notification`); return shouldShow; } diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index e4278463f63a..242f355a32b4 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1134,48 +1134,51 @@ function setIsComposerFullSize(reportID, isComposerFullSize) { /** * @param {String} reportID * @param {Object} action + * @param {Boolean} isRemote * @returns {Boolean} */ -function shouldShowReportActionNotification(reportID, action) { +function shouldShowReportActionNotification(reportID, action, isRemote = false) { + const tag = isRemote ? '[PushNotification]' : '[LocalNotification]'; + if (ReportActionsUtils.isDeletedAction(action)) { - Log.info('[Notification] Skipping notification because the action was deleted', false, {reportID, action}); + Log.info(`${tag} Skipping notification because the action was deleted`, false, {reportID, action}); return false; } if (!ActiveClientManager.isClientTheLeader()) { - Log.info('[Notification] Skipping notification because this client is not the leader'); + Log.info(`${tag} Skipping notification because this client is not the leader`); return false; } // We don't want to send a local notification if the user preference is daily or mute const notificationPreference = lodashGet(allReports, [reportID, 'notificationPreference'], CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS); if (notificationPreference === CONST.REPORT.NOTIFICATION_PREFERENCE.MUTE || notificationPreference === CONST.REPORT.NOTIFICATION_PREFERENCE.DAILY) { - Log.info(`[Notification] No notification because user preference is to be notified: ${notificationPreference}`); + Log.info(`${tag} No notification because user preference is to be notified: ${notificationPreference}`); return false; } // If this comment is from the current user we don't want to parrot whatever they wrote back to them. if (action.actorAccountID === currentUserAccountID) { - Log.info('[Notification] No notification because comment is from the currently logged in user'); + Log.info(`${tag} No notification because comment is from the currently logged in user`); return false; } // If we are currently viewing this report do not show a notification. if (reportID === Navigation.getReportIDFromRoute() && Visibility.isVisible()) { - Log.info('[Notification] No notification because it was a comment for the current report', false, {currentReport: Navigation.getReportIDFromRoute(), reportID, action}); + Log.info(`${tag} No notification because it was a comment for the current report`); return false; } // If this notification was delayed and the user saw the message already, don't show it const report = allReports[reportID]; if (report && report.lastReadTime >= action.created) { - Log.info('[Notification] No notification because the comment was already read', false, {created: action.created, lastReadTime: report.lastReadTime}); + Log.info(`${tag} No notification because the comment was already read`, false, {created: action.created, lastReadTime: report.lastReadTime}); return false; } // Don't show a notification if no comment exists if (!_.some(action.message, f => f.type === 'COMMENT')) { - Log.info('[Notification] No notification because no comments exist for the current action'); + Log.info(`${tag} No notification because no comments exist for the current action`); return false; } From f3279a278023054a471d7014bec3018420387c46 Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Thu, 16 Mar 2023 16:31:07 -0400 Subject: [PATCH 12/20] handle push notifications with no reportAction in payload --- .../PushNotification/index.native.js | 4 ++-- src/libs/actions/Report.js | 16 +++++++++------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/libs/Notification/PushNotification/index.native.js b/src/libs/Notification/PushNotification/index.native.js index 8153a53ed09e..be9435c6842e 100644 --- a/src/libs/Notification/PushNotification/index.native.js +++ b/src/libs/Notification/PushNotification/index.native.js @@ -85,8 +85,8 @@ function shouldShowPushNotification(pushPayload) { pushData = JSON.parse(pushData); } - if (!pushData.reportID || !pushData.reportAction) { - Log.info('[PushNotification] Not a report action notification. Showing notification', {reportID: pushData.reportID, reportAction: pushData.reportAction}); + if (!pushData.reportID) { + Log.info('[PushNotification] Not a report action notification. Showing notification'); return true; } diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index 242f355a32b4..cb7de28c26fc 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1133,14 +1133,16 @@ function setIsComposerFullSize(reportID, isComposerFullSize) { /** * @param {String} reportID - * @param {Object} action - * @param {Boolean} isRemote + * @param {Object} action the associated report action (optional) + * @param {Boolean} isRemote whether or not this notification is a remote push notification * @returns {Boolean} */ -function shouldShowReportActionNotification(reportID, action, isRemote = false) { +function shouldShowReportActionNotification(reportID, action = null, isRemote = false) { const tag = isRemote ? '[PushNotification]' : '[LocalNotification]'; - if (ReportActionsUtils.isDeletedAction(action)) { + // Due to payload size constraints, some push notifications may have their report action stripped + // so we must double check that we were provided an action before using it in these checks. + if (action && ReportActionsUtils.isDeletedAction(action)) { Log.info(`${tag} Skipping notification because the action was deleted`, false, {reportID, action}); return false; } @@ -1158,7 +1160,7 @@ function shouldShowReportActionNotification(reportID, action, isRemote = false) } // If this comment is from the current user we don't want to parrot whatever they wrote back to them. - if (action.actorAccountID === currentUserAccountID) { + if (action && action.actorAccountID === currentUserAccountID) { Log.info(`${tag} No notification because comment is from the currently logged in user`); return false; } @@ -1171,13 +1173,13 @@ function shouldShowReportActionNotification(reportID, action, isRemote = false) // If this notification was delayed and the user saw the message already, don't show it const report = allReports[reportID]; - if (report && report.lastReadTime >= action.created) { + if (action && report && report.lastReadTime >= action.created) { Log.info(`${tag} No notification because the comment was already read`, false, {created: action.created, lastReadTime: report.lastReadTime}); return false; } // Don't show a notification if no comment exists - if (!_.some(action.message, f => f.type === 'COMMENT')) { + if (action && !_.some(action.message, f => f.type === 'COMMENT')) { Log.info(`${tag} No notification because no comments exist for the current action`); return false; } From 20b3c82b30628d67667b5840aa7f63d7ea57d9e0 Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Thu, 16 Mar 2023 18:05:06 -0400 Subject: [PATCH 13/20] use report action from onyxData --- .../PushNotification/index.native.js | 4 +++- src/libs/ReportActionsUtils.js | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/libs/Notification/PushNotification/index.native.js b/src/libs/Notification/PushNotification/index.native.js index be9435c6842e..d917690bf57a 100644 --- a/src/libs/Notification/PushNotification/index.native.js +++ b/src/libs/Notification/PushNotification/index.native.js @@ -7,6 +7,7 @@ import NotificationType from './NotificationType'; import * as PushNotification from '../../actions/PushNotification'; import ONYXKEYS from '../../../ONYXKEYS'; import * as Report from '../../actions/Report'; +import * as ReportActionUtils from '../../ReportActionsUtils'; let isUserOptedInToPushNotifications = false; Onyx.connect({ @@ -90,7 +91,8 @@ function shouldShowPushNotification(pushPayload) { return true; } - const shouldShow = Report.shouldShowReportActionNotification(String(pushData.reportID), pushData.reportAction, true); + const reportAction = ReportActionUtils.getLatestReportActionFromOnyxData(pushData.onyxData); + const shouldShow = Report.shouldShowReportActionNotification(String(pushData.reportID), reportAction, true); Log.info(`[PushNotification] ${shouldShow ? 'Showing' : 'Not showing'} notification`); return shouldShow; } diff --git a/src/libs/ReportActionsUtils.js b/src/libs/ReportActionsUtils.js index df55ae49faee..54cedc82a83a 100644 --- a/src/libs/ReportActionsUtils.js +++ b/src/libs/ReportActionsUtils.js @@ -223,6 +223,22 @@ function getLastClosedReportAction(reportActions) { return lodashFindLast(sortedReportActions, action => action.actionName === CONST.REPORT.ACTIONS.TYPE.CLOSED); } +/** + * @param {Array} onyxData + * @returns {Object} The latest report action in the `onyxData` or `null` if one couldn't be found + */ +function getLatestReportActionFromOnyxData(onyxData) { + const reportActionUpdate = _.find(onyxData, onyxUpdate => onyxUpdate.key.startsWith(ONYXKEYS.COLLECTION.REPORT_ACTIONS)); + + if (!reportActionUpdate) { + return null; + } + + const reportActions = _.values(reportActionUpdate.value); + const sortedReportActions = getSortedReportActions(reportActions); + return _.last(sortedReportActions); +} + export { getSortedReportActions, getLastVisibleAction, @@ -232,4 +248,5 @@ export { isConsecutiveActionMadeByPreviousActor, getSortedReportActionsForDisplay, getLastClosedReportAction, + getLatestReportActionFromOnyxData, }; From 48128a8f675641120437aeec3e4c3b80ccbd9eff Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Thu, 16 Mar 2023 18:19:43 -0400 Subject: [PATCH 14/20] use platform modules to fix errors about using iOS/Android SDKs on the wrong platform --- .../index.android.js | 9 ++++ .../index.ios.js | 18 ++++++++ .../configureForegroundNotifications/index.js | 4 ++ .../PushNotification/index.native.js | 44 ++----------------- .../shouldShowPushNotification.js | 30 +++++++++++++ 5 files changed, 64 insertions(+), 41 deletions(-) create mode 100644 src/libs/Notification/PushNotification/configureForegroundNotifications/index.android.js create mode 100644 src/libs/Notification/PushNotification/configureForegroundNotifications/index.ios.js create mode 100644 src/libs/Notification/PushNotification/configureForegroundNotifications/index.js create mode 100644 src/libs/Notification/PushNotification/shouldShowPushNotification.js diff --git a/src/libs/Notification/PushNotification/configureForegroundNotifications/index.android.js b/src/libs/Notification/PushNotification/configureForegroundNotifications/index.android.js new file mode 100644 index 000000000000..ba44f0aced8e --- /dev/null +++ b/src/libs/Notification/PushNotification/configureForegroundNotifications/index.android.js @@ -0,0 +1,9 @@ +import Airship from '@ua/react-native-airship'; +import shouldShowPushNotification from '../shouldShowPushNotification'; + +/** + * Configures notification handling while in the foreground on iOS and Android. This is a no-op on other platforms. + */ +export default function configureForegroundNotifications() { + Airship.push.android.setForegroundDisplayPredicate(pushPayload => Promise.resolve(shouldShowPushNotification(pushPayload))); +} diff --git a/src/libs/Notification/PushNotification/configureForegroundNotifications/index.ios.js b/src/libs/Notification/PushNotification/configureForegroundNotifications/index.ios.js new file mode 100644 index 000000000000..2226e748c508 --- /dev/null +++ b/src/libs/Notification/PushNotification/configureForegroundNotifications/index.ios.js @@ -0,0 +1,18 @@ +import Airship, {iOS} from '@ua/react-native-airship'; +import shouldShowPushNotification from '../shouldShowPushNotification'; + +/** + * Configures notification handling while in the foreground on iOS and Android. This is a no-op on other platforms. + */ +export default function configureForegroundNotifications() { + // Set our default iOS foreground presentation to be loud with a banner + // More info here https://developer.apple.com/documentation/usernotifications/unusernotificationcenterdelegate/1649518-usernotificationcenter + Airship.push.iOS.setForegroundPresentationOptions([ + iOS.ForegroundPresentationOption.List, + iOS.ForegroundPresentationOption.Banner, + iOS.ForegroundPresentationOption.Sound, + iOS.ForegroundPresentationOption.Badge, + ]); + + Airship.push.iOS.setForegroundPresentationOptionsCallback(pushPayload => Promise.resolve(shouldShowPushNotification(pushPayload) ? null : [])); +} diff --git a/src/libs/Notification/PushNotification/configureForegroundNotifications/index.js b/src/libs/Notification/PushNotification/configureForegroundNotifications/index.js new file mode 100644 index 000000000000..c6cb13a0b3b9 --- /dev/null +++ b/src/libs/Notification/PushNotification/configureForegroundNotifications/index.js @@ -0,0 +1,4 @@ +/** + * Configures notification handling while in the foreground on iOS and Android. This is a no-op on other platforms. + */ +export default function () {} diff --git a/src/libs/Notification/PushNotification/index.native.js b/src/libs/Notification/PushNotification/index.native.js index d917690bf57a..0121ea058642 100644 --- a/src/libs/Notification/PushNotification/index.native.js +++ b/src/libs/Notification/PushNotification/index.native.js @@ -1,13 +1,12 @@ import _ from 'underscore'; import Onyx from 'react-native-onyx'; -import Airship, {EventType, iOS} from '@ua/react-native-airship'; +import Airship, {EventType} from '@ua/react-native-airship'; import lodashGet from 'lodash/get'; import Log from '../../Log'; import NotificationType from './NotificationType'; import * as PushNotification from '../../actions/PushNotification'; import ONYXKEYS from '../../../ONYXKEYS'; -import * as Report from '../../actions/Report'; -import * as ReportActionUtils from '../../ReportActionsUtils'; +import configureForegroundNotifications from './configureForegroundNotifications'; let isUserOptedInToPushNotifications = false; Onyx.connect({ @@ -71,32 +70,6 @@ function refreshNotificationOptInStatus() { }); } -/** - * Returns whether the given Airship notification should be shown depending on the current state of the app - * @param {PushPayload} pushPayload - * @returns {Boolean} - */ -function shouldShowPushNotification(pushPayload) { - Log.info('[PushNotification] push notification received', false, {pushPayload}); - - let pushData = pushPayload.extras.payload; - - // The payload is string encoded on Android - if (_.isString(pushData)) { - pushData = JSON.parse(pushData); - } - - if (!pushData.reportID) { - Log.info('[PushNotification] Not a report action notification. Showing notification'); - return true; - } - - const reportAction = ReportActionUtils.getLatestReportActionFromOnyxData(pushData.onyxData); - const shouldShow = Report.shouldShowReportActionNotification(String(pushData.reportID), reportAction, true); - Log.info(`[PushNotification] ${shouldShow ? 'Showing' : 'Not showing'} notification`); - return shouldShow; -} - /** * Configure push notifications and register callbacks. This is separate from namedUser registration because it needs to be executed * from a headless JS process, outside of any react lifecycle. @@ -124,18 +97,7 @@ function init() { // Keep track of which users have enabled push notifications via an NVP. Airship.addListener(EventType.NotificationOptInStatus, refreshNotificationOptInStatus); - // Set our default iOS foreground presentation to be loud with a banner - // More info here https://developer.apple.com/documentation/usernotifications/unusernotificationcenterdelegate/1649518-usernotificationcenter - Airship.push.iOS.setForegroundPresentationOptions([ - iOS.ForegroundPresentationOption.List, - iOS.ForegroundPresentationOption.Banner, - iOS.ForegroundPresentationOption.Sound, - iOS.ForegroundPresentationOption.Badge, - ]); - - // Override foreground presentation per notification depending on the app's state - Airship.push.android.setForegroundDisplayPredicate(pushPayload => Promise.resolve(shouldShowPushNotification(pushPayload))); - Airship.push.iOS.setForegroundPresentationOptionsCallback(pushPayload => Promise.resolve(shouldShowPushNotification(pushPayload) ? null : [])); + configureForegroundNotifications(); } /** diff --git a/src/libs/Notification/PushNotification/shouldShowPushNotification.js b/src/libs/Notification/PushNotification/shouldShowPushNotification.js new file mode 100644 index 000000000000..b9b9d974374c --- /dev/null +++ b/src/libs/Notification/PushNotification/shouldShowPushNotification.js @@ -0,0 +1,30 @@ +import _ from 'underscore'; +import * as Report from '../../actions/Report'; +import Log from '../../Log'; +import * as ReportActionUtils from '../../ReportActionsUtils'; + +/** + * Returns whether the given Airship notification should be shown depending on the current state of the app + * @param {PushPayload} pushPayload + * @returns {Boolean} + */ +export default function shouldShowPushNotification(pushPayload) { + Log.info('[PushNotification] push notification received', false, {pushPayload}); + + let pushData = pushPayload.extras.payload; + + // The payload is string encoded on Android + if (_.isString(pushData)) { + pushData = JSON.parse(pushData); + } + + if (!pushData.reportID) { + Log.info('[PushNotification] Not a report action notification. Showing notification'); + return true; + } + + const reportAction = ReportActionUtils.getLatestReportActionFromOnyxData(pushData.onyxData); + const shouldShow = Report.shouldShowReportActionNotification(String(pushData.reportID), reportAction, true); + Log.info(`[PushNotification] ${shouldShow ? 'Showing' : 'Not showing'} notification`); + return shouldShow; +} From 9fa14ddcb0c949a708c373229c683f10e41d27ce Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Fri, 17 Mar 2023 12:52:28 -0400 Subject: [PATCH 15/20] add airship mocks for foreground callbacks --- __mocks__/@ua/react-native-airship.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/__mocks__/@ua/react-native-airship.js b/__mocks__/@ua/react-native-airship.js index ec394d982ab8..20ecd58d85a6 100644 --- a/__mocks__/@ua/react-native-airship.js +++ b/__mocks__/@ua/react-native-airship.js @@ -20,6 +20,10 @@ const Airship = { iOS: { setBadgeNumber: jest.fn(), setForegroundPresentationOptions: jest.fn(), + setForegroundPresentationOptionsCallback: jest.fn(), + }, + android: { + setForegroundDisplayPredicate: jest.fn(), }, enableUserNotifications: () => Promise.resolve(false), clearNotifications: jest.fn(), From fc6ebf89d1a155a91edb77ba401f1783c4926914 Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Fri, 17 Mar 2023 12:59:07 -0400 Subject: [PATCH 16/20] mock new camera roll lib --- __mocks__/@react-native-camera-roll/camera-roll.js | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 __mocks__/@react-native-camera-roll/camera-roll.js diff --git a/__mocks__/@react-native-camera-roll/camera-roll.js b/__mocks__/@react-native-camera-roll/camera-roll.js new file mode 100644 index 000000000000..4274cd531a85 --- /dev/null +++ b/__mocks__/@react-native-camera-roll/camera-roll.js @@ -0,0 +1,5 @@ +export default { + CameraRoll: { + save: jest.fn(), + }, +}; From 9dc1ec43a8eac6902648c97d5bc0d1ee7d25eafd Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Fri, 17 Mar 2023 15:40:48 -0400 Subject: [PATCH 17/20] fix require cycle between PushNotification libs and Report lib --- ...bscribeToReportCommentPushNotifications.js | 38 +++++++++++++++++++ src/libs/actions/Report.js | 34 ----------------- src/libs/actions/Session/index.js | 4 +- src/setup/platformSetup/index.native.js | 4 +- 4 files changed, 42 insertions(+), 38 deletions(-) create mode 100644 src/libs/Notification/PushNotification/subscribeToReportCommentPushNotifications.js diff --git a/src/libs/Notification/PushNotification/subscribeToReportCommentPushNotifications.js b/src/libs/Notification/PushNotification/subscribeToReportCommentPushNotifications.js new file mode 100644 index 000000000000..eb889853b1d3 --- /dev/null +++ b/src/libs/Notification/PushNotification/subscribeToReportCommentPushNotifications.js @@ -0,0 +1,38 @@ +import {CONST} from 'expensify-common/lib/CONST'; +import {Linking} from 'react-native'; +import Onyx from 'react-native-onyx'; +import PushNotification from '.'; +import ROUTES from '../../../ROUTES'; +import Log from '../../Log'; +import Navigation from '../../Navigation/Navigation'; + +/** + * Setup reportComment push notification callbacks. + */ +export default function subscribeToReportCommentPushNotifications() { + PushNotification.onReceived(PushNotification.TYPE.REPORT_COMMENT, ({reportID, onyxData}) => { + Log.info('[Report] Handled event sent by Airship', false, {reportID}); + Onyx.update(onyxData); + }); + + // Open correct report when push notification is clicked + PushNotification.onSelected(PushNotification.TYPE.REPORT_COMMENT, ({reportID}) => { + if (Navigation.canNavigate('navigate')) { + // If a chat is visible other than the one we are trying to navigate to, then we need to navigate back + if (Navigation.getActiveRoute().slice(1, 2) === ROUTES.REPORT && !Navigation.isActiveRoute(`r/${reportID}`)) { + Navigation.goBack(); + } + Navigation.isDrawerReady() + .then(() => { + Navigation.navigate(ROUTES.getReportRoute(reportID)); + }); + } else { + // Navigation container is not yet ready, use deeplinking to open to correct report instead + Navigation.setDidTapNotification(); + Navigation.isDrawerReady() + .then(() => { + Linking.openURL(`${CONST.DEEPLINK_BASE_URL}${ROUTES.getReportRoute(reportID)}`); + }); + } + }); +} diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index cb7de28c26fc..46b336875d97 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -1,4 +1,3 @@ -import {Linking} from 'react-native'; import _ from 'underscore'; import lodashGet from 'lodash/get'; import ExpensiMark from 'expensify-common/lib/ExpensiMark'; @@ -6,7 +5,6 @@ import Onyx from 'react-native-onyx'; import ONYXKEYS from '../../ONYXKEYS'; import * as Pusher from '../Pusher/pusher'; import LocalNotification from '../Notification/LocalNotification'; -import PushNotification from '../Notification/PushNotification'; import Navigation from '../Navigation/Navigation'; import * as ActiveClientManager from '../ActiveClientManager'; import Visibility from '../Visibility'; @@ -64,37 +62,6 @@ function getReportChannelName(reportID) { return `${CONST.PUSHER.PRIVATE_REPORT_CHANNEL_PREFIX}${reportID}${CONFIG.PUSHER.SUFFIX}`; } -/** - * Setup reportComment push notification callbacks. - */ -function subscribeToReportCommentPushNotifications() { - PushNotification.onReceived(PushNotification.TYPE.REPORT_COMMENT, ({reportID, onyxData}) => { - Log.info('[Report] Handled event sent by Airship', false, {reportID}); - Onyx.update(onyxData); - }); - - // Open correct report when push notification is clicked - PushNotification.onSelected(PushNotification.TYPE.REPORT_COMMENT, ({reportID}) => { - if (Navigation.canNavigate('navigate')) { - // If a chat is visible other than the one we are trying to navigate to, then we need to navigate back - if (Navigation.getActiveRoute().slice(1, 2) === ROUTES.REPORT && !Navigation.isActiveRoute(`r/${reportID}`)) { - Navigation.goBack(); - } - Navigation.isDrawerReady() - .then(() => { - Navigation.navigate(ROUTES.getReportRoute(reportID)); - }); - } else { - // Navigation container is not yet ready, use deeplinking to open to correct report instead - Navigation.setDidTapNotification(); - Navigation.isDrawerReady() - .then(() => { - Linking.openURL(`${CONST.DEEPLINK_BASE_URL}${ROUTES.getReportRoute(reportID)}`); - }); - } - }); -} - /** * There are 2 possibilities that we can receive via pusher for a user's typing status: * 1. The "new" way from New Expensify is passed as {[login]: Boolean} (e.g. {yuwen@expensify.com: true}), where the value @@ -1396,7 +1363,6 @@ export { reconnect, updateNotificationPreference, subscribeToReportTypingEvents, - subscribeToReportCommentPushNotifications, unsubscribeFromReportChannel, saveReportComment, broadcastUserIsTyping, diff --git a/src/libs/actions/Session/index.js b/src/libs/actions/Session/index.js index edf29b069400..13f1a3ab084a 100644 --- a/src/libs/actions/Session/index.js +++ b/src/libs/actions/Session/index.js @@ -15,10 +15,10 @@ import * as Authentication from '../../Authentication'; import * as Welcome from '../Welcome'; import * as API from '../../API'; import * as NetworkStore from '../../Network/NetworkStore'; -import * as Report from '../Report'; import DateUtils from '../../DateUtils'; import Navigation from '../../Navigation/Navigation'; import ROUTES from '../../../ROUTES'; +import subscribeToReportCommentPushNotifications from '../../Notification/PushNotification/subscribeToReportCommentPushNotifications'; let credentials = {}; Onyx.connect({ @@ -40,7 +40,7 @@ Onyx.connect({ // Prevent issue where report linking fails after users switch accounts without closing the app PushNotification.init(); - Report.subscribeToReportCommentPushNotifications(); + subscribeToReportCommentPushNotifications(); } else { PushNotification.deregister(); PushNotification.clearNotifications(); diff --git a/src/setup/platformSetup/index.native.js b/src/setup/platformSetup/index.native.js index 2ffbdcb036a9..d164600c7706 100644 --- a/src/setup/platformSetup/index.native.js +++ b/src/setup/platformSetup/index.native.js @@ -1,8 +1,8 @@ import crashlytics from '@react-native-firebase/crashlytics'; import CONFIG from '../../CONFIG'; import PushNotification from '../../libs/Notification/PushNotification'; -import * as Report from '../../libs/actions/Report'; import Performance from '../../libs/Performance'; +import subscribeToReportCommentPushNotifications from '../../libs/Notification/PushNotification/subscribeToReportCommentPushNotifications'; export default function () { // We do not want to send crash reports if we are on a locally built release version of the app. @@ -19,7 +19,7 @@ export default function () { * Otherwise, they will not be executed when the app is completely closed, and the push notification won't update the app data. */ PushNotification.init(); - Report.subscribeToReportCommentPushNotifications(); + subscribeToReportCommentPushNotifications(); // Setup Flipper plugins when on dev if (__DEV__ && typeof jest === 'undefined') { From 8b08eb3a4f75bb47a9d22e695cb20aeb2bd8bf2a Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Thu, 30 Mar 2023 14:23:52 +0000 Subject: [PATCH 18/20] remove unnecessary docs --- .../configureForegroundNotifications/index.android.js | 3 --- .../configureForegroundNotifications/index.ios.js | 3 --- 2 files changed, 6 deletions(-) diff --git a/src/libs/Notification/PushNotification/configureForegroundNotifications/index.android.js b/src/libs/Notification/PushNotification/configureForegroundNotifications/index.android.js index ba44f0aced8e..29613e0fe050 100644 --- a/src/libs/Notification/PushNotification/configureForegroundNotifications/index.android.js +++ b/src/libs/Notification/PushNotification/configureForegroundNotifications/index.android.js @@ -1,9 +1,6 @@ import Airship from '@ua/react-native-airship'; import shouldShowPushNotification from '../shouldShowPushNotification'; -/** - * Configures notification handling while in the foreground on iOS and Android. This is a no-op on other platforms. - */ export default function configureForegroundNotifications() { Airship.push.android.setForegroundDisplayPredicate(pushPayload => Promise.resolve(shouldShowPushNotification(pushPayload))); } diff --git a/src/libs/Notification/PushNotification/configureForegroundNotifications/index.ios.js b/src/libs/Notification/PushNotification/configureForegroundNotifications/index.ios.js index 2226e748c508..cf565df7747b 100644 --- a/src/libs/Notification/PushNotification/configureForegroundNotifications/index.ios.js +++ b/src/libs/Notification/PushNotification/configureForegroundNotifications/index.ios.js @@ -1,9 +1,6 @@ import Airship, {iOS} from '@ua/react-native-airship'; import shouldShowPushNotification from '../shouldShowPushNotification'; -/** - * Configures notification handling while in the foreground on iOS and Android. This is a no-op on other platforms. - */ export default function configureForegroundNotifications() { // Set our default iOS foreground presentation to be loud with a banner // More info here https://developer.apple.com/documentation/usernotifications/unusernotificationcenterdelegate/1649518-usernotificationcenter From 130c0b041909d84ef0e6b91ba412309fa573f6b1 Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Thu, 30 Mar 2023 15:26:32 +0000 Subject: [PATCH 19/20] add a note about this possibly being fixed in the future --- android/app/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/android/app/build.gradle b/android/app/build.gradle index dcb55e04fea4..8cba6f1811be 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -334,6 +334,7 @@ dependencies { implementation project(':react-native-plaid-link-sdk') // Fixes a version conflict between airship and react-native-plaid-link-sdk + // This may be fixed by a newer version of the plaid SDK (not working as of 10.0.0) implementation "androidx.work:work-runtime-ktx:2.8.0" // This okhttp3 dependency prevents the app from crashing - See https://github.com/plaid/react-native-plaid-link-sdk/issues/74#issuecomment-648435002 From 5405f30f615aba8fc96d5d0c5348bcaedc257e23 Mon Sep 17 00:00:00 2001 From: Andrew Rosiclair Date: Wed, 5 Apr 2023 16:30:50 -0400 Subject: [PATCH 20/20] clarify setForegroundPresentationOptionsCallback behavior --- .../configureForegroundNotifications/index.ios.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libs/Notification/PushNotification/configureForegroundNotifications/index.ios.js b/src/libs/Notification/PushNotification/configureForegroundNotifications/index.ios.js index cf565df7747b..27586403eab2 100644 --- a/src/libs/Notification/PushNotification/configureForegroundNotifications/index.ios.js +++ b/src/libs/Notification/PushNotification/configureForegroundNotifications/index.ios.js @@ -11,5 +11,7 @@ export default function configureForegroundNotifications() { iOS.ForegroundPresentationOption.Badge, ]); + // Set a callback to override our foreground presentation per notification depending on the app's current state. + // Returning null keeps the default presentation. Returning [] uses no presentation (hides the notification). Airship.push.iOS.setForegroundPresentationOptionsCallback(pushPayload => Promise.resolve(shouldShowPushNotification(pushPayload) ? null : [])); }