Added main functionality
This commit is contained in:
129
apk/utils/birthday-notifications.ts
Normal file
129
apk/utils/birthday-notifications.ts
Normal file
@@ -0,0 +1,129 @@
|
||||
import { Platform } from "react-native";
|
||||
import * as Notifications from "expo-notifications";
|
||||
|
||||
import type { BirthdayEntry } from "@/context/birthdays-context";
|
||||
|
||||
const CHANNEL_ID = "birthdays";
|
||||
const NOTIFICATION_HOUR = 9;
|
||||
const NOTIFICATION_MINUTE = 0;
|
||||
const IDENTIFIER_PREFIX = "birthday-";
|
||||
|
||||
Notifications.setNotificationHandler({
|
||||
handleNotification: async () => ({
|
||||
shouldPlaySound: true,
|
||||
shouldSetBadge: false,
|
||||
shouldShowBanner: true,
|
||||
shouldShowList: true,
|
||||
}),
|
||||
});
|
||||
|
||||
function getNotificationIdentifier(id: string) {
|
||||
return `${IDENTIFIER_PREFIX}${id}`;
|
||||
}
|
||||
|
||||
function parseBirthdayDate(date: string) {
|
||||
return new Date(`${date}T00:00:00`);
|
||||
}
|
||||
|
||||
async function configureAndroidChannel() {
|
||||
if (Platform.OS !== "android") {
|
||||
return;
|
||||
}
|
||||
|
||||
await Notifications.setNotificationChannelAsync(CHANNEL_ID, {
|
||||
name: "Birthdays",
|
||||
description: "Birthday reminders",
|
||||
importance: Notifications.AndroidImportance.DEFAULT,
|
||||
});
|
||||
}
|
||||
|
||||
async function hasNotificationPermissionAsync() {
|
||||
const settings = await Notifications.getPermissionsAsync();
|
||||
|
||||
return (
|
||||
settings.granted ||
|
||||
settings.ios?.status === Notifications.IosAuthorizationStatus.PROVISIONAL
|
||||
);
|
||||
}
|
||||
|
||||
async function ensureNotificationPermissionAsync() {
|
||||
await configureAndroidChannel();
|
||||
|
||||
if (await hasNotificationPermissionAsync()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const settings = await Notifications.requestPermissionsAsync({
|
||||
ios: {
|
||||
allowAlert: true,
|
||||
allowBadge: false,
|
||||
allowSound: true,
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
settings.granted ||
|
||||
settings.ios?.status === Notifications.IosAuthorizationStatus.PROVISIONAL
|
||||
);
|
||||
}
|
||||
|
||||
async function clearStaleBirthdayNotifications(birthdays: BirthdayEntry[]) {
|
||||
const validIdentifiers = new Set(
|
||||
birthdays.map((birthday) => getNotificationIdentifier(birthday.id))
|
||||
);
|
||||
const scheduledNotifications =
|
||||
await Notifications.getAllScheduledNotificationsAsync();
|
||||
|
||||
await Promise.all(
|
||||
scheduledNotifications
|
||||
.filter(
|
||||
(notification) =>
|
||||
notification.identifier.startsWith(IDENTIFIER_PREFIX) &&
|
||||
!validIdentifiers.has(notification.identifier)
|
||||
)
|
||||
.map((notification) =>
|
||||
Notifications.cancelScheduledNotificationAsync(notification.identifier)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
async function scheduleBirthdayNotification(birthday: BirthdayEntry) {
|
||||
const parsedDate = parseBirthdayDate(birthday.date);
|
||||
const month = parsedDate.getMonth();
|
||||
const day = parsedDate.getDate();
|
||||
|
||||
await Notifications.cancelScheduledNotificationAsync(
|
||||
getNotificationIdentifier(birthday.id)
|
||||
);
|
||||
|
||||
await Notifications.scheduleNotificationAsync({
|
||||
identifier: getNotificationIdentifier(birthday.id),
|
||||
content: {
|
||||
title: `Today is ${birthday.name}'s birthday`,
|
||||
body: `Wish ${birthday.name} a happy birthday!`,
|
||||
sound: true,
|
||||
},
|
||||
trigger: {
|
||||
type: Notifications.SchedulableTriggerInputTypes.YEARLY,
|
||||
channelId: Platform.OS === "android" ? CHANNEL_ID : undefined,
|
||||
month,
|
||||
day,
|
||||
hour: NOTIFICATION_HOUR,
|
||||
minute: NOTIFICATION_MINUTE,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function syncBirthdayNotifications(birthdays: BirthdayEntry[]) {
|
||||
if (Platform.OS === "web") {
|
||||
return;
|
||||
}
|
||||
|
||||
const permissionGranted = await ensureNotificationPermissionAsync();
|
||||
if (!permissionGranted) {
|
||||
return;
|
||||
}
|
||||
|
||||
await clearStaleBirthdayNotifications(birthdays);
|
||||
await Promise.all(birthdays.map(scheduleBirthdayNotification));
|
||||
}
|
||||
Reference in New Issue
Block a user