-
Notifications
You must be signed in to change notification settings - Fork 34
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: delayed push notifications (#254)
- Loading branch information
Showing
11 changed files
with
190 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,15 @@ | ||
// SPDX-License-Identifier: ice License 1.0 | ||
|
||
import {registerBackgroundTasksHeadlessTask} from '@store/modules/BackgroundTasks/headless'; | ||
import {registerBackgroundMessageHandler} from '@store/modules/PushNotifications/headless'; | ||
import {AppRegistry} from 'react-native'; | ||
import 'react-native-url-polyfill/auto'; | ||
import {name as appName} from './app.json'; | ||
import {App} from './src/App'; | ||
|
||
import {LoggingWrapper} from '@services/logging'; | ||
|
||
AppRegistry.registerComponent(appName, () => LoggingWrapper(App)); | ||
|
||
registerBackgroundTasksHeadlessTask(); | ||
registerBackgroundMessageHandler(); | ||
|
||
AppRegistry.registerComponent(appName, () => LoggingWrapper(App)); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
// SPDX-License-Identifier: ice License 1.0 | ||
|
||
import messaging, { | ||
FirebaseMessagingTypes, | ||
} from '@react-native-firebase/messaging'; | ||
import {store} from '@store/configureStore'; | ||
import {PushNotificationsActions} from '@store/modules/PushNotifications/actions'; | ||
import {isDataOnlyMessage} from '@store/modules/PushNotifications/utils/isDataOnlyMessage'; | ||
|
||
/** | ||
* Resolve handler promise only when all the work is done | ||
*/ | ||
const backgroundMessageHandler = async ( | ||
message: FirebaseMessagingTypes.RemoteMessage, | ||
) => { | ||
if (isDataOnlyMessage(message)) { | ||
await new Promise<void>(resolve => { | ||
store.dispatch( | ||
PushNotificationsActions.DATA_MESSAGE_ARRIVE.STATE.create({ | ||
message, | ||
finishTask: resolve, | ||
}), | ||
); | ||
}); | ||
} | ||
}; | ||
|
||
export const registerBackgroundMessageHandler = () => { | ||
messaging().setBackgroundMessageHandler(backgroundMessageHandler); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
100 changes: 100 additions & 0 deletions
100
src/store/modules/PushNotifications/sagas/handleDataMessageSaga.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
// SPDX-License-Identifier: ice License 1.0 | ||
|
||
import {COLORS} from '@constants/colors'; | ||
import notifee, { | ||
AndroidImportance, | ||
AndroidStyle, | ||
Notification, | ||
TimestampTrigger, | ||
TriggerType, | ||
} from '@notifee/react-native'; | ||
import {dayjs} from '@services/dayjs'; | ||
import {logError} from '@services/logging'; | ||
import {isAppActiveSelector} from '@store/modules/AppCommon/selectors'; | ||
import {PushNotificationsActions} from '@store/modules/PushNotifications/actions'; | ||
import {CHANNEL_ID} from '@store/modules/PushNotifications/constants'; | ||
import { | ||
DataMessageType, | ||
DelayedDataMessageData, | ||
} from '@store/modules/PushNotifications/types'; | ||
import {isDataOnlyMessage} from '@store/modules/PushNotifications/utils/isDataOnlyMessage'; | ||
import {call, SagaReturnType, select} from 'redux-saga/effects'; | ||
|
||
export function* handleDataMessageSaga({ | ||
payload: {message, finishTask}, | ||
}: ReturnType< | ||
typeof PushNotificationsActions.DATA_MESSAGE_ARRIVE.STATE.create | ||
>) { | ||
try { | ||
if (!isDataOnlyMessage(message)) { | ||
throw new Error('Message is not data-only'); | ||
} | ||
|
||
switch (message.data?.type as DataMessageType) { | ||
case 'delayed': | ||
yield call(handleDelayedDataMessage, { | ||
data: message.data as DelayedDataMessageData, | ||
}); | ||
break; | ||
default: | ||
logError(`Unable to handle data message type: ${message.data?.type}`); | ||
} | ||
} finally { | ||
if (finishTask) { | ||
yield call(finishTask); | ||
} | ||
} | ||
} | ||
|
||
function* handleDelayedDataMessage({data}: {data: DelayedDataMessageData}) { | ||
const {title, body, imageUrl, minDelaySec, maxDelaySec} = data; | ||
|
||
const minDelay = minDelaySec ? parseInt(minDelaySec, 10) : 0; | ||
const maxDelay = maxDelaySec ? parseInt(maxDelaySec, 10) : 0; | ||
|
||
if (isNaN(minDelay) || isNaN(maxDelay)) { | ||
throw new Error( | ||
`Delayed message min / max delay is incorrect, minDelay=${minDelaySec} maxDelay=${maxDelaySec}`, | ||
); | ||
} | ||
|
||
const isAppActive: SagaReturnType<typeof isAppActiveSelector> = yield select( | ||
isAppActiveSelector, | ||
); | ||
|
||
const delaySec = isAppActive | ||
? 0 | ||
: Math.round(minDelay + Math.random() * (maxDelay - minDelay)); | ||
|
||
const notification: Notification = { | ||
title, | ||
body, | ||
data, | ||
android: { | ||
channelId: CHANNEL_ID, | ||
smallIcon: 'ic_stat_notification', | ||
sound: 'default', | ||
color: COLORS.primaryLight, | ||
...(imageUrl | ||
? { | ||
largeIcon: imageUrl, | ||
style: {type: AndroidStyle.BIGPICTURE, picture: imageUrl}, | ||
} | ||
: {}), | ||
importance: AndroidImportance.HIGH, | ||
pressAction: { | ||
id: 'default', | ||
}, | ||
}, | ||
}; | ||
|
||
if (delaySec > 0) { | ||
const trigger: TimestampTrigger = { | ||
type: TriggerType.TIMESTAMP, | ||
timestamp: dayjs().add(delaySec, 's').valueOf(), | ||
}; | ||
yield call(notifee.createTriggerNotification, notification, trigger); | ||
} else { | ||
yield call(notifee.displayNotification, notification); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
// SPDX-License-Identifier: ice License 1.0 | ||
|
||
export type DataMessageType = 'delayed'; | ||
|
||
export type DelayedDataMessageData = { | ||
title: string; | ||
body: string; | ||
minDelaySec?: string; | ||
maxDelaySec?: string; | ||
imageUrl?: string; | ||
}; |
10 changes: 10 additions & 0 deletions
10
src/store/modules/PushNotifications/utils/isDataOnlyMessage.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
// SPDX-License-Identifier: ice License 1.0 | ||
|
||
import {FirebaseMessagingTypes} from '@react-native-firebase/messaging'; | ||
import {isEmpty} from 'lodash'; | ||
|
||
export const isDataOnlyMessage = ( | ||
message: FirebaseMessagingTypes.RemoteMessage, | ||
) => { | ||
return message.data && isEmpty(message.notification ?? {}); | ||
}; |