1- import { createSubscriptionRequest , registerSubscription , unregisterSubscription } from '@sanity/sdk'
2- import { SanityApp , SanityConfig } from '@sanity/sdk-react'
1+ import { SanityApp , SanityConfig , useFrameConnection } from '@sanity/sdk-react'
32import { Spinner , ThemeProvider } from '@sanity/ui'
43import { buildTheme } from '@sanity/ui/theme'
5- import { type JSX , Suspense , useState } from 'react'
4+ import { type JSX , Suspense , useState , useEffect , useRef , useCallback } from 'react'
5+ import { registerSubscription , unregisterSubscription , createSubscriptionRequest } from '@sanity/sdk'
66
77const theme = buildTheme ( { } )
88
@@ -21,10 +21,132 @@ const devConfigs: SanityConfig[] = [
2121 } ,
2222]
2323
24+ // Message types for iframe communication
25+ type QueryRequestMessage = {
26+ type : 'dashboard/v1/query/request'
27+ data : {
28+ queryId : string
29+ queryOptions : any
30+ requestId : string
31+ }
32+ }
33+
34+ type QueryResponseMessage = {
35+ type : 'dashboard/v1/query/response'
36+ data : {
37+ requestId : string
38+ data : unknown
39+ error ?: string
40+ subscriptionId : string
41+ }
42+ }
43+
2444// SharedWorker test component
25- function SharedWorkerTest ( ) {
45+ function SharedWorkerTest ( { iframeRef } : { iframeRef : React . RefObject < HTMLIFrameElement | null > } ) {
2646 const [ subscriptionId , setSubscriptionId ] = useState < string | null > ( null )
2747 const [ status , setStatus ] = useState < string > ( 'Ready to test' )
48+ const [ connectionStatus , setConnectionStatus ] = useState < string > ( 'Not connected' )
49+ const connectionRef = useRef < ( ( ) => void ) | null > ( null )
50+
51+ // Stable status handler
52+ const handleStatus = useCallback ( ( status : string ) => {
53+ setConnectionStatus ( status )
54+ console . log ( '[Dashboard] Connection status:' , status )
55+ } , [ ] )
56+
57+ // Stable message handler
58+ const handleQueryRequest = useCallback ( async ( data : any ) => {
59+ console . log ( '[Dashboard] Received query request:' , data )
60+
61+ try {
62+ // Create a subscription request from the incoming query data
63+ const subscription = createSubscriptionRequest ( {
64+ storeName : 'query' ,
65+ projectId : data . queryOptions . projectId ,
66+ dataset : data . queryOptions . dataset ,
67+ params : {
68+ query : data . queryOptions . query ,
69+ options : data . queryOptions . params || { } ,
70+ } ,
71+ appId : 'dashboard-app' ,
72+ } )
73+
74+ console . log ( '[Dashboard] Creating subscription for query:' , subscription )
75+
76+ // Register the subscription with the SharedWorker (it will handle deduplication)
77+ const subscriptionId = await registerSubscription ( subscription )
78+ console . log ( '[Dashboard] Subscription registered with ID:' , subscriptionId )
79+
80+ // Return the subscription ID and any initial data
81+ return {
82+ requestId : data . requestId ,
83+ subscriptionId,
84+ data : { message : 'Query subscription created successfully' } ,
85+ }
86+ } catch ( error ) {
87+ console . error ( '[Dashboard] Error handling query request:' , error )
88+ return {
89+ requestId : data . requestId ,
90+ error : error instanceof Error ? error . message : String ( error ) ,
91+ subscriptionId : null ,
92+ }
93+ }
94+ } , [ ] )
95+
96+ const { connect} = useFrameConnection <
97+ QueryResponseMessage ,
98+ QueryRequestMessage
99+ > ( {
100+ name : 'dashboard' ,
101+ connectTo : 'sdk-app' ,
102+ targetOrigin : '*' ,
103+ onStatus : handleStatus ,
104+ heartbeat : false , // Disable heartbeat to reduce cycling
105+ onMessage : {
106+ 'dashboard/v1/query/request' : handleQueryRequest ,
107+ } ,
108+ } )
109+
110+ useEffect ( ( ) => {
111+ const handleIframeLoad = ( ) => {
112+ // Clean up any existing connection
113+ if ( connectionRef . current ) {
114+ connectionRef . current ( )
115+ connectionRef . current = null
116+ }
117+
118+ // Wait for iframe to be fully loaded
119+ setTimeout ( ( ) => {
120+ if ( iframeRef . current ?. contentWindow ) {
121+ try {
122+ const cleanup = connect ( iframeRef . current . contentWindow )
123+ connectionRef . current = cleanup
124+ console . log ( '[Dashboard] Connected to SDK app iframe' )
125+ } catch ( error ) {
126+ console . error ( '[Dashboard] Failed to connect to iframe:' , error )
127+ }
128+ }
129+ } , 100 )
130+ }
131+
132+ const iframe = iframeRef . current
133+ if ( iframe ) {
134+ iframe . addEventListener ( 'load' , handleIframeLoad )
135+
136+ // If iframe is already loaded, connect immediately
137+ if ( iframe . contentDocument ?. readyState === 'complete' ) {
138+ handleIframeLoad ( )
139+ }
140+
141+ return ( ) => {
142+ if ( connectionRef . current ) {
143+ connectionRef . current ( )
144+ connectionRef . current = null
145+ }
146+ iframe . removeEventListener ( 'load' , handleIframeLoad )
147+ }
148+ }
149+ } , [ connect ] )
28150
29151 const testSubscription = async ( ) => {
30152 // eslint-disable-next-line no-console
@@ -69,7 +191,8 @@ function SharedWorkerTest() {
69191 < div style = { { padding : 12 , borderBottom : '1px solid #eee' } } >
70192 < div > Dashboard (iframes sdk-app below)</ div >
71193 < div style = { { marginTop : 8 , fontSize : '14px' } } >
72- < div > SharedWorker Test:</ div >
194+ < div > Comlink Connection Status: { connectionStatus } </ div >
195+ < div style = { { marginTop : 8 } } > SharedWorker Test:</ div >
73196 < div style = { { marginTop : 4 } } >
74197 < button onClick = { testSubscription } disabled = { ! ! subscriptionId } >
75198 Test Subscription
@@ -106,7 +229,7 @@ export default function App(): JSX.Element {
106229 flexDirection : 'column' ,
107230 } }
108231 >
109- < SharedWorkerTest />
232+ < SharedWorkerTest iframeRef = { iframeRef } />
110233 < iframe
111234 title = "sdk-app"
112235 src = "http://localhost:3341/"
0 commit comments