mirror of
https://github.com/soconnor0919/hristudio.git
synced 2026-03-24 03:37:51 -04:00
Pre-conf work 2025
This commit is contained in:
@@ -1,19 +0,0 @@
|
||||
import * as React from "react"
|
||||
|
||||
const MOBILE_BREAKPOINT = 768
|
||||
|
||||
export function useIsMobile() {
|
||||
const [isMobile, setIsMobile] = React.useState<boolean>(false)
|
||||
|
||||
React.useEffect(() => {
|
||||
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)
|
||||
const onChange = () => {
|
||||
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
|
||||
}
|
||||
mql.addEventListener("change", onChange)
|
||||
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
|
||||
return () => mql.removeEventListener("change", onChange)
|
||||
}, [])
|
||||
|
||||
return isMobile
|
||||
}
|
||||
@@ -3,9 +3,97 @@
|
||||
import { useSession } from "next-auth/react";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
|
||||
export interface WebSocketMessage {
|
||||
export type TrialStatus =
|
||||
| "scheduled"
|
||||
| "in_progress"
|
||||
| "completed"
|
||||
| "aborted"
|
||||
| "failed";
|
||||
|
||||
export interface TrialSnapshot {
|
||||
id: string;
|
||||
status: TrialStatus;
|
||||
startedAt?: string | Date | null;
|
||||
completedAt?: string | Date | null;
|
||||
}
|
||||
|
||||
interface ConnectionEstablishedMessage {
|
||||
type: "connection_established";
|
||||
data: {
|
||||
trialId: string;
|
||||
userId: string | null;
|
||||
role: string;
|
||||
connectedAt: number;
|
||||
};
|
||||
}
|
||||
|
||||
interface HeartbeatResponseMessage {
|
||||
type: "heartbeat_response";
|
||||
data: {
|
||||
timestamp: number;
|
||||
};
|
||||
}
|
||||
|
||||
interface TrialStatusMessage {
|
||||
type: "trial_status";
|
||||
data: {
|
||||
trial: TrialSnapshot;
|
||||
current_step_index: number;
|
||||
timestamp: number;
|
||||
};
|
||||
}
|
||||
|
||||
interface TrialActionExecutedMessage {
|
||||
type: "trial_action_executed";
|
||||
data: {
|
||||
action_type: string;
|
||||
timestamp: number;
|
||||
} & Record<string, unknown>;
|
||||
}
|
||||
|
||||
interface InterventionLoggedMessage {
|
||||
type: "intervention_logged";
|
||||
data: {
|
||||
timestamp: number;
|
||||
} & Record<string, unknown>;
|
||||
}
|
||||
|
||||
interface StepChangedMessage {
|
||||
type: "step_changed";
|
||||
data: {
|
||||
from_step?: number;
|
||||
to_step: number;
|
||||
step_name?: string;
|
||||
timestamp: number;
|
||||
} & Record<string, unknown>;
|
||||
}
|
||||
|
||||
interface ErrorMessage {
|
||||
type: "error";
|
||||
data: {
|
||||
message?: string;
|
||||
};
|
||||
}
|
||||
|
||||
type KnownInboundMessage =
|
||||
| ConnectionEstablishedMessage
|
||||
| HeartbeatResponseMessage
|
||||
| TrialStatusMessage
|
||||
| TrialActionExecutedMessage
|
||||
| InterventionLoggedMessage
|
||||
| StepChangedMessage
|
||||
| ErrorMessage;
|
||||
|
||||
export type WebSocketMessage =
|
||||
| KnownInboundMessage
|
||||
| {
|
||||
type: string;
|
||||
data: unknown;
|
||||
};
|
||||
|
||||
export interface OutgoingMessage {
|
||||
type: string;
|
||||
data: any;
|
||||
data: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export interface UseWebSocketOptions {
|
||||
@@ -23,7 +111,7 @@ export interface UseWebSocketReturn {
|
||||
isConnected: boolean;
|
||||
isConnecting: boolean;
|
||||
connectionError: string | null;
|
||||
sendMessage: (message: WebSocketMessage) => void;
|
||||
sendMessage: (message: OutgoingMessage) => void;
|
||||
disconnect: () => void;
|
||||
reconnect: () => void;
|
||||
lastMessage: WebSocketMessage | null;
|
||||
@@ -40,25 +128,30 @@ export function useWebSocket({
|
||||
heartbeatInterval = 30000,
|
||||
}: UseWebSocketOptions): UseWebSocketReturn {
|
||||
const { data: session } = useSession();
|
||||
const [isConnected, setIsConnected] = useState(false);
|
||||
const [isConnecting, setIsConnecting] = useState(false);
|
||||
const [isConnected, setIsConnected] = useState<boolean>(false);
|
||||
const [isConnecting, setIsConnecting] = useState<boolean>(false);
|
||||
const [connectionError, setConnectionError] = useState<string | null>(null);
|
||||
const [hasAttemptedConnection, setHasAttemptedConnection] =
|
||||
useState<boolean>(false);
|
||||
const [lastMessage, setLastMessage] = useState<WebSocketMessage | null>(null);
|
||||
|
||||
const wsRef = useRef<WebSocket | null>(null);
|
||||
const reconnectTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||
const heartbeatTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||
const attemptCountRef = useRef(0);
|
||||
const mountedRef = useRef(true);
|
||||
const attemptCountRef = useRef<number>(0);
|
||||
const mountedRef = useRef<boolean>(true);
|
||||
const connectionStableTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||
|
||||
// Generate auth token (simplified - in production use proper JWT)
|
||||
const getAuthToken = useCallback(() => {
|
||||
const getAuthToken = useCallback((): string | null => {
|
||||
if (!session?.user) return null;
|
||||
// In production, this would be a proper JWT token
|
||||
return btoa(JSON.stringify({ userId: session.user.id, timestamp: Date.now() }));
|
||||
return btoa(
|
||||
JSON.stringify({ userId: session.user.id, timestamp: Date.now() }),
|
||||
);
|
||||
}, [session]);
|
||||
|
||||
const sendMessage = useCallback((message: WebSocketMessage) => {
|
||||
const sendMessage = useCallback((message: OutgoingMessage): void => {
|
||||
if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN) {
|
||||
wsRef.current.send(JSON.stringify(message));
|
||||
} else {
|
||||
@@ -66,11 +159,11 @@ export function useWebSocket({
|
||||
}
|
||||
}, []);
|
||||
|
||||
const sendHeartbeat = useCallback(() => {
|
||||
const sendHeartbeat = useCallback((): void => {
|
||||
sendMessage({ type: "heartbeat", data: {} });
|
||||
}, [sendMessage]);
|
||||
|
||||
const scheduleHeartbeat = useCallback(() => {
|
||||
const scheduleHeartbeat = useCallback((): void => {
|
||||
if (heartbeatTimeoutRef.current) {
|
||||
clearTimeout(heartbeatTimeoutRef.current);
|
||||
}
|
||||
@@ -82,99 +175,167 @@ export function useWebSocket({
|
||||
}, heartbeatInterval);
|
||||
}, [isConnected, sendHeartbeat, heartbeatInterval]);
|
||||
|
||||
const handleMessage = useCallback((event: MessageEvent) => {
|
||||
try {
|
||||
const message: WebSocketMessage = JSON.parse(event.data);
|
||||
setLastMessage(message);
|
||||
const handleMessage = useCallback(
|
||||
(event: MessageEvent<string>): void => {
|
||||
try {
|
||||
const message = JSON.parse(event.data) as WebSocketMessage;
|
||||
setLastMessage(message);
|
||||
|
||||
// Handle system messages
|
||||
switch (message.type) {
|
||||
case "connection_established":
|
||||
console.log("WebSocket connection established:", message.data);
|
||||
setIsConnected(true);
|
||||
setIsConnecting(false);
|
||||
setConnectionError(null);
|
||||
attemptCountRef.current = 0;
|
||||
scheduleHeartbeat();
|
||||
onConnect?.();
|
||||
break;
|
||||
// Handle system messages
|
||||
switch (message.type) {
|
||||
case "connection_established": {
|
||||
console.log(
|
||||
"WebSocket connection established:",
|
||||
(message as ConnectionEstablishedMessage).data,
|
||||
);
|
||||
setIsConnected(true);
|
||||
setIsConnecting(false);
|
||||
setConnectionError(null);
|
||||
attemptCountRef.current = 0;
|
||||
scheduleHeartbeat();
|
||||
onConnect?.();
|
||||
break;
|
||||
}
|
||||
|
||||
case "heartbeat_response":
|
||||
// Heartbeat acknowledged, connection is alive
|
||||
break;
|
||||
case "heartbeat_response":
|
||||
// Heartbeat acknowledged, connection is alive
|
||||
break;
|
||||
|
||||
case "error":
|
||||
console.error("WebSocket server error:", message.data);
|
||||
setConnectionError(message.data.message || "Server error");
|
||||
onError?.(new Event("server_error"));
|
||||
break;
|
||||
case "error": {
|
||||
console.error("WebSocket server error:", message);
|
||||
const msg =
|
||||
(message as ErrorMessage).data?.message ?? "Server error";
|
||||
setConnectionError(msg);
|
||||
onError?.(new Event("server_error"));
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
// Pass to user-defined message handler
|
||||
onMessage?.(message);
|
||||
break;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error parsing WebSocket message:", error);
|
||||
setConnectionError("Failed to parse message");
|
||||
}
|
||||
}, [onMessage, onConnect, onError, scheduleHeartbeat]);
|
||||
|
||||
const handleClose = useCallback((event: CloseEvent) => {
|
||||
console.log("WebSocket connection closed:", event.code, event.reason);
|
||||
setIsConnected(false);
|
||||
setIsConnecting(false);
|
||||
|
||||
if (heartbeatTimeoutRef.current) {
|
||||
clearTimeout(heartbeatTimeoutRef.current);
|
||||
}
|
||||
|
||||
onDisconnect?.();
|
||||
|
||||
// Attempt reconnection if not manually closed and component is still mounted
|
||||
if (event.code !== 1000 && mountedRef.current && attemptCountRef.current < reconnectAttempts) {
|
||||
attemptCountRef.current++;
|
||||
const delay = reconnectInterval * Math.pow(1.5, attemptCountRef.current - 1); // Exponential backoff
|
||||
|
||||
console.log(`Attempting reconnection ${attemptCountRef.current}/${reconnectAttempts} in ${delay}ms`);
|
||||
setConnectionError(`Connection lost. Reconnecting... (${attemptCountRef.current}/${reconnectAttempts})`);
|
||||
|
||||
reconnectTimeoutRef.current = setTimeout(() => {
|
||||
if (mountedRef.current) {
|
||||
connect();
|
||||
default:
|
||||
// Pass to user-defined message handler
|
||||
onMessage?.(message);
|
||||
break;
|
||||
}
|
||||
}, delay);
|
||||
} else if (attemptCountRef.current >= reconnectAttempts) {
|
||||
setConnectionError("Failed to reconnect after maximum attempts");
|
||||
}
|
||||
}, [onDisconnect, reconnectAttempts, reconnectInterval]);
|
||||
} catch (error) {
|
||||
console.error("Error parsing WebSocket message:", error);
|
||||
setConnectionError("Failed to parse message");
|
||||
}
|
||||
},
|
||||
[onMessage, onConnect, onError, scheduleHeartbeat],
|
||||
);
|
||||
|
||||
const handleError = useCallback((event: Event) => {
|
||||
console.error("WebSocket error:", event);
|
||||
setConnectionError("Connection error");
|
||||
setIsConnecting(false);
|
||||
onError?.(event);
|
||||
}, [onError]);
|
||||
const handleClose = useCallback(
|
||||
(event: CloseEvent): void => {
|
||||
console.log("WebSocket connection closed:", event.code, event.reason);
|
||||
setIsConnected(false);
|
||||
setIsConnecting(false);
|
||||
|
||||
const connect = useCallback(() => {
|
||||
if (heartbeatTimeoutRef.current) {
|
||||
clearTimeout(heartbeatTimeoutRef.current);
|
||||
}
|
||||
|
||||
onDisconnect?.();
|
||||
|
||||
// Attempt reconnection if not manually closed and component is still mounted
|
||||
// In development, don't aggressively reconnect to prevent UI flashing
|
||||
if (
|
||||
event.code !== 1000 &&
|
||||
mountedRef.current &&
|
||||
attemptCountRef.current < reconnectAttempts &&
|
||||
process.env.NODE_ENV !== "development"
|
||||
) {
|
||||
attemptCountRef.current++;
|
||||
const delay =
|
||||
reconnectInterval * Math.pow(1.5, attemptCountRef.current - 1); // Exponential backoff
|
||||
|
||||
console.log(
|
||||
`Attempting reconnection ${attemptCountRef.current}/${reconnectAttempts} in ${delay}ms`,
|
||||
);
|
||||
setConnectionError(
|
||||
`Connection lost. Reconnecting... (${attemptCountRef.current}/${reconnectAttempts})`,
|
||||
);
|
||||
|
||||
reconnectTimeoutRef.current = setTimeout(() => {
|
||||
if (mountedRef.current) {
|
||||
attemptCountRef.current = 0;
|
||||
setIsConnecting(true);
|
||||
setConnectionError(null);
|
||||
}
|
||||
}, delay);
|
||||
} else if (attemptCountRef.current >= reconnectAttempts) {
|
||||
setConnectionError("Failed to reconnect after maximum attempts");
|
||||
} else if (
|
||||
process.env.NODE_ENV === "development" &&
|
||||
event.code !== 1000
|
||||
) {
|
||||
// In development, set a stable error message without reconnection attempts
|
||||
setConnectionError("WebSocket unavailable - using polling mode");
|
||||
}
|
||||
},
|
||||
[onDisconnect, reconnectAttempts, reconnectInterval],
|
||||
);
|
||||
|
||||
const handleError = useCallback(
|
||||
(event: Event): void => {
|
||||
// In development, WebSocket failures are expected with Edge Runtime
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
// Only set error state after the first failed attempt to prevent flashing
|
||||
if (!hasAttemptedConnection) {
|
||||
setHasAttemptedConnection(true);
|
||||
// Debounce the error state to prevent UI flashing
|
||||
if (connectionStableTimeoutRef.current) {
|
||||
clearTimeout(connectionStableTimeoutRef.current);
|
||||
}
|
||||
connectionStableTimeoutRef.current = setTimeout(() => {
|
||||
setConnectionError("WebSocket unavailable - using polling mode");
|
||||
setIsConnecting(false);
|
||||
}, 1000);
|
||||
}
|
||||
} else {
|
||||
console.error("WebSocket error:", event);
|
||||
setConnectionError("Connection error");
|
||||
setIsConnecting(false);
|
||||
}
|
||||
onError?.(event);
|
||||
},
|
||||
[onError, hasAttemptedConnection],
|
||||
);
|
||||
|
||||
const connectInternal = useCallback((): void => {
|
||||
if (!session?.user || !trialId) {
|
||||
setConnectionError("Missing authentication or trial ID");
|
||||
if (!hasAttemptedConnection) {
|
||||
setConnectionError("Missing authentication or trial ID");
|
||||
setHasAttemptedConnection(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (wsRef.current &&
|
||||
(wsRef.current.readyState === WebSocket.CONNECTING ||
|
||||
wsRef.current.readyState === WebSocket.OPEN)) {
|
||||
if (
|
||||
wsRef.current &&
|
||||
(wsRef.current.readyState === WebSocket.CONNECTING ||
|
||||
wsRef.current.readyState === WebSocket.OPEN)
|
||||
) {
|
||||
return; // Already connecting or connected
|
||||
}
|
||||
|
||||
const token = getAuthToken();
|
||||
if (!token) {
|
||||
setConnectionError("Failed to generate auth token");
|
||||
if (!hasAttemptedConnection) {
|
||||
setConnectionError("Failed to generate auth token");
|
||||
setHasAttemptedConnection(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
setIsConnecting(true);
|
||||
// Only show connecting state for the first attempt or if we've been stable
|
||||
if (!hasAttemptedConnection || isConnected) {
|
||||
setIsConnecting(true);
|
||||
}
|
||||
|
||||
// Clear any pending error updates
|
||||
if (connectionStableTimeoutRef.current) {
|
||||
clearTimeout(connectionStableTimeoutRef.current);
|
||||
}
|
||||
|
||||
setConnectionError(null);
|
||||
|
||||
try {
|
||||
@@ -191,15 +352,26 @@ export function useWebSocket({
|
||||
console.log("WebSocket connection opened");
|
||||
// Connection establishment is handled in handleMessage
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
console.error("Failed to create WebSocket connection:", error);
|
||||
setConnectionError("Failed to create connection");
|
||||
if (!hasAttemptedConnection) {
|
||||
setConnectionError("Failed to create connection");
|
||||
setHasAttemptedConnection(true);
|
||||
}
|
||||
setIsConnecting(false);
|
||||
}
|
||||
}, [session, trialId, getAuthToken, handleMessage, handleClose, handleError]);
|
||||
}, [
|
||||
session,
|
||||
trialId,
|
||||
getAuthToken,
|
||||
handleMessage,
|
||||
handleClose,
|
||||
handleError,
|
||||
hasAttemptedConnection,
|
||||
isConnected,
|
||||
]);
|
||||
|
||||
const disconnect = useCallback(() => {
|
||||
const disconnect = useCallback((): void => {
|
||||
mountedRef.current = false;
|
||||
|
||||
if (reconnectTimeoutRef.current) {
|
||||
@@ -210,6 +382,10 @@ export function useWebSocket({
|
||||
clearTimeout(heartbeatTimeoutRef.current);
|
||||
}
|
||||
|
||||
if (connectionStableTimeoutRef.current) {
|
||||
clearTimeout(connectionStableTimeoutRef.current);
|
||||
}
|
||||
|
||||
if (wsRef.current) {
|
||||
wsRef.current.close(1000, "Manual disconnect");
|
||||
wsRef.current = null;
|
||||
@@ -218,32 +394,53 @@ export function useWebSocket({
|
||||
setIsConnected(false);
|
||||
setIsConnecting(false);
|
||||
setConnectionError(null);
|
||||
setHasAttemptedConnection(false);
|
||||
attemptCountRef.current = 0;
|
||||
}, []);
|
||||
|
||||
const reconnect = useCallback(() => {
|
||||
const reconnect = useCallback((): void => {
|
||||
disconnect();
|
||||
mountedRef.current = true;
|
||||
attemptCountRef.current = 0;
|
||||
setTimeout(connect, 100); // Small delay to ensure cleanup
|
||||
}, [disconnect, connect]);
|
||||
setHasAttemptedConnection(false);
|
||||
setTimeout(() => {
|
||||
if (mountedRef.current) {
|
||||
void connectInternal();
|
||||
}
|
||||
}, 100); // Small delay to ensure cleanup
|
||||
}, [disconnect, connectInternal]);
|
||||
|
||||
// Effect to establish initial connection
|
||||
useEffect(() => {
|
||||
if (session?.user && trialId) {
|
||||
connect();
|
||||
if (session?.user?.id && trialId) {
|
||||
// In development, only attempt connection once to prevent flashing
|
||||
if (process.env.NODE_ENV === "development" && hasAttemptedConnection) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Trigger reconnection if timeout was set
|
||||
if (reconnectTimeoutRef.current) {
|
||||
clearTimeout(reconnectTimeoutRef.current);
|
||||
reconnectTimeoutRef.current = null;
|
||||
void connectInternal();
|
||||
} else {
|
||||
void connectInternal();
|
||||
}
|
||||
}
|
||||
|
||||
return () => {
|
||||
mountedRef.current = false;
|
||||
disconnect();
|
||||
};
|
||||
}, [session?.user?.id, trialId]); // Reconnect if user or trial changes
|
||||
}, [session?.user?.id, trialId, hasAttemptedConnection]);
|
||||
|
||||
// Cleanup on unmount
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
mountedRef.current = false;
|
||||
if (connectionStableTimeoutRef.current) {
|
||||
clearTimeout(connectionStableTimeoutRef.current);
|
||||
}
|
||||
disconnect();
|
||||
};
|
||||
}, [disconnect]);
|
||||
@@ -262,27 +459,30 @@ export function useWebSocket({
|
||||
// Hook for trial-specific WebSocket events
|
||||
export function useTrialWebSocket(trialId: string) {
|
||||
const [trialEvents, setTrialEvents] = useState<WebSocketMessage[]>([]);
|
||||
const [currentTrialStatus, setCurrentTrialStatus] = useState<any>(null);
|
||||
const [wizardActions, setWizardActions] = useState<any[]>([]);
|
||||
const [currentTrialStatus, setCurrentTrialStatus] =
|
||||
useState<TrialSnapshot | null>(null);
|
||||
const [wizardActions, setWizardActions] = useState<WebSocketMessage[]>([]);
|
||||
|
||||
const handleMessage = useCallback((message: WebSocketMessage) => {
|
||||
const handleMessage = useCallback((message: WebSocketMessage): void => {
|
||||
// Add to events log
|
||||
setTrialEvents(prev => [...prev, message].slice(-100)); // Keep last 100 events
|
||||
setTrialEvents((prev) => [...prev, message].slice(-100)); // Keep last 100 events
|
||||
|
||||
switch (message.type) {
|
||||
case "trial_status":
|
||||
setCurrentTrialStatus(message.data.trial);
|
||||
case "trial_status": {
|
||||
const data = (message as TrialStatusMessage).data;
|
||||
setCurrentTrialStatus(data.trial);
|
||||
break;
|
||||
}
|
||||
|
||||
case "trial_action_executed":
|
||||
case "intervention_logged":
|
||||
case "step_changed":
|
||||
setWizardActions(prev => [...prev, message].slice(-50)); // Keep last 50 actions
|
||||
setWizardActions((prev) => [...prev, message].slice(-50)); // Keep last 50 actions
|
||||
break;
|
||||
|
||||
case "step_changed":
|
||||
// Handle step transitions
|
||||
console.log("Step changed:", message.data);
|
||||
// Handle step transitions (optional logging)
|
||||
console.log("Step changed:", (message as StepChangedMessage).data);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -295,42 +495,68 @@ export function useTrialWebSocket(trialId: string) {
|
||||
trialId,
|
||||
onMessage: handleMessage,
|
||||
onConnect: () => {
|
||||
console.log(`Connected to trial ${trialId} WebSocket`);
|
||||
// Request current trial status on connect
|
||||
webSocket.sendMessage({ type: "request_trial_status", data: {} });
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
console.log(`Connected to trial ${trialId} WebSocket`);
|
||||
}
|
||||
},
|
||||
onDisconnect: () => {
|
||||
console.log(`Disconnected from trial ${trialId} WebSocket`);
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
console.log(`Disconnected from trial ${trialId} WebSocket`);
|
||||
}
|
||||
},
|
||||
onError: (error) => {
|
||||
console.error(`Trial ${trialId} WebSocket error:`, error);
|
||||
onError: () => {
|
||||
// Suppress noisy WebSocket errors in development
|
||||
if (process.env.NODE_ENV !== "development") {
|
||||
console.error(`Trial ${trialId} WebSocket connection failed`);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// Request trial status after connection is established
|
||||
useEffect(() => {
|
||||
if (webSocket.isConnected) {
|
||||
webSocket.sendMessage({ type: "request_trial_status", data: {} });
|
||||
}
|
||||
}, [webSocket.isConnected, webSocket]);
|
||||
|
||||
// Trial-specific actions
|
||||
const executeTrialAction = useCallback((actionType: string, actionData: any) => {
|
||||
webSocket.sendMessage({
|
||||
type: "trial_action",
|
||||
data: {
|
||||
actionType,
|
||||
...actionData,
|
||||
},
|
||||
});
|
||||
}, [webSocket]);
|
||||
const executeTrialAction = useCallback(
|
||||
(actionType: string, actionData: Record<string, unknown>): void => {
|
||||
webSocket.sendMessage({
|
||||
type: "trial_action",
|
||||
data: {
|
||||
actionType,
|
||||
...actionData,
|
||||
},
|
||||
});
|
||||
},
|
||||
[webSocket],
|
||||
);
|
||||
|
||||
const logWizardIntervention = useCallback((interventionData: any) => {
|
||||
webSocket.sendMessage({
|
||||
type: "wizard_intervention",
|
||||
data: interventionData,
|
||||
});
|
||||
}, [webSocket]);
|
||||
const logWizardIntervention = useCallback(
|
||||
(interventionData: Record<string, unknown>): void => {
|
||||
webSocket.sendMessage({
|
||||
type: "wizard_intervention",
|
||||
data: interventionData,
|
||||
});
|
||||
},
|
||||
[webSocket],
|
||||
);
|
||||
|
||||
const transitionStep = useCallback((stepData: any) => {
|
||||
webSocket.sendMessage({
|
||||
type: "step_transition",
|
||||
data: stepData,
|
||||
});
|
||||
}, [webSocket]);
|
||||
const transitionStep = useCallback(
|
||||
(stepData: {
|
||||
from_step?: number;
|
||||
to_step: number;
|
||||
step_name?: string;
|
||||
[k: string]: unknown;
|
||||
}): void => {
|
||||
webSocket.sendMessage({
|
||||
type: "step_transition",
|
||||
data: stepData,
|
||||
});
|
||||
},
|
||||
[webSocket],
|
||||
);
|
||||
|
||||
return {
|
||||
...webSocket,
|
||||
|
||||
Reference in New Issue
Block a user