Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Background push notification processing #3877

Merged
merged 37 commits into from
Sep 1, 2021
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
da2100e
Upgrade UA to 11.0.2
roryabraham Jul 2, 2021
3352b90
Create a separate init function for push notification callbacks
roryabraham Jul 2, 2021
ccb1461
Give default notification preference and use const
roryabraham Jul 2, 2021
ea21c60
Move report comment push notification callbacks to their own function
roryabraham Jul 2, 2021
9675d72
Initialize onyx and push notifications outside of react lifecycle
roryabraham Jul 2, 2021
e52d86d
Update airship pod
roryabraham Jul 7, 2021
c2d1d48
Merge branch 'main' into Rory-BackgroundPushNotificationsV2
roryabraham Jul 12, 2021
4722f11
Update Podfile.lock
roryabraham Jul 12, 2021
93e5640
Merge branch 'main' into Rory-BackgroundPushNotificationsV2
roryabraham Jul 28, 2021
14f3e75
Reinstall pods
roryabraham Jul 28, 2021
c0ffbff
Merge branch 'main' into Rory-BackgroundPushNotificationsV2
roryabraham Jul 28, 2021
eaed997
Resolve merge conflict
roryabraham Jul 28, 2021
d213a76
Merge branch 'main' into Rory-BackgroundPushNotificationsV2
roryabraham Aug 12, 2021
c4616bd
Reinstall pods after merge
roryabraham Aug 12, 2021
0886014
Move native flipper setup to correct location
roryabraham Aug 12, 2021
4416bee
Add __DEV__ to eslint globals
roryabraham Aug 12, 2021
062b50d
Remove Onyx initialization from React component again
roryabraham Aug 12, 2021
5547cbc
Merge branch 'main' into Rory-BackgroundPushNotificationsV2
roryabraham Aug 13, 2021
bf64272
Cleanup pods and setup after merge
roryabraham Aug 13, 2021
02d11db
Fix import
roryabraham Aug 13, 2021
e76d782
Defer navigation until main NavigationContainer is loaded
roryabraham Aug 14, 2021
0301b66
Merge branch 'main' into Rory-BackgroundPushNotificationsV2
roryabraham Aug 25, 2021
e3d50d9
Resolve bad merge
roryabraham Aug 25, 2021
7dc8374
Use deep link instead of Navigation.navigate
roryabraham Aug 25, 2021
8254d99
Set flag when drawer should start closed
roryabraham Aug 26, 2021
f580621
Add new-expensify:// URL scheme
roryabraham Aug 26, 2021
fa45b6e
Create getDefaultDrawerState function
roryabraham Aug 26, 2021
c52cbd1
Add comment about headless JS and Onyx in setup/index.js
roryabraham Aug 26, 2021
0abff13
More detailed comment in platformSetup/index.native.js
roryabraham Aug 26, 2021
7179916
Use setDidTapNotification instead of closeDrawer side-effect
roryabraham Aug 27, 2021
b9a9606
Add clearer comment about headless Onyx init
roryabraham Aug 27, 2021
ab9400f
Add angry all-caps warning message
roryabraham Aug 28, 2021
a1db149
Get rid of unnecessary promise chain
roryabraham Aug 28, 2021
fe6829c
Make dummy change in README
roryabraham Aug 30, 2021
da6e525
Undo dummy change
roryabraham Aug 30, 2021
52a4908
Fully undo dummy change
roryabraham Aug 30, 2021
58a5b20
Merge branch 'main' into Rory-BackgroundPushNotificationsV2
roryabraham Aug 30, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 13 additions & 13 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
PODS:
- Airship (14.3.0):
- Airship/Automation (= 14.3.0)
- Airship/Core (= 14.3.0)
- Airship/ExtendedActions (= 14.3.0)
- Airship/MessageCenter (= 14.3.0)
- Airship/Automation (14.3.0):
- Airship (14.4.2):
- Airship/Automation (= 14.4.2)
- Airship/Core (= 14.4.2)
- Airship/ExtendedActions (= 14.4.2)
- Airship/MessageCenter (= 14.4.2)
- Airship/Automation (14.4.2):
- Airship/Core
- Airship/Core (14.3.0)
- Airship/ExtendedActions (14.3.0):
- Airship/Core (14.4.2)
- Airship/ExtendedActions (14.4.2):
- Airship/Core
- Airship/MessageCenter (14.3.0):
- Airship/MessageCenter (14.4.2):
- Airship/Core
- boost-for-react-native (1.63.0)
- CocoaAsyncSocket (7.6.5)
Expand Down Expand Up @@ -492,8 +492,8 @@ PODS:
- React-Core
- RNSVG (12.1.0):
- React
- urbanairship-react-native (11.0.1):
- Airship (= 14.3.0)
- urbanairship-react-native (11.0.2):
- Airship (= 14.4.2)
- React-Core
- Yoga (1.14.0)
- YogaKit (1.18.1):
Expand Down Expand Up @@ -723,7 +723,7 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon/yoga"

