Skip to content

Instantly share code, notes, and snippets.

@vshkl
Last active April 5, 2023 11:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vshkl/5de94cf51a621865df8e436469749c7e to your computer and use it in GitHub Desktop.
Save vshkl/5de94cf51a621865df8e436469749c7e to your computer and use it in GitHub Desktop.
Local notification management | react-native, typescript, notifee.
import { Alert } from 'react-native'
import notifee, {
TriggerType,
RepeatFrequency,
AuthorizationStatus,
EventType,
type TimestampTrigger,
type Notification,
type Event,
type EventDetail,
type AndroidAction,
type IOSNotificationCategoryAction,
} from '@notifee/react-native'
import moment from 'moment'
import { useEffect } from 'react'
/**
* A type for describing a side of the lens in the context of scheduled notifications.
*/
type Side = 'left' | 'right' | 'both'
/**
* A type that describe shape of the data payload that is sent with the scheduled notifications.
*/
type NotificationData = {
/**
* Side of the lens
*/
side: Side,
/**
* Number of days after which notification should be shown
*/
inDays: number,
}
/**
* An enum representing notification ids depending on the lens' side.
*/
enum NotificationId {
/**
* Notification for left lens only.
*/
LEFT = 'id-notification-left',
/**
* Notification for right lens only.
*/
RIGHT = 'id-notification-right',
/**
* Notification for both left and right lenses.
*/
BOTH = 'id-notification-both',
}
/**
* An enum representing notification action ids depending on the kind of the action.
*/
enum ActionId {
/**
* Main notification action. I.e., when the whole notification is clicked.
*/
MAIN = 'id-action-main',
/**
* Lens change action. When "Change" quick action is clicked.
*/
CHANGE = 'id-action-change',
/**
* Lens change action. When "Postpone" quick action is clicked.
*/
POSTPONE = 'id-action-postpone',
}
/**
* Function used to initialize listerting and reacting to quick actions from the notification when
* the app is in the backhround. This function need to be called as high in the app's hierarchy as
* alogside the place, where the main application is registerd.
*/
const listernToNotificationActionsBackground = () => {
notifee.onBackgroundEvent(async (event: Event) => {
console.log(event)
await processNotificationQuckActions(event)
})
}
/**
* Function used to initialize listerting and reacting to quick actions from the notification when
* the app is in the foreground.
*/
const listernToNotificationActionsForeground = () => {
useEffect(() => notifee.onForegroundEvent(async (event: Event) => {
await processNotificationQuckActions(event)
}), [])
}
/**
* Function used to schedule a new lens change notification.
*
* @param side Lens's side. Side of the lens in the context of scheduled notifications.
* @param inDays Days to notification. Number of days after which notification should be shown.
*/
const scheduleLensNotification = async (
side: Side,
inDays: number,
): Promise<void> => {
const triggerTime = moment('10:00', 'HH:mm').add(inDays, 'days').toDate()
await checkNotificationPermission()
await notifee.cancelTriggerNotification(({
left: NotificationId.LEFT,
right: NotificationId.RIGHT,
both: NotificationId.BOTH,
}[side]))
const channelId: string = await notifee.createChannel({
id: 'lenses-change-notification',
name: 'Lenses Notification Chanel',
})
const notificationId: NotificationId = ({
left: NotificationId.LEFT,
right: NotificationId.RIGHT,
both: NotificationId.BOTH,
}[side])
const notificationTitle: string = ({
left: 'Change lens',
right: 'Change lens',
both: 'Change lenses',
}[side])
const nofificationBody: string = ({
left: 'It is time to change your left lens!',
right: 'It is time to change your right lens!',
both: 'It is time to change your lenses!',
}[side])
const dataPayload: NotificationData = {
side,
inDays,
}
const actionsAndroid: AndroidAction[] = [
{
title: 'Change',
pressAction: {
id: ActionId.CHANGE,
},
},
{
title: 'Postpone',
pressAction: {
id: ActionId.POSTPONE,
},
},
]
// eslint-disable-next-line @typescript-eslint/naming-convention
const actionsiOS: IOSNotificationCategoryAction[] = [
{
id: ActionId.CHANGE,
title: 'Change',
},
{
id: ActionId.POSTPONE,
title: 'Postpone',
},
]
const notification: Notification = {
id: notificationId,
title: notificationTitle,
body: nofificationBody,
data: dataPayload,
android: {
channelId,
actions: actionsAndroid,
pressAction: {
id: ActionId.MAIN,
},
},
ios: {
categoryId: ActionId.MAIN,
},
}
const trigger: TimestampTrigger = {
type: TriggerType.TIMESTAMP,
timestamp: triggerTime.valueOf(),
repeatFrequency: RepeatFrequency.NONE,
}
await notifee.createTriggerNotification(notification, trigger)
await notifee.setNotificationCategories([
{
id: ActionId.MAIN,
actions: actionsiOS,
},
])
}
const checkNotificationPermission = async (): Promise<void> => {
await notifee.requestPermission()
const settings = await notifee.getNotificationSettings()
if (settings.authorizationStatus === AuthorizationStatus.DENIED) {
Alert.alert(
'Permission needed',
'Lenses need notification permission to remind you about changing your lenses',
)
}
}
const processNotificationQuckActions = async ({ type, detail }: Event) => {
if (type === EventType.ACTION_PRESS) {
const data: NotificationData | undefined = createNotificationData(detail)
if (data) {
await scheduleLensNotification(data.side, data.inDays)
}
}
}
const createNotificationData = (
detail: EventDetail,
): NotificationData | undefined => {
const { notification, pressAction }: EventDetail = detail
if (pressAction?.id === ActionId.CHANGE
&& notification?.data?.side
&& notification?.data?.inDays) {
return {
side: notification.data.side as Side,
inDays: notification.data.inDays as number,
}
}
if (pressAction?.id === ActionId.POSTPONE
&& notification?.data?.side) {
return {
side: notification.data.side as Side,
inDays: 1,
}
}
return undefined
}
export {
scheduleLensNotification,
listernToNotificationActionsBackground,
listernToNotificationActionsForeground,
type Side,
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment