135 lines
2.9 KiB
TypeScript
135 lines
2.9 KiB
TypeScript
import {
|
|
createContext,
|
|
ReactNode,
|
|
useContext,
|
|
useEffect,
|
|
useMemo,
|
|
useState,
|
|
} from "react";
|
|
import AsyncStorage from "@react-native-async-storage/async-storage";
|
|
|
|
import { syncBirthdayNotifications } from "@/utils/birthday-notifications";
|
|
export interface BirthdayEntry {
|
|
id: string;
|
|
name: string;
|
|
date: string;
|
|
}
|
|
|
|
interface AddBirthdayInput {
|
|
name: string;
|
|
date: string;
|
|
}
|
|
|
|
interface BirthdaysContextValue {
|
|
birthdays: BirthdayEntry[];
|
|
addBirthday: (input: AddBirthdayInput) => void;
|
|
deleteBirthday: (id: string) => void;
|
|
}
|
|
|
|
const STORAGE_KEY = "birthdays";
|
|
|
|
const BirthdaysContext = createContext<BirthdaysContextValue | undefined>(
|
|
undefined
|
|
);
|
|
|
|
export function BirthdaysProvider({ children }: { children: ReactNode }) {
|
|
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(
|
|
() => ({
|
|
birthdays,
|
|
addBirthday: ({ name, date }: AddBirthdayInput) => {
|
|
const trimmedName = name.trim();
|
|
|
|
if (!trimmedName) {
|
|
return;
|
|
}
|
|
|
|
setBirthdays((currentBirthdays) => [
|
|
...currentBirthdays,
|
|
{
|
|
id: `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
name: trimmedName,
|
|
date,
|
|
},
|
|
]);
|
|
},
|
|
deleteBirthday: (id: string) => {
|
|
setBirthdays((currentBirthdays) =>
|
|
currentBirthdays.filter((birthday) => birthday.id !== id)
|
|
);
|
|
},
|
|
}),
|
|
[birthdays]
|
|
);
|
|
|
|
return (
|
|
<BirthdaysContext.Provider value={value}>
|
|
{children}
|
|
</BirthdaysContext.Provider>
|
|
);
|
|
}
|
|
|
|
export function useBirthdays() {
|
|
const context = useContext(BirthdaysContext);
|
|
|
|
if (!context) {
|
|
throw new Error("useBirthdays must be used within a BirthdaysProvider");
|
|
}
|
|
|
|
return context;
|
|
}
|