clean up ts/eslint errors

This commit is contained in:
YoJames2019
2026-02-10 10:56:00 -05:00
parent 5b3a0f1334
commit 40957632d5
5 changed files with 89 additions and 86 deletions

View File

@@ -1,5 +1,5 @@
import { Room } from 'matrix-js-sdk'; import { Room } from 'matrix-js-sdk';
import React, { useContext, useMemo, useCallback, useEffect, useRef, MouseEventHandler, useState, ReactNode } from 'react'; import React, { useContext, useCallback, useEffect, useRef, MouseEventHandler, useState, ReactNode } from 'react';
import { Box, Button, config, Spinner, Text } from 'folds'; import { Box, Button, config, Spinner, Text } from 'folds';
import { useCallState } from '../../pages/client/call/CallProvider'; import { useCallState } from '../../pages/client/call/CallProvider';
import { useCallMembers } from '../../hooks/useCallMemberships'; import { useCallMembers } from '../../hooks/useCallMemberships';
@@ -157,7 +157,7 @@ export function CallView({ room }: { room: Room }) {
navigateRoom(room.roomId); navigateRoom(room.roomId);
} }
if (!callIsCurrentAndReady) { if (!callIsCurrentAndReady) {
hangUp(room.roomId); hangUp();
setActiveCallRoomId(room.roomId); setActiveCallRoomId(room.roomId);
} }
}; };

View File

