import { useCallback, useEffect, useRef } from "react"; import { Platform } from "react-native"; import { usePostiz } from "@/context/PostizContext"; import { PostizPost } from "@/context/PostizContext"; const POLL_INTERVAL_MS = 15 * 60 * 1000; const SEEN_KEY = "postiz_seen_statuses"; function isExpoGo(): boolean { try { const Constants = require("expo-constants").default; return Constants?.executionEnvironment === "storeClient"; } catch { return false; } } async function getSeenStatuses(): Promise> { try { const { default: AsyncStorage } = await import( "@react-native-async-storage/async-storage" ); const raw = await AsyncStorage.getItem(SEEN_KEY); return raw ? JSON.parse(raw) : {}; } catch { return {}; } } async function saveSeenStatuses(map: Record) { try { const { default: AsyncStorage } = await import( "@react-native-async-storage/async-storage" ); await AsyncStorage.setItem(SEEN_KEY, JSON.stringify(map)); } catch {} } async function sendStatusNotification(post: PostizPost) { if (Platform.OS === "web" || isExpoGo()) return; try { const Notifications = require("expo-notifications"); const isError = post.state === "ERROR"; await Notifications.scheduleNotificationAsync({ content: { title: isError ? "Post failed to publish" : "Post published!", body: post.content.length > 80 ? post.content.slice(0, 80) + "…" : post.content, data: { postId: post.id }, }, trigger: null, }); } catch {} } export function useNotifications() { const { client, isConfigured } = usePostiz(); const intervalRef = useRef | null>(null); const permissionGranted = useRef(false); const requestPermissions = useCallback(async () => { if (Platform.OS === "web" || isExpoGo()) return false; try { const Notifications = require("expo-notifications"); Notifications.setNotificationHandler({ handleNotification: async () => ({ shouldShowAlert: true, shouldPlaySound: true, shouldSetBadge: true, shouldShowBanner: true, shouldShowList: true, }), }); const { status: existing } = await Notifications.getPermissionsAsync(); if (existing === "granted") { permissionGranted.current = true; return true; } const { status } = await Notifications.requestPermissionsAsync(); permissionGranted.current = status === "granted"; return permissionGranted.current; } catch { return false; } }, []); const checkForStatusChanges = useCallback(async () => { if (!client || !permissionGranted.current) return; try { const now = new Date(); const from = new Date(now); from.setDate(from.getDate() - 7); const res = await client.get("posts", { params: { startDate: from.toISOString(), endDate: now.toISOString(), }, }); const posts: PostizPost[] = Array.isArray(res.data) ? res.data : res.data?.posts ?? []; const seen = await getSeenStatuses(); const updated: Record = { ...seen }; const toNotify: PostizPost[] = []; for (const post of posts) { const prev = seen[post.id]; if (prev === undefined) { updated[post.id] = post.state; continue; } if ( prev !== post.state && (post.state === "PUBLISHED" || post.state === "ERROR") ) { toNotify.push(post); } updated[post.id] = post.state; } await saveSeenStatuses(updated); for (const post of toNotify) { await sendStatusNotification(post); } } catch {} }, [client]); useEffect(() => { if (!isConfigured || Platform.OS === "web" || isExpoGo()) return; let mounted = true; (async () => { const granted = await requestPermissions(); if (!granted || !mounted) return; await checkForStatusChanges(); intervalRef.current = setInterval(() => { checkForStatusChanges(); }, POLL_INTERVAL_MS); })(); return () => { mounted = false; if (intervalRef.current) { clearInterval(intervalRef.current); intervalRef.current = null; } }; }, [isConfigured, requestPermissions, checkForStatusChanges]); return { requestPermissions }; }