Last active
April 5, 2023 11:04
-
-
Save vshkl/5de94cf51a621865df8e436469749c7e to your computer and use it in GitHub Desktop.
Local notification management | react-native, typescript, notifee.
This file contains hidden or 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
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