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'
3
2
import { Spinner , ThemeProvider } from '@sanity/ui'
4
3
import { 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'
6
6
7
7
const theme = buildTheme ( { } )
8
8
@@ -21,10 +21,132 @@ const devConfigs: SanityConfig[] = [
21
21
} ,
22
22
]
23
23
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
+
24
44
// SharedWorker test component
25
- function SharedWorkerTest ( ) {
45
+ function SharedWorkerTest ( { iframeRef } : { iframeRef : React . RefObject < HTMLIFrameElement | null > } ) {
26
46
const [ subscriptionId , setSubscriptionId ] = useState < string | null > ( null )
27
47
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 ] )
28
150
29
151
const testSubscription = async ( ) => {
30
152
// eslint-disable-next-line no-console
@@ -69,7 +191,8 @@ function SharedWorkerTest() {
69
191
< div style = { { padding : 12 , borderBottom : '1px solid #eee' } } >
70
192
< div > Dashboard (iframes sdk-app below)</ div >
71
193
< 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 >
73
196
< div style = { { marginTop : 4 } } >
74
197
< button onClick = { testSubscription } disabled = { ! ! subscriptionId } >
75
198
Test Subscription
@@ -106,7 +229,7 @@ export default function App(): JSX.Element {
106
229
flexDirection : 'column' ,
107
230
} }
108
231
>
109
- < SharedWorkerTest />
232
+ < SharedWorkerTest iframeRef = { iframeRef } />
110
233
< iframe
111
234
title = "sdk-app"
112
235
src = "http://localhost:3341/"
0 commit comments