This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

semble / src / webapp / lib / embed / rpcSessionProvider.tsx
2.1 kB 76 lines
1'use client'; 2 3import { createContext, use, useEffect, useRef, useState } from 'react'; 4import { newMessagePortRpcSession, type RpcStub } from 'capnweb'; 5 6type EmbedBlockData = 7 | { type: 'text'; content: string } 8 | { 9 type: 'embed'; 10 url: string; 11 height?: number; 12 aspectRatio?: string; 13 }; 14 15// Define the interface the *host* exposes (methods you can call) 16export type HostMethods = { 17 open(url: string): void; 18 replaceWith(block: EmbedBlockData): void; 19 addBelow(block: EmbedBlockData): void; 20}; 21 22export type HostSession = RpcStub<HostMethods>; 23 24const RpcSessionContext = createContext<HostSession | null>(null); 25 26function connectToHost(): Promise<HostSession> { 27 return new Promise((resolve) => { 28 // Listen for the host to send us a MessagePort 29 function handleMessage(event: MessageEvent) { 30 if (event.data?.type !== 'parts.page.channel') return; 31 32 const port = event.ports[0]; 33 if (!port) return; 34 35 // Start the RPC session over the transferred port 36 const session = newMessagePortRpcSession(port, null) as HostSession; 37 resolve(session); 38 39 // Remove listener once connected 40 window.removeEventListener('message', handleMessage); 41 } 42 43 window.addEventListener('message', handleMessage); 44 45 // Signal to the host that we want to connect 46 window.parent.postMessage({ type: 'parts.page.connect' }, '*'); 47 }); 48} 49 50export function RpcSessionProvider({ 51 children, 52}: { 53 children: React.ReactNode; 54}) { 55 const [session, setSession] = useState<HostSession | null>(null); 56 const connectingRef = useRef(false); 57 58 useEffect(() => { 59 // De-dupe: only connect once even in StrictMode double-mount 60 if (connectingRef.current) return; 61 connectingRef.current = true; 62 63 connectToHost().then((session) => setSession(() => session)); 64 65 return () => { 66 // Cleanup on unmount 67 session?.[Symbol.dispose](); 68 }; 69 }, [session]); 70 71 return <RpcSessionContext value={session}>{children}</RpcSessionContext>; 72} 73 74export function useRpcSession() { 75 return use(RpcSessionContext); 76}