import React, { useEffect, useRef, useSyncExternalStore } from 'react'; import { Box, Icon, Icons, Text, color } from 'folds'; import { Room } from 'matrix-js-sdk'; import { Widget } from 'matrix-widget-api'; import { useMatrixClient } from '../../../hooks/useMatrixClient'; import { WidgetPermissionPrompt } from './WidgetPermissionPrompt'; import { attachWidgetSession, detachWidgetSession, getWidgetSession, subscribeWidgetSessions, widgetSessionKey, widgetSessionsVersion, } from './widgetSessions'; type RoomWidgetViewProps = { room: Room; widget: Widget; }; // Shows one room widget in the Widgets panel. The widget itself runs in a // session (widgetSessions.ts) whose sandboxed iframe lives in an app-level // layer and is positioned over this placeholder, so a widget that asked to // stay on screen can keep running in a floating window after the panel closes. // Display-only access is granted silently; reading/sending in this room is // asked for (WidgetPermissionPrompt) and, once granted, the room's live events // are fed to the widget. Re-attaches only when the widget id or its (template) // URL changes, so viewing a widget doesn't reload it on unrelated state updates. export function RoomWidgetView({ room, widget }: RoomWidgetViewProps) { const mx = useMatrixClient(); const slotRef = useRef(null); const widgetRef = useRef(widget); widgetRef.current = widget; const key = widgetSessionKey(room.roomId, widget.id); useSyncExternalStore(subscribeWidgetSessions, widgetSessionsVersion); const session = getWidgetSession(key); useEffect(() => { const slot = slotRef.current; if (!slot) return undefined; attachWidgetSession(mx, room, widgetRef.current, slot); return () => detachWidgetSession(key, slot); }, [mx, room, key, widget.templateUrl]); if (session?.blocked) { return ( This widget can't be loaded because its URL is on this app's own origin. ); } const addedBy = widget.creatorUserId ? (room.getMember(widget.creatorUserId)?.rawDisplayName ?? widget.creatorUserId) : undefined; return ( <> {session?.pendingAsk && ( )} ); }