@@ -77,6 +77,7 @@ export interface IApp extends IWidget {
roomId: string; roomId: string;
eventId?: string; eventId?: string;
avatar_url?: string; avatar_url?: string;
sender: string;
'io.element.managed_hybrid'?: boolean; 'io.element.managed_hybrid'?: boolean;
} }
@@ -91,7 +92,7 @@ export class SmallWidget extends EventEmitter {
public url?: string; public url?: string;
public iframe: HTMLElement | null; public iframe: HTMLIFrameElement | null = null;
private type: string; // Type of the widget (e.g., 'm.call') private type: string; // Type of the widget (e.g., 'm.call')
@@ -143,8 +144,11 @@ export class SmallWidget extends EventEmitter {
// Timelines are most recent last // Timelines are most recent last
const events = room.getLiveTimeline()?.getEvents() || []; const events = room.getLiveTimeline()?.getEvents() || [];
const roomEvent = events[events.length - 1]; const roomEvent = events[events.length - 1];
if (!roomEvent) continue; // force later code to think the room is fresh // force later code to think the room is fresh
this.readUpToMap[room.roomId] = roomEvent.getId()!; if (roomEvent) {
const eventId = roomEvent.getId();
if(eventId) this.readUpToMap[room.roomId] = eventId;
}
} }
this.messaging.on('action:org.matrix.msc2876.read_events', (ev: CustomEvent) => { this.messaging.on('action:org.matrix.msc2876.read_events', (ev: CustomEvent) => {
@@ -163,28 +167,16 @@ export class SmallWidget extends EventEmitter {
const stateEvents = state.events?.get(type); const stateEvents = state.events?.get(type);
for (const [key, eventObject] of stateEvents?.entries() ?? []) { Array.from(stateEvents?.values() ?? []).forEach(eventObject => {
events.push(eventObject.event); events.push(eventObject.event)
} })
return this.messaging?.transport.reply(ev.detail, { events }); return this.messaging?.transport.reply(ev.detail, { events });
}); });
/*
this.messaging?.on('action:content_loaded', () => {
this.messaging?.transport?.send('io.element.join', {
audioInput: 'true',
videoInput: 'true',
});
});
*/
this.client.on(ClientEvent.Event, this.onEvent); this.client.on(ClientEvent.Event, this.onEvent);
this.client.on(MatrixEventEvent.Decrypted, this.onEventDecrypted); this.client.on(MatrixEventEvent.Decrypted, this.onEventDecrypted);
//this.client.on(RoomStateEvent.Events, this.onStateUpdate);
this.client.on(ClientEvent.ToDeviceEvent, this.onToDeviceEvent); this.client.on(ClientEvent.ToDeviceEvent, this.onToDeviceEvent);
//this.client.on(RoomStateEvent.Events, this.onReadEvent);
// this.messaging.setViewedRoomId(this.roomId ?? null);
this.messaging.on( this.messaging.on(
`action:${WidgetApiFromWidgetAction.UpdateAlwaysOnScreen}`, `action:${WidgetApiFromWidgetAction.UpdateAlwaysOnScreen}`,
async (ev: CustomEvent<IStickyActionRequest>) => { async (ev: CustomEvent<IStickyActionRequest>) => {
@@ -196,7 +188,7 @@ export class SmallWidget extends EventEmitter {
this.messaging.transport.reply(ev.detail, {}); this.messaging.transport.reply(ev.detail, {});
} }
// Stop being persistent can be done instantly // Stop being persistent can be done instantly
//MAKE PERSISTENT HERE // MAKE PERSISTENT HERE
// Send the ack after the widget actually has become sticky. // Send the ack after the widget actually has become sticky.
} }
} }
@@ -272,21 +264,28 @@ export class SmallWidget extends EventEmitter {
const timeline = room.getLiveTimeline(); const timeline = room.getLiveTimeline();
const events = this.arrayFastClone(timeline.getEvents()).reverse().slice(0, 100); const events = this.arrayFastClone(timeline.getEvents()).reverse().slice(0, 100);
for (const timelineEvent of events) { let advanced = false;
if (timelineEvent.getId() === upToEventId) {
events.some(timelineEvent => {
const id = timelineEvent.getId();
if (id === upToEventId) {
// The event must be somewhere before the "read up to" marker // The event must be somewhere before the "read up to" marker
return false;
}
if (timelineEvent.getId() === ev.getId()) {
// The event is after the marker; advance it
this.readUpToMap[roomId] = evId;
return true; return true;
} }
}
// We can't say for sure whether the widget has seen the event; let's if (id === evId) {
// just assume that it has // The event is after the marker; advance it
return false; this.readUpToMap[roomId] = evId;
advanced = true;
return true;
}
// We can't say for sure whether the widget has seen the event; let's
// just assume that it has
return false;
});
return advanced;
} }
private feedEvent(ev: MatrixEvent): void { private feedEvent(ev: MatrixEvent): void {
@@ -401,12 +400,4 @@ export const createVirtualWidget = (
roomId, roomId,
// Add other required fields from IWidget if necessary // Add other required fields from IWidget if necessary
sender: creatorUserId, // Example: Assuming sender is the creator sender: creatorUserId, // Example: Assuming sender is the creator
content: {
// Example content structure
type,
url: url.toString(),
name,
data,
creatorUserId,
},
}); });

View File

@@ -60,7 +60,7 @@ export function CallNavStatus() {
> >
<Icon src={Icons.VolumeHigh}/> <Icon src={Icons.VolumeHigh}/>
<Text style={{ flexGrow: 1 }} size="B400" truncate> <Text style={{ flexGrow: 1 }} size="B400" truncate>
{mx.getRoom(activeCallRoomId)?.name} {activeCallRoomId ? mx.getRoom(activeCallRoomId)?.name : ""}
</Text> </Text>
</Chip> </Chip>
)} )}

View File