SPEC CHECKSUMS:
Airship: 7609d263d3a207f112d6db066af5852b80af6819
Airship: 29d674abeac754f783fc46c7d383d6f046687341
boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
DoubleConversion: cf9b38bf0b2d048436d9a82ad2abe1404f11e7de
Expand Down Expand Up @@ -802,7 +802,7 @@ SPEC CHECKSUMS:
RNReanimated: b8c8004b43446e3c2709fe64b2b41072f87428ad
RNScreens: e8e8dd0588b5da0ab57dcca76ab9b2d8987757e0
RNSVG: ce9d996113475209013317e48b05c21ee988d42e
urbanairship-react-native: d415a12e67ba93bf3ce914df9a310b66a88a5cc3
urbanairship-react-native: 60b4b4235838ff109a2639b639e2ef01d54ad455
Yoga: a7de31c64fe738607e7a3803e3f591a4b1df7393
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a

Expand Down
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@
"rn-fetch-blob": "^0.12.0",
"save": "^2.4.0",
"underscore": "^1.10.2",
"urbanairship-react-native": "^11.0.1"
"urbanairship-react-native": "^11.0.2"
},
"devDependencies": {
"@actions/core": "^1.2.6",
Expand Down
31 changes: 1 addition & 30 deletions src/Expensify.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,12 @@ import lodashGet from 'lodash/get';
import PropTypes from 'prop-types';
import React, {PureComponent} from 'react';
import {View, AppState} from 'react-native';
import Onyx, {withOnyx} from 'react-native-onyx';
import {withOnyx} from 'react-native-onyx';

import BootSplash from './libs/BootSplash';
import listenToStorageEvents from './libs/listenToStorageEvents';
import * as ActiveClientManager from './libs/ActiveClientManager';
import ONYXKEYS from './ONYXKEYS';
import CONST from './CONST';
import NavigationRoot from './libs/Navigation/NavigationRoot';
import Log from './libs/Log';
import migrateOnyx from './libs/migrateOnyx';
import styles from './styles/styles';
import PushNotification from './libs/Notification/PushNotification';
Expand All @@ -21,32 +18,6 @@ import {growlRef} from './libs/Growl';
import Navigation from './libs/Navigation/Navigation';
import ROUTES from './ROUTES';

// Initialize the store when the app loads for the first time
Onyx.init({
keys: ONYXKEYS,
safeEvictionKeys: [ONYXKEYS.COLLECTION.REPORT_ACTIONS],
initialKeyStates: {

// Clear any loading and error messages so they do not appear on app startup
[ONYXKEYS.SESSION]: {loading: false, shouldShowComposeInput: true},
[ONYXKEYS.ACCOUNT]: CONST.DEFAULT_ACCOUNT_DATA,
[ONYXKEYS.NETWORK]: {isOffline: false},
[ONYXKEYS.IOU]: {
loading: false, error: false, creatingIOUTransaction: false, isRetrievingCurrency: false,
},
},
registerStorageEventListener: (onStorageEvent) => {
listenToStorageEvents(onStorageEvent);
},
});
Onyx.registerLogger(({level, message}) => {
if (level === 'alert') {
Log.alert(message, 0, {}, false);
} else {
Log.client(message);
}
});

const propTypes = {
/* Onyx Props */

Expand Down
43 changes: 25 additions & 18 deletions src/libs/Notification/PushNotification/index.native.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,30 @@ function pushNotificationEventCallback(eventType, notification) {
action(payload);
}

/**
* Register push notification callbacks. This is separate from namedUser registration because it needs to be executed
roryabraham marked this conversation as resolved.
Show resolved Hide resolved
* from a headless JS process, outside of any react lifecycle.
marcaaron marked this conversation as resolved.
Show resolved Hide resolved
*/
function init() {
nickmurray47 marked this conversation as resolved.
Show resolved Hide resolved
// Setup event listeners
UrbanAirship.addListener(EventType.PushReceived, (notification) => {
// If a push notification is received while the app is in foreground,
// we'll assume pusher is connected so we'll ignore it and not fetch the same data twice.
if (AppState.currentState === 'active') {
console.debug('[PUSH_NOTIFICATION] Push received while app is in foreground, not executing any callback.');
return;
}

pushNotificationEventCallback(EventType.PushReceived, notification);
});

// Note: the NotificationResponse event has a nested PushReceived event,
// so event.notification refers to the same thing as notification above ^
UrbanAirship.addListener(EventType.NotificationResponse, (event) => {
pushNotificationEventCallback(EventType.NotificationResponse, event.notification);
});
}

/**
* Register this device for push notifications for the given accountID.
*
Expand All @@ -71,24 +95,6 @@ function register(accountID) {
// Regardless of the user's opt-in status, we still want to receive silent push notifications.
console.debug(`[PUSH_NOTIFICATIONS] Subscribing to notifications for account ID ${accountID}`);
UrbanAirship.setNamedUser(accountID.toString());

// Setup event listeners
UrbanAirship.addListener(EventType.PushReceived, (notification) => {
// If a push notification is received while the app is in foreground,
// we'll assume pusher is connected so we'll ignore it and not write the same data twice.
if (AppState.currentState === 'active') {
console.debug('[PUSH_NOTIFICATION] Push received while app is in foreground, not executing any callback.');
return;
}

pushNotificationEventCallback(EventType.PushReceived, notification);
});

// Note: the NotificationResponse event has a nested PushReceived event,
// so event.notification refers to the same thing as notification above ^
UrbanAirship.addListener(EventType.NotificationResponse, (event) => {
pushNotificationEventCallback(EventType.NotificationResponse, event.notification);
});
}

/**
Expand Down Expand Up @@ -142,6 +148,7 @@ function onSelected(notificationType, callback) {
}

export default {
init,
register,
deregister,
onReceived,
Expand Down
14 changes: 12 additions & 2 deletions src/libs/actions/Report.js
Original file line number Diff line number Diff line change
Expand Up @@ -514,9 +514,13 @@ function updateReportActionMessage(reportID, sequenceNumber, message) {
*
* @param {Number} reportID
* @param {Object} reportAction
* @param {String} notificationPreference On what cadence the user would like to be notified
* @param {String} [notificationPreference] On what cadence the user would like to be notified
*/
function updateReportWithNewAction(reportID, reportAction, notificationPreference) {
function updateReportWithNewAction(
reportID,
reportAction,
notificationPreference = CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS,
nickmurray47 marked this conversation as resolved.
Show resolved Hide resolved
) {
const newMaxSequenceNumber = reportAction.sequenceNumber;
const isFromCurrentUser = reportAction.actorAccountID === currentUserAccountID;
const initialLastReadSequenceNumber = lastReadSequenceNumbers[reportID] || 0;
Expand Down Expand Up @@ -715,7 +719,12 @@ function subscribeToUserEvents() {
{error, pusherChannelName, eventName: Pusher.TYPE.REPORT_TOGGLE_PINNED},
);
});
}

/**
* Setup reportComment push notification callbacks.
*/
function subscribeToReportCommentPushNotifications() {
PushNotification.onReceived(PushNotification.TYPE.REPORT_COMMENT, ({reportID, reportAction}) => {
Log.info('[Report] Handled event sent by Airship', true, {reportID});
updateReportWithNewAction(reportID, reportAction);
Expand Down Expand Up @@ -1293,6 +1302,7 @@ export {
setNewMarkerPosition,
subscribeToReportTypingEvents,
subscribeToUserEvents,
subscribeToReportCommentPushNotifications,
unsubscribeFromReportChannel,
saveReportComment,
broadcastUserIsTyping,
Expand Down
7 changes: 4 additions & 3 deletions src/pages/ReportDetailsPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import lodashGet from 'lodash/get';
import compose from '../libs/compose';
import withLocalize, {withLocalizePropTypes} from '../components/withLocalize';
import ONYXKEYS from '../ONYXKEYS';
import CONST from '../CONST';
import ScreenWrapper from '../components/ScreenWrapper';
import Navigation from '../libs/Navigation/Navigation';
import HeaderWithCloseButton from '../components/HeaderWithCloseButton';
Expand Down Expand Up @@ -72,16 +73,16 @@ class ReportDetailsPage extends Component {

this.notificationPreferencesOptions = {
default: {
value: 'always',
value: CONST.REPORT.NOTIFICATION_PREFERENCE.ALWAYS,
label: props.translate('reportDetailsPage.always'),

},
daily: {
value: 'daily',
value: CONST.REPORT.NOTIFICATION_PREFERENCE.DAILY,
label: props.translate('reportDetailsPage.daily'),
},
mute: {
value: 'mute',
value: CONST.REPORT.NOTIFICATION_PREFERENCE.MUTE,
label: props.translate('reportDetailsPage.mute'),
},
};
Expand Down
38 changes: 38 additions & 0 deletions src/setup/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import Onyx from 'react-native-onyx';
import ONYXKEYS from '../ONYXKEYS';
import CONST from '../CONST';
import listenToStorageEvents from '../libs/listenToStorageEvents';
import Log from '../libs/Log';
import platformSetup from './platformSetup';

export default function () {
marcaaron marked this conversation as resolved.
Show resolved Hide resolved
// Initialize the Onyx store when the app loads for the first time
Onyx.init({
keys: ONYXKEYS,
safeEvictionKeys: [ONYXKEYS.COLLECTION.REPORT_ACTIONS],
initialKeyStates: {

// Clear any loading and error messages so they do not appear on app startup
[ONYXKEYS.SESSION]: {loading: false, shouldShowComposeInput: true},
[ONYXKEYS.ACCOUNT]: CONST.DEFAULT_ACCOUNT_DATA,
[ONYXKEYS.NETWORK]: {isOffline: false},
[ONYXKEYS.IOU]: {
loading: false, error: false, creatingIOUTransaction: false, isRetrievingCurrency: false,
},
},
registerStorageEventListener: (onStorageEvent) => {
listenToStorageEvents(onStorageEvent);
},
});

Onyx.registerLogger(({level, message}) => {
if (level === 'alert') {
Log.alert(message, 0, {}, false);
} else {
Log.client(message);
}
});

// Perform any other platform-specific setup
platformSetup();
}
4 changes: 0 additions & 4 deletions src/setup/index.native.js

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {AppRegistry} from 'react-native';
import {ipcRenderer} from 'electron';
import Config from '../CONFIG';
import LocalNotification from '../libs/Notification/LocalNotification';
import DateUtils from '../libs/DateUtils';
import Config from '../../CONFIG';
import LocalNotification from '../../libs/Notification/LocalNotification';
import DateUtils from '../../libs/DateUtils';


export default function () {
Expand Down
11 changes: 11 additions & 0 deletions src/setup/platformSetup/index.native.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import PushNotification from '../../libs/Notification/PushNotification';
import {subscribeToReportCommentPushNotifications} from '../../libs/actions/Report';

/**
* Register callbacks for push notifications.
* This must happen outside of any React lifecycle in order for the headless JS process to work.
roryabraham marked this conversation as resolved.
Show resolved Hide resolved
*/
export default function () {
PushNotification.init();
subscribeToReportCommentPushNotifications();
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import {AppRegistry} from 'react-native';
import checkForUpdates from '../libs/checkForUpdates';
import Config from '../CONFIG';
import HttpUtils from '../libs/HttpUtils';
import DateUtils from '../libs/DateUtils';
import {version as currentVersion} from '../../package.json';
import Visibility from '../libs/Visibility';
import checkForUpdates from '../../libs/checkForUpdates';
import Config from '../../CONFIG';
import HttpUtils from '../../libs/HttpUtils';
import DateUtils from '../../libs/DateUtils';
import {version as currentVersion} from '../../../package.json';
import Visibility from '../../libs/Visibility';

/**
* Download the latest app version from the server, and if it is different than the current one,
Expand Down