Added main functionality
This commit is contained in:
@@ -12,6 +12,9 @@
|
|||||||
"supportsTablet": true
|
"supportsTablet": true
|
||||||
},
|
},
|
||||||
"android": {
|
"android": {
|
||||||
|
"permissions": [
|
||||||
|
"SCHEDULE_EXACT_ALARM"
|
||||||
|
],
|
||||||
"adaptiveIcon": {
|
"adaptiveIcon": {
|
||||||
"backgroundColor": "#E6F4FE",
|
"backgroundColor": "#E6F4FE",
|
||||||
"foregroundImage": "./assets/images/android-icon-foreground.png",
|
"foregroundImage": "./assets/images/android-icon-foreground.png",
|
||||||
@@ -27,6 +30,7 @@
|
|||||||
},
|
},
|
||||||
"plugins": [
|
"plugins": [
|
||||||
"expo-router",
|
"expo-router",
|
||||||
|
"expo-notifications",
|
||||||
[
|
[
|
||||||
"expo-splash-screen",
|
"expo-splash-screen",
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,18 +1,126 @@
|
|||||||
// BirthdayItem.tsx
|
import { useState } from "react";
|
||||||
import { StyleSheet, Text, View } from "react-native";
|
import {
|
||||||
|
Modal,
|
||||||
|
Pressable,
|
||||||
|
StyleSheet,
|
||||||
|
Text,
|
||||||
|
View,
|
||||||
|
} from "react-native";
|
||||||
|
|
||||||
interface BirthdayItemProps {
|
interface BirthdayItemProps {
|
||||||
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
date: string;
|
date: string;
|
||||||
age?: number;
|
onDelete: (id: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function BirthdayItem({ name, date, age }: BirthdayItemProps) {
|
export function BirthdayItem({ id, name, date, onDelete }: BirthdayItemProps) {
|
||||||
|
const [menuOpen, setMenuOpen] = useState(false);
|
||||||
|
const [menuPosition, setMenuPosition] = useState({ top: 0, left: 0 });
|
||||||
|
|
||||||
|
const daysUntilNext = (birthdayDate: string) => {
|
||||||
|
const today = new Date();
|
||||||
|
const parsedDate = new Date(birthdayDate);
|
||||||
|
const month = parsedDate.getMonth();
|
||||||
|
const day = parsedDate.getDate();
|
||||||
|
const currentYear = today.getFullYear();
|
||||||
|
|
||||||
|
let target = new Date(currentYear, month, day);
|
||||||
|
if (target < today.setHours(0, 0, 0, 0)) {
|
||||||
|
target = new Date(currentYear + 1, month, day);
|
||||||
|
}
|
||||||
|
const msPerDay = 1000 * 60 * 60 * 24;
|
||||||
|
const diff = target.getTime() - new Date(today.setHours(0, 0, 0, 0)).getTime();
|
||||||
|
return Math.ceil(diff / msPerDay);
|
||||||
|
};
|
||||||
|
|
||||||
|
const computeNewAge = (birthdayDate: string) => {
|
||||||
|
const parsedDate = new Date(birthdayDate);
|
||||||
|
const today = new Date();
|
||||||
|
let age = today.getFullYear() - parsedDate.getFullYear();
|
||||||
|
|
||||||
|
const monthDiff = today.getMonth() - parsedDate.getMonth();
|
||||||
|
const dayDiff = today.getDate() - parsedDate.getDate();
|
||||||
|
|
||||||
|
if (monthDiff < 0 || (monthDiff === 0 && dayDiff < 0)) {
|
||||||
|
age--;
|
||||||
|
}
|
||||||
|
return age;
|
||||||
|
};
|
||||||
|
|
||||||
|
const days = daysUntilNext(date);
|
||||||
|
|
||||||
|
const handleOpenMenu = (event: {
|
||||||
|
nativeEvent: { pageX: number; pageY: number };
|
||||||
|
}) => {
|
||||||
|
setMenuPosition({
|
||||||
|
top: event.nativeEvent.pageY + 8,
|
||||||
|
left: event.nativeEvent.pageX - 136,
|
||||||
|
});
|
||||||
|
setMenuOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDelete = () => {
|
||||||
|
setMenuOpen(false);
|
||||||
|
onDelete(id);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.itemContainer}>
|
<>
|
||||||
<Text style={styles.name}>{name}</Text>
|
<View style={styles.itemContainer}>
|
||||||
{age && <Text style={styles.age}>Age: {age}</Text>}
|
<View style={styles.contentRow}>
|
||||||
</View>
|
<View style={styles.textContainer}>
|
||||||
|
<Text style={styles.name}>{name}</Text>
|
||||||
|
{days === 0 ? (
|
||||||
|
<Text style={styles.age}>
|
||||||
|
{name} makes {computeNewAge(date)} years today!
|
||||||
|
</Text>
|
||||||
|
) : (
|
||||||
|
date && <Text style={styles.age}>Days remaining: {days}</Text>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
<Pressable
|
||||||
|
accessibilityLabel={`Open actions for ${name}`}
|
||||||
|
hitSlop={8}
|
||||||
|
onPress={handleOpenMenu}
|
||||||
|
style={styles.menuTrigger}
|
||||||
|
>
|
||||||
|
<Text style={styles.menuTriggerText}>⋮</Text>
|
||||||
|
</Pressable>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<Modal
|
||||||
|
animationType="fade"
|
||||||
|
onRequestClose={() => setMenuOpen(false)}
|
||||||
|
transparent
|
||||||
|
visible={menuOpen}
|
||||||
|
>
|
||||||
|
<View style={styles.menuBackdrop}>
|
||||||
|
<Pressable
|
||||||
|
onPress={() => setMenuOpen(false)}
|
||||||
|
style={styles.menuBackdropPressable}
|
||||||
|
/>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
styles.menu,
|
||||||
|
{
|
||||||
|
top: Math.max(menuPosition.top, 12),
|
||||||
|
left: Math.max(menuPosition.left, 12),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Pressable disabled style={[styles.menuItem, styles.menuItemDisabled]}>
|
||||||
|
<Text style={[styles.menuItemText, styles.menuItemTextDisabled]}>
|
||||||
|
Edit
|
||||||
|
</Text>
|
||||||
|
</Pressable>
|
||||||
|
<Pressable onPress={handleDelete} style={styles.menuItem}>
|
||||||
|
<Text style={[styles.menuItemText, styles.deleteText]}>Delete</Text>
|
||||||
|
</Pressable>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,6 +132,27 @@ const styles = StyleSheet.create({
|
|||||||
elevation: 2,
|
elevation: 2,
|
||||||
marginBottom: 10,
|
marginBottom: 10,
|
||||||
},
|
},
|
||||||
|
contentRow: {
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
gap: 12,
|
||||||
|
},
|
||||||
|
textContainer: {
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
menuTrigger: {
|
||||||
|
alignItems: "center",
|
||||||
|
borderRadius: 999,
|
||||||
|
height: 36,
|
||||||
|
justifyContent: "center",
|
||||||
|
width: 36,
|
||||||
|
},
|
||||||
|
menuTriggerText: {
|
||||||
|
color: "#444",
|
||||||
|
fontSize: 22,
|
||||||
|
lineHeight: 22,
|
||||||
|
},
|
||||||
name: {
|
name: {
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: "bold",
|
fontWeight: "bold",
|
||||||
@@ -35,4 +164,40 @@ const styles = StyleSheet.create({
|
|||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
marginTop: 5,
|
marginTop: 5,
|
||||||
},
|
},
|
||||||
|
menuBackdrop: {
|
||||||
|
...StyleSheet.absoluteFillObject,
|
||||||
|
},
|
||||||
|
menuBackdropPressable: {
|
||||||
|
...StyleSheet.absoluteFillObject,
|
||||||
|
},
|
||||||
|
menu: {
|
||||||
|
backgroundColor: "#fff",
|
||||||
|
borderRadius: 8,
|
||||||
|
elevation: 8,
|
||||||
|
minWidth: 140,
|
||||||
|
overflow: "hidden",
|
||||||
|
position: "absolute",
|
||||||
|
shadowColor: "#000",
|
||||||
|
shadowOffset: { width: 0, height: 4 },
|
||||||
|
shadowOpacity: 0.15,
|
||||||
|
shadowRadius: 12,
|
||||||
|
},
|
||||||
|
menuItem: {
|
||||||
|
paddingHorizontal: 14,
|
||||||
|
paddingVertical: 12,
|
||||||
|
},
|
||||||
|
menuItemDisabled: {
|
||||||
|
opacity: 0.45,
|
||||||
|
},
|
||||||
|
menuItemText: {
|
||||||
|
color: "#222",
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: "500",
|
||||||
|
},
|
||||||
|
menuItemTextDisabled: {
|
||||||
|
color: "#666",
|
||||||
|
},
|
||||||
|
deleteText: {
|
||||||
|
color: "#c53a3a",
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ interface SectionData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function BirthdayList() {
|
export function BirthdayList() {
|
||||||
const { birthdays } = useBirthdays();
|
const { birthdays, deleteBirthday } = useBirthdays();
|
||||||
const groupedData = groupBirthdaysByDate(birthdays);
|
const groupedData = groupBirthdaysByDate(birthdays);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -22,7 +22,12 @@ export function BirthdayList() {
|
|||||||
sections={groupedData}
|
sections={groupedData}
|
||||||
keyExtractor={(item) => item.id}
|
keyExtractor={(item) => item.id}
|
||||||
renderItem={({ item }) => (
|
renderItem={({ item }) => (
|
||||||
<BirthdayItem name={item.name} date={item.date} />
|
<BirthdayItem
|
||||||
|
id={item.id}
|
||||||
|
name={item.name}
|
||||||
|
date={item.date}
|
||||||
|
onDelete={deleteBirthday}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
style={styles.list}
|
style={styles.list}
|
||||||
contentContainerStyle={styles.listContent}
|
contentContainerStyle={styles.listContent}
|
||||||
@@ -48,8 +53,23 @@ export function BirthdayList() {
|
|||||||
// Helper function to group and sort
|
// Helper function to group and sort
|
||||||
function groupBirthdaysByDate(data: BirthdayEntry[]): SectionData[] {
|
function groupBirthdaysByDate(data: BirthdayEntry[]): SectionData[] {
|
||||||
// Sort by date
|
// Sort by date
|
||||||
|
const today = new Date();
|
||||||
|
const currentYear = today.getFullYear();
|
||||||
|
|
||||||
|
const daysUntilNext = (month: number, day: number) => {
|
||||||
|
let target = new Date(currentYear, month, day);
|
||||||
|
if(target < today.setHours(0, 0, 0, 0)){
|
||||||
|
target = new Date(currentYear + 1, month, day);
|
||||||
|
}
|
||||||
|
const msPerDay = 1000 * 60 * 60 * 24;
|
||||||
|
const diff = target - new Date(today.setHours(0,0,0,0));
|
||||||
|
return Math.ceil(diff / msPerDay);
|
||||||
|
};
|
||||||
|
|
||||||
const sorted = [...data].sort((a, b) => {
|
const sorted = [...data].sort((a, b) => {
|
||||||
return parseBirthdayDate(a.date).getTime() - parseBirthdayDate(b.date).getTime();
|
const dateA = parseBirthdayDate(a);
|
||||||
|
const dateB = parseBirthdayDate(b);
|
||||||
|
return daysUntilNext(dateA.getMonth(), dateA.getDate()) - daysUntilNext(dateB.getMonth(), dateB.getDate());
|
||||||
});
|
});
|
||||||
|
|
||||||
// Group by date
|
// Group by date
|
||||||
|
|||||||
@@ -2,10 +2,13 @@ import {
|
|||||||
createContext,
|
createContext,
|
||||||
ReactNode,
|
ReactNode,
|
||||||
useContext,
|
useContext,
|
||||||
|
useEffect,
|
||||||
useMemo,
|
useMemo,
|
||||||
useState,
|
useState,
|
||||||
} from "react";
|
} from "react";
|
||||||
|
import AsyncStorage from "@react-native-async-storage/async-storage";
|
||||||
|
|
||||||
|
import { syncBirthdayNotifications } from "@/utils/birthday-notifications";
|
||||||
export interface BirthdayEntry {
|
export interface BirthdayEntry {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
@@ -20,14 +23,70 @@ interface AddBirthdayInput {
|
|||||||
interface BirthdaysContextValue {
|
interface BirthdaysContextValue {
|
||||||
birthdays: BirthdayEntry[];
|
birthdays: BirthdayEntry[];
|
||||||
addBirthday: (input: AddBirthdayInput) => void;
|
addBirthday: (input: AddBirthdayInput) => void;
|
||||||
|
deleteBirthday: (id: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const STORAGE_KEY = "birthdays";
|
||||||
|
|
||||||
const BirthdaysContext = createContext<BirthdaysContextValue | undefined>(
|
const BirthdaysContext = createContext<BirthdaysContextValue | undefined>(
|
||||||
undefined
|
undefined
|
||||||
);
|
);
|
||||||
|
|
||||||
export function BirthdaysProvider({ children }: { children: ReactNode }) {
|
export function BirthdaysProvider({ children }: { children: ReactNode }) {
|
||||||
const [birthdays, setBirthdays] = useState<BirthdayEntry[]>([]);
|
const [birthdays, setBirthdays] = useState<BirthdayEntry[]>([]);
|
||||||
|
const [isLoaded, setIsLoaded] = useState(false);
|
||||||
|
|
||||||
|
// Load birthdays on app start from storage
|
||||||
|
useEffect(() => {
|
||||||
|
const loadBirthdays = async () => {
|
||||||
|
try {
|
||||||
|
const stored = await AsyncStorage.getItem(STORAGE_KEY);
|
||||||
|
if (stored) {
|
||||||
|
setBirthdays(JSON.parse(stored));
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
console.log("Error loading birthdays", e);
|
||||||
|
} finally {
|
||||||
|
setIsLoaded(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
loadBirthdays();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Save birthdays whenever they change
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isLoaded) return;
|
||||||
|
|
||||||
|
const saveBirthdays = async () => {
|
||||||
|
try {
|
||||||
|
await AsyncStorage.setItem(
|
||||||
|
STORAGE_KEY,
|
||||||
|
JSON.stringify(birthdays)
|
||||||
|
);
|
||||||
|
} catch(e) {
|
||||||
|
console.log("Error saving birthdays", e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
saveBirthdays();
|
||||||
|
}, [birthdays, isLoaded]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isLoaded) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const synchronizeNotifications = async () => {
|
||||||
|
try {
|
||||||
|
await syncBirthdayNotifications(birthdays);
|
||||||
|
} catch (error) {
|
||||||
|
console.log("Error syncing birthday notifications", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
synchronizeNotifications();
|
||||||
|
}, [birthdays, isLoaded]);
|
||||||
|
|
||||||
const value = useMemo(
|
const value = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
@@ -48,6 +107,11 @@ export function BirthdaysProvider({ children }: { children: ReactNode }) {
|
|||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
|
deleteBirthday: (id: string) => {
|
||||||
|
setBirthdays((currentBirthdays) =>
|
||||||
|
currentBirthdays.filter((birthday) => birthday.id !== id)
|
||||||
|
);
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
[birthdays]
|
[birthdays]
|
||||||
);
|
);
|
||||||
|
|||||||
178
apk/package-lock.json
generated
178
apk/package-lock.json
generated
@@ -9,6 +9,7 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@expo/vector-icons": "^15.0.3",
|
"@expo/vector-icons": "^15.0.3",
|
||||||
|
"@react-native-async-storage/async-storage": "2.2.0",
|
||||||
"@react-native-community/datetimepicker": "^9.1.0",
|
"@react-native-community/datetimepicker": "^9.1.0",
|
||||||
"@react-navigation/bottom-tabs": "^7.4.0",
|
"@react-navigation/bottom-tabs": "^7.4.0",
|
||||||
"@react-navigation/elements": "^2.6.3",
|
"@react-navigation/elements": "^2.6.3",
|
||||||
@@ -19,6 +20,7 @@
|
|||||||
"expo-haptics": "~15.0.8",
|
"expo-haptics": "~15.0.8",
|
||||||
"expo-image": "~3.0.11",
|
"expo-image": "~3.0.11",
|
||||||
"expo-linking": "~8.0.11",
|
"expo-linking": "~8.0.11",
|
||||||
|
"expo-notifications": "~0.32.16",
|
||||||
"expo-router": "~6.0.23",
|
"expo-router": "~6.0.23",
|
||||||
"expo-splash-screen": "~31.0.13",
|
"expo-splash-screen": "~31.0.13",
|
||||||
"expo-status-bar": "~3.0.9",
|
"expo-status-bar": "~3.0.9",
|
||||||
@@ -2305,6 +2307,12 @@
|
|||||||
"url": "https://github.com/sponsors/nzakas"
|
"url": "https://github.com/sponsors/nzakas"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@ide/backoff": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@ide/backoff/-/backoff-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-F0YfUDjvT+Mtt/R4xdl2X0EYCHMMiJqNLdxHD++jDT5ydEFIyqbCHh51Qx2E211dgZprPKhV7sHmnXKpLuvc5g==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@isaacs/fs-minipass": {
|
"node_modules/@isaacs/fs-minipass": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
|
||||||
@@ -2798,6 +2806,18 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@react-native-async-storage/async-storage": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-gvRvjR5JAaUZF8tv2Kcq/Gbt3JHwbKFYfmb445rhOj6NUMx3qPLixmDx5pZAyb9at1bYvJ4/eTUipU5aki45xw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"merge-options": "^3.0.4"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react-native": "^0.0.0-0 || >=0.65 <1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@react-native-community/datetimepicker": {
|
"node_modules/@react-native-community/datetimepicker": {
|
||||||
"version": "9.1.0",
|
"version": "9.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@react-native-community/datetimepicker/-/datetimepicker-9.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@react-native-community/datetimepicker/-/datetimepicker-9.1.0.tgz",
|
||||||
@@ -4303,6 +4323,19 @@
|
|||||||
"integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==",
|
"integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/assert": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"call-bind": "^1.0.2",
|
||||||
|
"is-nan": "^1.3.2",
|
||||||
|
"object-is": "^1.1.5",
|
||||||
|
"object.assign": "^4.1.4",
|
||||||
|
"util": "^0.12.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/async-function": {
|
"node_modules/async-function": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz",
|
||||||
@@ -4323,7 +4356,6 @@
|
|||||||
"version": "1.0.7",
|
"version": "1.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
|
||||||
"integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
|
"integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"possible-typed-array-names": "^1.0.0"
|
"possible-typed-array-names": "^1.0.0"
|
||||||
@@ -4544,6 +4576,12 @@
|
|||||||
"@babel/core": "^7.0.0"
|
"@babel/core": "^7.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/badgin": {
|
||||||
|
"version": "1.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/badgin/-/badgin-1.2.3.tgz",
|
||||||
|
"integrity": "sha512-NQGA7LcfCpSzIbGRbkgjgdWkjy7HI+Th5VLxTJfW5EeaAf3fnS+xWQaQOCYiny+q6QSvxqoSO04vCx+4u++EJw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/balanced-match": {
|
"node_modules/balanced-match": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
@@ -4748,7 +4786,6 @@
|
|||||||
"version": "1.0.9",
|
"version": "1.0.9",
|
||||||
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz",
|
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz",
|
||||||
"integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==",
|
"integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"call-bind-apply-helpers": "^1.0.2",
|
"call-bind-apply-helpers": "^1.0.2",
|
||||||
@@ -4767,7 +4804,6 @@
|
|||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
||||||
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
|
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"es-errors": "^1.3.0",
|
"es-errors": "^1.3.0",
|
||||||
@@ -4781,7 +4817,6 @@
|
|||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
|
||||||
"integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
|
"integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"call-bind-apply-helpers": "^1.0.2",
|
"call-bind-apply-helpers": "^1.0.2",
|
||||||
@@ -5271,7 +5306,6 @@
|
|||||||
"version": "1.1.4",
|
"version": "1.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
|
||||||
"integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
|
"integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"es-define-property": "^1.0.0",
|
"es-define-property": "^1.0.0",
|
||||||
@@ -5298,7 +5332,6 @@
|
|||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
|
||||||
"integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
|
"integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"define-data-property": "^1.0.1",
|
"define-data-property": "^1.0.1",
|
||||||
@@ -5390,7 +5423,6 @@
|
|||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||||
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
|
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"call-bind-apply-helpers": "^1.0.1",
|
"call-bind-apply-helpers": "^1.0.1",
|
||||||
@@ -5519,7 +5551,6 @@
|
|||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
||||||
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
|
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
@@ -5566,7 +5597,6 @@
|
|||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
|
||||||
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
|
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"es-errors": "^1.3.0"
|
"es-errors": "^1.3.0"
|
||||||
@@ -6112,6 +6142,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/expo-application": {
|
||||||
|
"version": "7.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/expo-application/-/expo-application-7.0.8.tgz",
|
||||||
|
"integrity": "sha512-qFGyxk7VJbrNOQWBbE09XUuGuvkOgFS9QfToaK2FdagM2aQ+x3CvGV2DuVgl/l4ZxPgIf3b/MNh9xHpwSwn74Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"expo": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/expo-asset": {
|
"node_modules/expo-asset": {
|
||||||
"version": "12.0.12",
|
"version": "12.0.12",
|
||||||
"resolved": "https://registry.npmjs.org/expo-asset/-/expo-asset-12.0.12.tgz",
|
"resolved": "https://registry.npmjs.org/expo-asset/-/expo-asset-12.0.12.tgz",
|
||||||
@@ -6244,6 +6283,26 @@
|
|||||||
"react-native": "*"
|
"react-native": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/expo-notifications": {
|
||||||
|
"version": "0.32.16",
|
||||||
|
"resolved": "https://registry.npmjs.org/expo-notifications/-/expo-notifications-0.32.16.tgz",
|
||||||
|
"integrity": "sha512-QQD/UA6v7LgvwIJ+tS7tSvqJZkdp0nCSj9MxsDk/jU1GttYdK49/5L2LvE/4U0H7sNBz1NZAyhDZozg8xgBLXw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@expo/image-utils": "^0.8.8",
|
||||||
|
"@ide/backoff": "^1.0.0",
|
||||||
|
"abort-controller": "^3.0.0",
|
||||||
|
"assert": "^2.0.0",
|
||||||
|
"badgin": "^1.1.5",
|
||||||
|
"expo-application": "~7.0.8",
|
||||||
|
"expo-constants": "~18.0.13"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"expo": "*",
|
||||||
|
"react": "*",
|
||||||
|
"react-native": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/expo-router": {
|
"node_modules/expo-router": {
|
||||||
"version": "6.0.23",
|
"version": "6.0.23",
|
||||||
"resolved": "https://registry.npmjs.org/expo-router/-/expo-router-6.0.23.tgz",
|
"resolved": "https://registry.npmjs.org/expo-router/-/expo-router-6.0.23.tgz",
|
||||||
@@ -6951,7 +7010,6 @@
|
|||||||
"version": "0.3.5",
|
"version": "0.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
|
||||||
"integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==",
|
"integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"is-callable": "^1.2.7"
|
"is-callable": "^1.2.7"
|
||||||
@@ -7045,7 +7103,6 @@
|
|||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz",
|
||||||
"integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==",
|
"integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
@@ -7073,7 +7130,6 @@
|
|||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
||||||
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
|
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"call-bind-apply-helpers": "^1.0.2",
|
"call-bind-apply-helpers": "^1.0.2",
|
||||||
@@ -7116,7 +7172,6 @@
|
|||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
|
||||||
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
|
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"dunder-proto": "^1.0.1",
|
"dunder-proto": "^1.0.1",
|
||||||
@@ -7266,7 +7321,6 @@
|
|||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
|
||||||
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
|
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
@@ -7307,7 +7361,6 @@
|
|||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
|
||||||
"integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
|
"integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"es-define-property": "^1.0.0"
|
"es-define-property": "^1.0.0"
|
||||||
@@ -7336,7 +7389,6 @@
|
|||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
||||||
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
|
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
@@ -7349,7 +7401,6 @@
|
|||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
|
||||||
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
|
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"has-symbols": "^1.0.3"
|
"has-symbols": "^1.0.3"
|
||||||
@@ -7605,6 +7656,22 @@
|
|||||||
"loose-envify": "^1.0.0"
|
"loose-envify": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/is-arguments": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"call-bound": "^1.0.2",
|
||||||
|
"has-tostringtag": "^1.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/is-array-buffer": {
|
"node_modules/is-array-buffer": {
|
||||||
"version": "3.0.5",
|
"version": "3.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
|
||||||
@@ -7709,7 +7776,6 @@
|
|||||||
"version": "1.2.7",
|
"version": "1.2.7",
|
||||||
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
|
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
|
||||||
"integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
|
"integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
@@ -7822,7 +7888,6 @@
|
|||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz",
|
||||||
"integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==",
|
"integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"call-bound": "^1.0.4",
|
"call-bound": "^1.0.4",
|
||||||
@@ -7864,6 +7929,22 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/is-nan": {
|
||||||
|
"version": "1.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz",
|
||||||
|
"integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"call-bind": "^1.0.0",
|
||||||
|
"define-properties": "^1.1.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/is-negative-zero": {
|
"node_modules/is-negative-zero": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz",
|
||||||
@@ -7903,11 +7984,19 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/is-plain-obj": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/is-regex": {
|
"node_modules/is-regex": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz",
|
||||||
"integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==",
|
"integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"call-bound": "^1.0.2",
|
"call-bound": "^1.0.2",
|
||||||
@@ -7990,7 +8079,6 @@
|
|||||||
"version": "1.1.15",
|
"version": "1.1.15",
|
||||||
"resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz",
|
"resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz",
|
||||||
"integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==",
|
"integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"which-typed-array": "^1.1.16"
|
"which-typed-array": "^1.1.16"
|
||||||
@@ -8881,7 +8969,6 @@
|
|||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||||
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
|
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
@@ -8893,6 +8980,18 @@
|
|||||||
"integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==",
|
"integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/merge-options": {
|
||||||
|
"version": "3.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/merge-options/-/merge-options-3.0.4.tgz",
|
||||||
|
"integrity": "sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"is-plain-obj": "^2.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/merge-stream": {
|
"node_modules/merge-stream": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
|
||||||
@@ -9516,11 +9615,26 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/object-is": {
|
||||||
|
"version": "1.1.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz",
|
||||||
|
"integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"call-bind": "^1.0.7",
|
||||||
|
"define-properties": "^1.2.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/object-keys": {
|
"node_modules/object-keys": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
|
||||||
"integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
|
"integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
@@ -9530,7 +9644,6 @@
|
|||||||
"version": "4.1.7",
|
"version": "4.1.7",
|
||||||
"resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz",
|
"resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz",
|
||||||
"integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==",
|
"integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"call-bind": "^1.0.8",
|
"call-bind": "^1.0.8",
|
||||||
@@ -10005,7 +10118,6 @@
|
|||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
|
||||||
"integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==",
|
"integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
@@ -10956,7 +11068,6 @@
|
|||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz",
|
||||||
"integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==",
|
"integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"call-bound": "^1.0.2",
|
"call-bound": "^1.0.2",
|
||||||
@@ -11106,7 +11217,6 @@
|
|||||||
"version": "1.2.2",
|
"version": "1.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
|
||||||
"integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
|
"integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"define-data-property": "^1.1.4",
|
"define-data-property": "^1.1.4",
|
||||||
@@ -12327,6 +12437,19 @@
|
|||||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/util": {
|
||||||
|
"version": "0.12.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz",
|
||||||
|
"integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"inherits": "^2.0.3",
|
||||||
|
"is-arguments": "^1.0.4",
|
||||||
|
"is-generator-function": "^1.0.7",
|
||||||
|
"is-typed-array": "^1.1.3",
|
||||||
|
"which-typed-array": "^1.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/utils-merge": {
|
"node_modules/utils-merge": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
||||||
@@ -12714,7 +12837,6 @@
|
|||||||
"version": "1.1.20",
|
"version": "1.1.20",
|
||||||
"resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz",
|
"resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz",
|
||||||
"integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==",
|
"integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"available-typed-arrays": "^1.0.7",
|
"available-typed-arrays": "^1.0.7",
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@expo/vector-icons": "^15.0.3",
|
"@expo/vector-icons": "^15.0.3",
|
||||||
|
"@react-native-async-storage/async-storage": "2.2.0",
|
||||||
"@react-native-community/datetimepicker": "^9.1.0",
|
"@react-native-community/datetimepicker": "^9.1.0",
|
||||||
"@react-navigation/bottom-tabs": "^7.4.0",
|
"@react-navigation/bottom-tabs": "^7.4.0",
|
||||||
"@react-navigation/elements": "^2.6.3",
|
"@react-navigation/elements": "^2.6.3",
|
||||||
@@ -22,6 +23,7 @@
|
|||||||
"expo-haptics": "~15.0.8",
|
"expo-haptics": "~15.0.8",
|
||||||
"expo-image": "~3.0.11",
|
"expo-image": "~3.0.11",
|
||||||
"expo-linking": "~8.0.11",
|
"expo-linking": "~8.0.11",
|
||||||
|
"expo-notifications": "~0.32.16",
|
||||||
"expo-router": "~6.0.23",
|
"expo-router": "~6.0.23",
|
||||||
"expo-splash-screen": "~31.0.13",
|
"expo-splash-screen": "~31.0.13",
|
||||||
"expo-status-bar": "~3.0.9",
|
"expo-status-bar": "~3.0.9",
|
||||||
|
|||||||
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