import { useEffect, useState } from "react"; import { subscribeSessionQuestions, type RtdbQuestionItem, } from "../lib/sessionQuestionsRtdb"; import { getVisitorSessionQuestions, type VisitorSessionQuestion } from "../lib/api"; import { isFirebaseClientConfigured } from "../lib/firebaseClient"; const API_POLL_MS = 4000; function isFirebasePermissionError(message: string): boolean { return /permission_denied|PERMISSION_DENIED|permission denied/i.test(message); } function apiQuestionsToRtdbItems(rows: VisitorSessionQuestion[]): RtdbQuestionItem[] { return rows .map((q) => ({ _id: String(q._id), author: String(q.author ?? "Anonymous"), text: String(q.text ?? ""), up: typeof q.up === "number" ? q.up : Number(q.up) || 0, down: typeof q.down === "number" ? q.down : Number(q.down) || 0, createdAt: q.createdAt != null ? String(q.createdAt) : undefined, status: "pending", audienceListed: true, })) .sort((a, b) => { if (b.up !== a.up) return b.up - a.up; const ta = a.createdAt ? Date.parse(a.createdAt) : 0; const tb = b.createdAt ? Date.parse(b.createdAt) : 0; return tb - ta; }); } /** * Live session questions for attendees. * - Tries Firebase RTDB when configured. * - On `permission_denied` (rules require auth), falls back to GET /api/visitor/sessions/:id/questions with polling. */ export function useSessionQuestionsRtdb( sessionId: string | null | undefined, eventId?: string | null, ) { const [items, setItems] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const sid = sessionId?.trim() ?? ""; const eid = eventId?.trim() ?? ""; useEffect(() => { if (!sid || !eid) { setItems([]); setLoading(false); setError( sid && !eid ? "event_id is required to load questions." : null, ); return; } let cancelled = false; let pollTimer: ReturnType | null = null; let unsubRtdb: (() => void) | null = null; let usingApi = false; const loadViaApi = async () => { try { const rows = await getVisitorSessionQuestions(sid, eid); if (cancelled) return; setItems(apiQuestionsToRtdbItems(rows)); setLoading(false); setError(null); } catch (e) { if (cancelled) return; setError(e instanceof Error ? e.message : "Could not load questions."); setItems([]); setLoading(false); } }; const startApiPolling = () => { if (usingApi) return; usingApi = true; if (unsubRtdb) { unsubRtdb(); unsubRtdb = null; } void loadViaApi(); pollTimer = setInterval(() => { if (cancelled || document.visibilityState !== "visible") return; void loadViaApi(); }, API_POLL_MS); }; setLoading(true); setError(null); if (!isFirebaseClientConfigured()) { startApiPolling(); } else { unsubRtdb = subscribeSessionQuestions( sid, (list) => { if (cancelled || usingApi) return; setItems(list); setLoading(false); setError(null); }, (err) => { if (cancelled) return; const msg = err.message || String(err); if (isFirebasePermissionError(msg)) { startApiPolling(); return; } setError(msg); setItems([]); setLoading(false); }, ); } return () => { cancelled = true; if (unsubRtdb) unsubRtdb(); if (pollTimer) clearInterval(pollTimer); }; }, [sid, eid]); return { items, loading, error }; }