"use client"; import React, { createContext, useContext, useEffect, useState } from "react"; type ConsentContextState = | { ready: false } | { ready: true; consentAnsweredForMeetings: Set; }; interface RecordingConsentContextValue { state: ConsentContextState; touch: (meetingId: string) => void; hasConsent: (meetingId: string) => boolean; } const RecordingConsentContext = createContext< RecordingConsentContextValue | undefined >(undefined); export const useRecordingConsent = () => { const context = useContext(RecordingConsentContext); if (!context) { throw new Error( "useRecordingConsent must be used within RecordingConsentProvider", ); } return context; }; interface RecordingConsentProviderProps { children: React.ReactNode; } const LOCAL_STORAGE_KEY = "recording_consent_meetings"; export const RecordingConsentProvider: React.FC< RecordingConsentProviderProps > = ({ children }) => { const [state, setState] = useState({ ready: false }); const safeWriteToStorage = (meetingIds: string[]): void => { try { if (typeof window !== "undefined" && window.localStorage) { localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(meetingIds)); } } catch (error) { console.error("Failed to save consent data to localStorage:", error); } }; // writes to local storage and to the state of context both const touch = (meetingId: string): void => { if (!state.ready) { console.warn("Attempted to touch consent before context is ready"); return; } // has success regardless local storage write success: we don't handle that // and don't want to crash anything with just consent functionality const newSet = state.consentAnsweredForMeetings.has(meetingId) ? state.consentAnsweredForMeetings : new Set([...state.consentAnsweredForMeetings, meetingId]); // note: preserves the set insertion order const array = Array.from(newSet).slice(-5); // Keep latest 5 safeWriteToStorage(array); setState({ ready: true, consentAnsweredForMeetings: newSet }); }; const hasConsent = (meetingId: string): boolean => { if (!state.ready) return false; return state.consentAnsweredForMeetings.has(meetingId); }; // initialize on mount useEffect(() => { try { if (typeof window === "undefined" || !window.localStorage) { setState({ ready: true, consentAnsweredForMeetings: new Set() }); return; } const stored = localStorage.getItem(LOCAL_STORAGE_KEY); if (!stored) { setState({ ready: true, consentAnsweredForMeetings: new Set() }); return; } const parsed = JSON.parse(stored); if (!Array.isArray(parsed)) { console.warn("Invalid consent data format in localStorage, resetting"); setState({ ready: true, consentAnsweredForMeetings: new Set() }); return; } // pre-historic way of parsing! const consentAnsweredForMeetings = new Set( parsed.filter((id) => !!id && typeof id === "string"), ); setState({ ready: true, consentAnsweredForMeetings }); } catch (error) { // we don't want to fail the page here; the component is not essential. console.error("Failed to parse consent data from localStorage:", error); setState({ ready: true, consentAnsweredForMeetings: new Set() }); } }, []); const value: RecordingConsentContextValue = { state, touch, hasConsent, }; return ( {children} ); };