Files
cinny/src/app/pages/client/CallProvider.tsx
T

127 lines
4.4 KiB
TypeScript
Raw Normal View History

2025-04-15 22:14:43 -05:00
import React, { createContext, useState, useContext, useMemo, useCallback, ReactNode } from 'react';
import { logger } from 'matrix-js-sdk/lib/logger';
2025-04-16 19:49:11 -05:00
import { WidgetApiToWidgetAction, ITransport, WidgetApiAction } from 'matrix-widget-api';
2025-04-15 22:14:43 -05:00
interface CallContextState {
activeCallRoomId: string | null;
setActiveCallRoomId: (roomId: string | null) => void;
hangUp: () => void;
2025-04-16 19:49:11 -05:00
activeApiTransport: ITransport | null;
registerActiveTransport: (roomId: string | null, transport: ITransport | null) => void;
2025-04-15 22:14:43 -05:00
sendWidgetAction: <T = unknown>(
2025-04-16 19:49:11 -05:00
action: WidgetApiToWidgetAction | string,
2025-04-15 22:14:43 -05:00
data: T
2025-04-16 19:49:11 -05:00
) => Promise<void>;
2025-04-15 22:14:43 -05:00
}
const CallContext = createContext<CallContextState | undefined>(undefined);
interface CallProviderProps {
children: ReactNode;
}
export function CallProvider({ children }: CallProviderProps) {
const [activeCallRoomId, setActiveCallRoomIdState] = useState<string | null>(null);
const [activeApiTransport, setActiveApiTransport] = useState<ITransport | null>(null);
const [transportRoomId, setTransportRoomId] = useState<string | null>(null);
const setActiveCallRoomId = useCallback(
(roomId: string | null) => {
logger.debug(`CallContext: Setting activeCallRoomId to ${roomId}`);
setActiveCallRoomIdState(roomId);
if (roomId === null || roomId !== transportRoomId) {
logger.debug(
`CallContext: Clearing active transport because active room changed or was cleared.`
);
setActiveApiTransport(null);
setTransportRoomId(null);
}
},
[transportRoomId]
2025-04-16 19:49:11 -05:00
);
2025-04-15 22:14:43 -05:00
const hangUp = useCallback(() => {
logger.debug(`CallContext: Hang up called.`);
setActiveCallRoomIdState(null);
logger.debug(`CallContext: Clearing active transport due to hangup.`);
setActiveApiTransport(null);
setTransportRoomId(null);
}, []);
const registerActiveTransport = useCallback(
(roomId: string | null, transport: ITransport | null) => {
if (roomId && transport) {
logger.debug(`CallContext: Registering active transport for room ${roomId}.`);
setActiveApiTransport(transport);
setTransportRoomId(roomId);
2025-04-16 19:49:11 -05:00
} else if (roomId === transportRoomId || roomId === null) {
logger.debug(`CallContext: Clearing active transport for room ${transportRoomId}.`);
setActiveApiTransport(null);
setTransportRoomId(null);
2025-04-15 22:14:43 -05:00
} else {
2025-04-16 19:49:11 -05:00
logger.debug(
`CallContext: Ignoring transport clear request for room ${roomId}, as current transport belongs to ${transportRoomId}.`
);
2025-04-15 22:14:43 -05:00
}
},
2025-04-16 19:49:11 -05:00
[transportRoomId]
2025-04-15 22:14:43 -05:00
);
const sendWidgetAction = useCallback(
2025-04-16 19:49:11 -05:00
async <T = unknown,>(action: WidgetApiToWidgetAction | string, data: T): Promise<void> => {
2025-04-15 22:14:43 -05:00
if (!activeApiTransport) {
logger.warn(
`CallContext: Cannot send action '${action}', no active API transport registered.`
);
return Promise.reject(new Error('No active call transport'));
}
if (!transportRoomId || transportRoomId !== activeCallRoomId) {
logger.warn(
`CallContext: Cannot send action '${action}', transport room (${transportRoomId}) does not match active call room (${activeCallRoomId}). Stale transport?`
);
return Promise.reject(new Error('Mismatched active call transport'));
}
try {
logger.debug(
`CallContext: Sending action '${action}' via active transport (room: ${transportRoomId}) with data:`,
data
);
2025-04-16 19:49:11 -05:00
await activeApiTransport.send<T>(action as WidgetApiAction, data);
2025-04-15 22:14:43 -05:00
} catch (error) {
logger.error(`CallContext: Error sending action '${action}':`, error);
return Promise.reject(error);
}
},
2025-04-16 19:49:11 -05:00
[activeApiTransport, activeCallRoomId, transportRoomId]
2025-04-15 22:14:43 -05:00
);
const contextValue = useMemo<CallContextState>(
() => ({
activeCallRoomId,
setActiveCallRoomId,
hangUp,
2025-04-16 19:49:11 -05:00
activeApiTransport,
2025-04-15 22:14:43 -05:00
registerActiveTransport,
sendWidgetAction,
}),
[
activeCallRoomId,
setActiveCallRoomId,
hangUp,
activeApiTransport,
registerActiveTransport,
sendWidgetAction,
]
);
return <CallContext.Provider value={contextValue}>{children}</CallContext.Provider>;
}
export function useCallState(): CallContextState {
const context = useContext(CallContext);
if (context === undefined) {
throw new Error('useCallState must be used within a CallProvider');
}
return context;
}