/** * Broadcasts module — React context + hook for in-app messages in React Native apps. */ import React, { createContext, useContext, useState, useCallback, useEffect } from 'react'; import type { PlatformSDK } from '../core.js'; export interface InAppMessage { id: string; title: string; body: string; type: 'info' | 'warning' | 'critical'; action?: { label: string; url: string }; dismissible: boolean; expiresAt?: string; } interface BroadcastContextType { messages: InAppMessage[]; dismiss: (id: string) => void; refresh: () => Promise; } const BroadcastContext = createContext(null); export function useBroadcasts(): BroadcastContextType { const ctx = useContext(BroadcastContext); if (!ctx) throw new Error('useBroadcasts must be used within a BroadcastProvider'); return ctx; } interface BroadcastProviderProps { sdk: PlatformSDK; /** Poll interval in ms (default: 300000 = 5 min) */ pollInterval?: number; children: React.ReactNode; } export function BroadcastProvider({ sdk, pollInterval = 300_000, children, }: BroadcastProviderProps): React.JSX.Element { const [messages, setMessages] = useState([]); const refresh = useCallback(async () => { try { const res = await sdk.fetch('/api/broadcasts/active'); if (res.ok) { const data = (await res.json()) as InAppMessage[]; setMessages(data); } } catch { /* silent */ } }, [sdk]); const dismiss = useCallback( (id: string) => { setMessages(prev => prev.filter(m => m.id !== id)); // Fire-and-forget dismiss on server sdk.fetch(`/api/broadcasts/${id}/dismiss`, { method: 'POST' }).catch(() => {}); }, [sdk] ); useEffect(() => { refresh(); const id = setInterval(refresh, pollInterval); return () => clearInterval(id); }, [refresh, pollInterval]); const value: BroadcastContextType = { messages, dismiss, refresh }; return React.createElement(BroadcastContext.Provider, { value }, children); } // MARK: - UI Components interface InAppMessageBannerProps { message: InAppMessage; onDismiss: () => void; } /** * Placeholder banner component — product apps should implement their own * styled version using this as a reference. Returns null (render-only hook). */ export function InAppMessageBanner(_props: InAppMessageBannerProps): React.JSX.Element | null { // Product apps implement their own styled component return null; } interface BroadcastModalProps { message: InAppMessage | null; onDismiss: () => void; } /** * Placeholder modal component — product apps should implement their own * styled version. Returns null. */ export function BroadcastModal(_props: BroadcastModalProps): React.JSX.Element | null { return null; }