@@ -8,7 +8,7 @@ import React, {
useEffect, useEffect,
} from 'react'; } from 'react';
import { logger } from 'matrix-js-sdk/lib/logger'; import { logger } from 'matrix-js-sdk/lib/logger';
import { WidgetApiToWidgetAction, WidgetApiAction, ClientWidgetApi } from 'matrix-widget-api'; import { WidgetApiToWidgetAction, WidgetApiAction, ClientWidgetApi, IWidgetApiRequestData } from 'matrix-widget-api';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { SmallWidget } from '../../../features/call/SmallWidget'; import { SmallWidget } from '../../../features/call/SmallWidget';
@@ -30,15 +30,16 @@ interface CallContextState {
setActiveCallRoomId: (roomId: string | null) => void; setActiveCallRoomId: (roomId: string | null) => void;
viewedCallRoomId: string | null; viewedCallRoomId: string | null;
setViewedCallRoomId: (roomId: string | null) => void; setViewedCallRoomId: (roomId: string | null) => void;
hangUp: (room: string) => void; hangUp: () => void;
activeClientWidgetApi: ClientWidgetApi | null; activeClientWidgetApi: ClientWidgetApi | null;
activeClientWidget: SmallWidget | null; activeClientWidget: SmallWidget | null;
registerActiveClientWidgetApi: ( registerActiveClientWidgetApi: (
roomId: string | null, roomId: string | null,
clientWidgetApi: ClientWidgetApi | null, clientWidgetApi: ClientWidgetApi | null,
clientWidget: SmallWidget clientWidget: SmallWidget,
activeClientIframeRef: HTMLIFrameElement
) => void; ) => void;
sendWidgetAction: <T = unknown>( sendWidgetAction: <T extends IWidgetApiRequestData = IWidgetApiRequestData>(
action: WidgetApiToWidgetAction | string, action: WidgetApiToWidgetAction | string,
data: T data: T
) => Promise<void>; ) => Promise<void>;
@@ -60,7 +61,6 @@ interface CallProviderProps {
const DEFAULT_AUDIO_ENABLED = true; const DEFAULT_AUDIO_ENABLED = true;
const DEFAULT_VIDEO_ENABLED = false; const DEFAULT_VIDEO_ENABLED = false;
const DEFAULT_CHAT_OPENED = false; const DEFAULT_CHAT_OPENED = false;
const DEFAULT_CALL_ACTIVE = false;
export function CallProvider({ children }: CallProviderProps) { export function CallProvider({ children }: CallProviderProps) {
const [activeCallRoomId, setActiveCallRoomIdState] = useState<string | null>(null); const [activeCallRoomId, setActiveCallRoomIdState] = useState<string | null>(null);
@@ -69,11 +69,12 @@ export function CallProvider({ children }: CallProviderProps) {
const [activeClientWidgetApi, setActiveClientWidgetApiState] = useState<ClientWidgetApi | null>(null); const [activeClientWidgetApi, setActiveClientWidgetApiState] = useState<ClientWidgetApi | null>(null);
const [activeClientWidget, setActiveClientWidget] = useState<SmallWidget | null>(null); const [activeClientWidget, setActiveClientWidget] = useState<SmallWidget | null>(null);
const [activeClientWidgetApiRoomId, setActiveClientWidgetApiRoomId] = useState<string | null>(null); const [activeClientWidgetApiRoomId, setActiveClientWidgetApiRoomId] = useState<string | null>(null);
const [activeClientWidgetIframeRef, setActiveClientWidgetIframeRef] = useState<HTMLIFrameElement | null>(null)
const [isAudioEnabled, setIsAudioEnabledState] = useState<boolean>(DEFAULT_AUDIO_ENABLED); const [isAudioEnabled, setIsAudioEnabledState] = useState<boolean>(DEFAULT_AUDIO_ENABLED);
const [isVideoEnabled, setIsVideoEnabledState] = useState<boolean>(DEFAULT_VIDEO_ENABLED); const [isVideoEnabled, setIsVideoEnabledState] = useState<boolean>(DEFAULT_VIDEO_ENABLED);
const [isChatOpen, setIsChatOpenState] = useState<boolean>(DEFAULT_CHAT_OPENED); const [isChatOpen, setIsChatOpenState] = useState<boolean>(DEFAULT_CHAT_OPENED);
const [isActiveCallReady, setIsActiveCallReady] = useState<boolean>(DEFAULT_CALL_ACTIVE); const [isActiveCallReady, setIsActiveCallReady] = useState<boolean>(false);
const { roomIdOrAlias: viewedRoomId } = useParams<{ roomIdOrAlias: string }>(); const { roomIdOrAlias: viewedRoomId } = useParams<{ roomIdOrAlias: string }>();
@@ -97,11 +98,13 @@ export function CallProvider({ children }: CallProviderProps) {
( (
clientWidgetApi: ClientWidgetApi | null, clientWidgetApi: ClientWidgetApi | null,
clientWidget: SmallWidget | null, clientWidget: SmallWidget | null,
roomId: string | null roomId: string | null,
clientWidgetIframeRef: HTMLIFrameElement | null
) => { ) => {
setActiveClientWidgetApiState(clientWidgetApi); setActiveClientWidgetApiState(clientWidgetApi);
setActiveClientWidget(clientWidget); setActiveClientWidget(clientWidget);
setActiveClientWidgetApiRoomId(roomId); setActiveClientWidgetApiRoomId(roomId);
setActiveClientWidgetIframeRef(clientWidgetIframeRef)
}, },
[] []
); );
@@ -110,17 +113,19 @@ export function CallProvider({ children }: CallProviderProps) {
( (
roomId: string | null, roomId: string | null,
clientWidgetApi: ClientWidgetApi | null, clientWidgetApi: ClientWidgetApi | null,
clientWidget: SmallWidget | null clientWidget: SmallWidget | null,
clientWidgetIframeRef: HTMLIFrameElement | null
) => { ) => {
if (activeClientWidgetApi && activeClientWidgetApi !== clientWidgetApi) { if (activeClientWidgetApi && activeClientWidgetApi !== clientWidgetApi) {
logger.debug(`CallContext: Cleaning up listeners for previous clientWidgetApi instance.`); logger.debug(`CallContext: Cleaning up listeners for previous clientWidgetApi instance.`);
} }
if (roomId && clientWidgetApi) { if (roomId && clientWidgetApi) {
logger.debug(`CallContext: Registering active clientWidgetApi for room ${roomId}.`); logger.debug(`CallContext: Registering active clientWidgetApi for room ${roomId}.`);
setActiveClientWidgetApi(clientWidgetApi, clientWidget, roomId); setActiveClientWidgetApi(clientWidgetApi, clientWidget, roomId, clientWidgetIframeRef);
} else if (roomId === activeClientWidgetApiRoomId || roomId === null) { } else if (roomId === activeClientWidgetApiRoomId || roomId === null) {
setActiveClientWidgetApi(null, null, null); setActiveClientWidgetApi(null, null, null, null);
} }
}, },
[activeClientWidgetApi, activeClientWidgetApiRoomId, setActiveClientWidgetApi] [activeClientWidgetApi, activeClientWidgetApiRoomId, setActiveClientWidgetApi]
@@ -128,7 +133,7 @@ export function CallProvider({ children }: CallProviderProps) {
const hangUp = useCallback( const hangUp = useCallback(
() => { () => {
setActiveClientWidgetApi(null, null, null); setActiveClientWidgetApi(null, null, null, null);
setActiveCallRoomIdState(null); setActiveCallRoomIdState(null);
activeClientWidgetApi?.transport.send(`${WIDGET_HANGUP_ACTION}`, {}); activeClientWidgetApi?.transport.send(`${WIDGET_HANGUP_ACTION}`, {});
setIsActiveCallReady(false); setIsActiveCallReady(false);
@@ -143,7 +148,7 @@ export function CallProvider({ children }: CallProviderProps) {
const sendWidgetAction = useCallback( const sendWidgetAction = useCallback(
async <T = unknown,>(action: WidgetApiToWidgetAction | string, data: T): Promise<void> => { async <T extends IWidgetApiRequestData = IWidgetApiRequestData,>(action: WidgetApiToWidgetAction | string, data: T): Promise<void> => {
if (!activeClientWidgetApi) { if (!activeClientWidgetApi) {
logger.warn( logger.warn(
`CallContext: Cannot send action '${action}', no active API clientWidgetApi registered.` `CallContext: Cannot send action '${action}', no active API clientWidgetApi registered.`
@@ -163,6 +168,8 @@ export function CallProvider({ children }: CallProviderProps) {
data data
); );
await activeClientWidgetApi.transport.send(action as WidgetApiAction, data); await activeClientWidgetApi.transport.send(action as WidgetApiAction, data);
return Promise.resolve()
}, },
[activeClientWidgetApi, activeCallRoomId, activeClientWidgetApiRoomId] [activeClientWidgetApi, activeCallRoomId, activeClientWidgetApiRoomId]
); );
@@ -211,15 +218,14 @@ export function CallProvider({ children }: CallProviderProps) {
return; return;
} }
const api = activeClientWidgetApi; if (!activeClientWidgetApi) {
if (!api) {
return; return;
} }
const handleHangup = (ev: CustomEvent) => { const handleHangup = (ev: CustomEvent) => {
ev.preventDefault(); ev.preventDefault();
if (isActiveCallReady && ev.detail.widgetId === activeClientWidgetApi?.widget.id) { if (isActiveCallReady && ev.detail.widgetId === activeClientWidgetApi.widget.id) {
activeClientWidgetApi?.transport.reply(ev.detail, {}); activeClientWidgetApi.transport.reply(ev.detail, {});
} }
logger.debug( logger.debug(
`CallContext: Received hangup action from widget in room ${activeCallRoomId}.`, `CallContext: Received hangup action from widget in room ${activeCallRoomId}.`,
@@ -251,33 +257,39 @@ export function CallProvider({ children }: CallProviderProps) {
const handleOnScreenStateUpdate = (ev: CustomEvent) => { const handleOnScreenStateUpdate = (ev: CustomEvent) => {
ev.preventDefault(); ev.preventDefault();
api.transport.reply(ev.detail, {}); activeClientWidgetApi.transport.reply(ev.detail, {});
}; };
const handleOnTileLayout = (ev: CustomEvent) => { const handleOnTileLayout = (ev: CustomEvent) => {
ev.preventDefault(); ev.preventDefault();
api.transport.reply(ev.detail, {}); activeClientWidgetApi.transport.reply(ev.detail, {});
}; };
const handleJoin = (ev: CustomEvent) => { const handleJoin = (ev: CustomEvent) => {
ev.preventDefault(); ev.preventDefault();
api.transport.reply(ev.detail, {}); activeClientWidgetApi.transport.reply(ev.detail, {});
const iframeDoc = const iframeDoc =
api.iframe?.contentDocument || activeClientWidgetIframeRef?.contentWindow?.document ||
api.iframe?.contentWindow.document; activeClientWidgetIframeRef?.contentDocument;
const observer = new MutationObserver(() => {
const button = iframeDoc.querySelector('[data-testid="incall_leave"]'); if(iframeDoc){
if (button) {
button.addEventListener('click', () => { const observer = new MutationObserver(() => {
hangUp() const button = iframeDoc.querySelector('[data-testid="incall_leave"]');
}); if (button) {
} button.addEventListener('click', () => {
observer.disconnect(); hangUp()
}); });
logger.debug('Join Call'); }
observer.observe(iframeDoc, { childList: true, subtree: true }); observer.disconnect();
});
logger.debug('Join Call');
observer.observe(iframeDoc, { childList: true, subtree: true });
}
setIsActiveCallReady(true); setIsActiveCallReady(true);
}; };
@@ -291,13 +303,14 @@ export function CallProvider({ children }: CallProviderProps) {
video_enabled: isVideoEnabled, video_enabled: isVideoEnabled,
}); });
api.on(`action:${WIDGET_HANGUP_ACTION}`, handleHangup); activeClientWidgetApi.on(`action:${WIDGET_HANGUP_ACTION}`, handleHangup);
api.on(`action:${WIDGET_MEDIA_STATE_UPDATE_ACTION}`, handleMediaStateUpdate); activeClientWidgetApi.on(`action:${WIDGET_MEDIA_STATE_UPDATE_ACTION}`, handleMediaStateUpdate);
api.on(`action:${WIDGET_TILE_UPDATE}`, handleOnTileLayout); activeClientWidgetApi.on(`action:${WIDGET_TILE_UPDATE}`, handleOnTileLayout);
api.on(`action:${WIDGET_ON_SCREEN_ACTION}`, handleOnScreenStateUpdate); activeClientWidgetApi.on(`action:${WIDGET_ON_SCREEN_ACTION}`, handleOnScreenStateUpdate);
api.on(`action:${WIDGET_JOIN_ACTION}`, handleJoin); activeClientWidgetApi.on(`action:${WIDGET_JOIN_ACTION}`, handleJoin);
}, [ }, [
activeClientWidgetIframeRef,
activeClientWidgetApi, activeClientWidgetApi,
activeCallRoomId, activeCallRoomId,
activeClientWidgetApiRoomId, activeClientWidgetApiRoomId,

View File

@@ -1,8 +1,7 @@
import React, { createContext, ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import React, { createContext, ReactNode, useCallback, useEffect, useMemo, useRef } from 'react';
import { logger } from 'matrix-js-sdk/lib/logger'; import { logger } from 'matrix-js-sdk/lib/logger';
import { ClientWidgetApi } from 'matrix-widget-api'; import { ClientWidgetApi } from 'matrix-widget-api';
import { Box } from 'folds'; import { Box } from 'folds';
import { useParams } from 'react-router-dom';
import { useCallState } from './CallProvider'; import { useCallState } from './CallProvider';
import { import {
createVirtualWidget, createVirtualWidget,
@@ -19,7 +18,7 @@ interface PersistentCallContainerProps {
children: ReactNode; children: ReactNode;
} }
export const CallRefContext = createContext(null); export const CallRefContext = createContext<React.MutableRefObject<HTMLIFrameElement | null> | null>(null);
export function PersistentCallContainer({ children }: PersistentCallContainerProps) { export function PersistentCallContainer({ children }: PersistentCallContainerProps) {
const callIframeRef = useRef<HTMLIFrameElement | null>(null); const callIframeRef = useRef<HTMLIFrameElement | null>(null);
@@ -44,10 +43,10 @@ export function PersistentCallContainer({ children }: PersistentCallContainerPro
const setupWidget = useCallback( const setupWidget = useCallback(
( (
widgetApiRef: { current: ClientWidgetApi }, widgetApiRef: React.MutableRefObject<ClientWidgetApi | null>,
smallWidgetRef: { current: SmallWidget }, smallWidgetRef: React.MutableRefObject<SmallWidget | null>,
iframeRef: { current: { src: string } }, iframeRef: React.MutableRefObject<HTMLIFrameElement | null>,
skipLobby: { toString: () => any }, skipLobby: boolean,
themeKind: ThemeKind | null themeKind: ThemeKind | null
) => { ) => {
if (mx?.getUserId()) { if (mx?.getUserId()) {
@@ -112,7 +111,7 @@ export function PersistentCallContainer({ children }: PersistentCallContainerPro
const widgetApiInstance = smallWidget.startMessaging(iframeElement); const widgetApiInstance = smallWidget.startMessaging(iframeElement);
widgetApiRef.current = widgetApiInstance; widgetApiRef.current = widgetApiInstance;
registerActiveClientWidgetApi(roomIdToSet, widgetApiRef.current, smallWidget); registerActiveClientWidgetApi(roomIdToSet, widgetApiRef.current, smallWidget, iframeElement);
widgetApiInstance.once('ready', () => { widgetApiInstance.once('ready', () => {
logger.info(`PersistentCallContainer: Widget for ${roomIdToSet} is ready.`); logger.info(`PersistentCallContainer: Widget for ${roomIdToSet} is ready.`);