// This program has been developed by students from the bachelor Computer Science at Utrecht // University within the Software Project course. // © Copyright Utrecht University (Department of Information and Computing Sciences) import React, { useEffect } from 'react'; const API_BASE = "http://localhost:8000"; const API_BASE_BP = API_BASE + "/button_pressed"; //UserInterruptAgent endpoint /** * HELPER: Unified sender function */ export const sendAPICall = async (type: string, context: string, endpoint?: string) => { try { const response = await fetch(`${API_BASE_BP}${endpoint ?? ""}`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ type, context }), }); if (!response.ok) throw new Error("Backend response error"); console.log(`API Call send - Type: ${type}, Context: ${context} ${endpoint ? `, Endpoint: ${endpoint}` : ""}`); } catch (err) { console.error(`Failed to send api call:`, err); } }; /** * Sends an API call to the CB for going to the next phase. * In case we can't go to the next phase, the function will throw an error. */ export async function nextPhase(): Promise { const type = "next_phase" const context = "" sendAPICall(type, context) } /** * Sends an API call to the CB for going to pause experiment */ export async function pauseExperiment(): Promise { const type = "pause" const context = "true" sendAPICall(type, context) } /** * Sends an API call to the CB for going to resume experiment */ export async function playExperiment(): Promise { const type = "pause" const context = "false" sendAPICall(type, context) } /** * Types for the experiment stream messages */ export type PhaseUpdate = { type: 'phase_update'; id: string }; export type GoalUpdate = { type: 'goal_update'; id: string }; export type TriggerUpdate = { type: 'trigger_update'; id: string; achieved: boolean }; export type CondNormsStateUpdate = { type: 'cond_norms_state_update'; norms: { id: string; active: boolean }[] }; export type ExperimentStreamData = PhaseUpdate | GoalUpdate | TriggerUpdate | CondNormsStateUpdate | Record; /** * A hook that listens to the experiment stream that updates current state of the program * via updates sent from the backend */ export function useExperimentLogger(onUpdate?: (data: ExperimentStreamData) => void) { const callbackRef = React.useRef(onUpdate); // Ref is updated every time with on update React.useEffect(() => { callbackRef.current = onUpdate; }, [onUpdate]); useEffect(() => { console.log("Connecting to Experiment Stream..."); const eventSource = new EventSource(`${API_BASE}/experiment_stream`); eventSource.onmessage = (event) => { try { const parsedData = JSON.parse(event.data) as ExperimentStreamData; //call function using the ref callbackRef.current?.(parsedData); } catch (err) { console.warn("Stream parse error:", err); } }; eventSource.onerror = (err) => { console.error("SSE Connection Error:", err); eventSource.close(); }; return () => { console.log("Closing Experiment Stream..."); eventSource.close(); }; }, []); } /** * A hook that listens to the status stream that updates active conditional norms * via updates sent from the backend */ export function useStatusLogger(onUpdate?: (data: ExperimentStreamData) => void) { const callbackRef = React.useRef(onUpdate); React.useEffect(() => { callbackRef.current = onUpdate; }, [onUpdate]); useEffect(() => { const eventSource = new EventSource(`${API_BASE}/status_stream`); eventSource.onmessage = (event) => { try { const parsedData = JSON.parse(event.data); callbackRef.current?.(parsedData); } catch (err) { console.warn("Status stream error:", err); } }; return () => eventSource.close(); }, []); }