142 lines
3.6 KiB
TypeScript
142 lines
3.6 KiB
TypeScript
"use client";
|
|
|
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
|
|
import { HABIT_PET_DATA_UPDATED_EVENT } from "@/lib/app-events";
|
|
import { getDailyReminderStatus } from "@/lib/daily-reminder-status";
|
|
import {
|
|
deliverDailyReminder,
|
|
getNotificationPermissionState,
|
|
getReminderCheckIntervalMs,
|
|
hasReachedReminderTime,
|
|
markDailyReminderSentToday,
|
|
wasDailyReminderSentToday,
|
|
} from "@/lib/daily-reminders";
|
|
import {
|
|
getProfilePreferences,
|
|
type ProfilePreferences,
|
|
} from "@/lib/profile-preferences-storage";
|
|
import { usesInAppReminders, usesSystemReminders } from "@/lib/reminder-delivery";
|
|
|
|
export function DailyReminderProvider() {
|
|
const [preferences, setPreferences] = useState<ProfilePreferences | null>(
|
|
null,
|
|
);
|
|
const isCheckingRef = useRef(false);
|
|
|
|
const loadPreferences = useCallback(async () => {
|
|
try {
|
|
const nextPreferences = await getProfilePreferences();
|
|
setPreferences(nextPreferences);
|
|
return nextPreferences;
|
|
} catch {
|
|
setPreferences(null);
|
|
return null;
|
|
}
|
|
}, []);
|
|
|
|
const runReminderCheck = useCallback(
|
|
async (activePreferences = preferences) => {
|
|
if (isCheckingRef.current) {
|
|
return;
|
|
}
|
|
|
|
isCheckingRef.current = true;
|
|
|
|
try {
|
|
if (!activePreferences?.dailyReminderEnabled) {
|
|
return;
|
|
}
|
|
|
|
if (
|
|
usesSystemReminders(activePreferences.dailyReminderDelivery) &&
|
|
!usesInAppReminders(activePreferences.dailyReminderDelivery) &&
|
|
getNotificationPermissionState() !== "granted"
|
|
) {
|
|
return;
|
|
}
|
|
|
|
const status = await getDailyReminderStatus();
|
|
|
|
if (wasDailyReminderSentToday(status.dateKey)) {
|
|
return;
|
|
}
|
|
|
|
if (!hasReachedReminderTime(activePreferences.dailyReminderTime)) {
|
|
return;
|
|
}
|
|
|
|
if (status.needsReminder) {
|
|
deliverDailyReminder(
|
|
status,
|
|
activePreferences.dailyReminderDelivery,
|
|
);
|
|
}
|
|
|
|
markDailyReminderSentToday(status.dateKey);
|
|
} finally {
|
|
isCheckingRef.current = false;
|
|
}
|
|
},
|
|
[preferences],
|
|
);
|
|
|
|
useEffect(() => {
|
|
let cancelled = false;
|
|
|
|
async function initialize() {
|
|
const loadedPreferences = await loadPreferences();
|
|
|
|
if (!cancelled && loadedPreferences) {
|
|
await runReminderCheck(loadedPreferences);
|
|
}
|
|
}
|
|
|
|
void initialize();
|
|
|
|
const handleDataUpdated = () => {
|
|
void (async () => {
|
|
const loadedPreferences = await loadPreferences();
|
|
|
|
if (loadedPreferences) {
|
|
await runReminderCheck(loadedPreferences);
|
|
}
|
|
})();
|
|
};
|
|
|
|
window.addEventListener(HABIT_PET_DATA_UPDATED_EVENT, handleDataUpdated);
|
|
|
|
return () => {
|
|
cancelled = true;
|
|
window.removeEventListener(HABIT_PET_DATA_UPDATED_EVENT, handleDataUpdated);
|
|
};
|
|
}, [loadPreferences, runReminderCheck]);
|
|
|
|
useEffect(() => {
|
|
if (!preferences) {
|
|
return;
|
|
}
|
|
|
|
const intervalId = window.setInterval(() => {
|
|
void runReminderCheck(preferences);
|
|
}, getReminderCheckIntervalMs());
|
|
|
|
const handleVisibilityOrFocus = () => {
|
|
if (document.visibilityState === "visible") {
|
|
void runReminderCheck(preferences);
|
|
}
|
|
};
|
|
|
|
window.addEventListener("focus", handleVisibilityOrFocus);
|
|
document.addEventListener("visibilitychange", handleVisibilityOrFocus);
|
|
|
|
return () => {
|
|
window.clearInterval(intervalId);
|
|
window.removeEventListener("focus", handleVisibilityOrFocus);
|
|
document.removeEventListener("visibilitychange", handleVisibilityOrFocus);
|
|
};
|
|
}, [preferences, runReminderCheck]);
|
|
|
|
return null;
|
|
}
|