learning_ai_common_plat/packages/react-native-platform-sdk/src/broadcasts/index.ts
saravanakumardb1 4ca9b73d75 fix(platform): build react-native-platform-sdk + fix admin-web typecheck errors
- react-native-platform-sdk: add tsconfig.json + 13 source files (core, auth, telemetry, feature-flags, kill-switch, broadcasts, surveys)
- react-native-platform-sdk: React hooks + providers wrapping platform-service APIs via fetch
- admin-web: fix ThemeEditor.tsx + themes/active/route.ts lysnrai token type access
- tracker-web: product-context import path fix (pre-existing)
2026-03-12 16:36:13 -07:00

105 lines
2.8 KiB
TypeScript

/**
* 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<void>;
}
const BroadcastContext = createContext<BroadcastContextType | null>(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<InAppMessage[]>([]);
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;
}