Skip to content

Commit 20914f3

Browse files
committed
feat(core): add connection to query store
1 parent cd558da commit 20914f3

File tree

5 files changed

+384
-12
lines changed

5 files changed

+384
-12
lines changed

apps/dashboard/src/App.tsx

Lines changed: 129 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
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'
32
import {Spinner, ThemeProvider} from '@sanity/ui'
43
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'
66

77
const 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/"

apps/sdk-app/src/App.tsx

Lines changed: 62 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import {SanityApp, SanityConfig} from '@sanity/sdk-react'
1+
import {SanityApp, SanityConfig, useQuery, useWindowConnection} from '@sanity/sdk-react'
22
import {Spinner, ThemeProvider} from '@sanity/ui'
33
import {buildTheme} from '@sanity/ui/theme'
4-
import {type JSX, Suspense} from 'react'
4+
import {type JSX, useEffect, useRef, useState} from 'react'
55

66
const theme = buildTheme({})
77

@@ -20,13 +20,70 @@ const devConfigs: SanityConfig[] = [
2020
},
2121
]
2222

23+
// Message types for iframe communication
24+
type QueryRequestMessage = {
25+
type: 'dashboard/v1/query/request'
26+
data: {
27+
queryId: string
28+
queryOptions: any
29+
requestId: string
30+
}
31+
}
32+
33+
type QueryResponseMessage = {
34+
type: 'dashboard/v1/query/response'
35+
data: {
36+
requestId: string
37+
data: unknown
38+
error?: string
39+
subscriptionId: string
40+
}
41+
}
42+
43+
// Test component to demonstrate query forwarding
44+
function QueryTest() {
45+
46+
// hack -- something in the node setup in the query store has a race condition
47+
useWindowConnection<QueryResponseMessage, QueryRequestMessage>({
48+
name: 'sdk-app',
49+
connectTo: 'dashboard',
50+
})
51+
52+
// This query should be forwarded to Dashboard when in iframe context
53+
const {data, isPending} = useQuery({
54+
query: '*[_type == "movie"][0...5]{_id, title, releaseYear}',
55+
projectId: 'ppsg7ml5',
56+
dataset: 'test',
57+
})
58+
59+
console.log('data', data)
60+
61+
return (
62+
<div style={{padding: 16}}>
63+
<h2>SDK App - Query Test</h2>
64+
<div style={{marginTop: 8}}>
65+
{/* <strong>Query Status:</strong> {isPending ? 'Loading...' : 'Loaded'} */}
66+
</div>
67+
<div style={{marginTop: 8}}>
68+
<strong>Data:</strong>
69+
<pre style={{marginTop: 4, padding: 8, backgroundColor: '#f5f5f5', borderRadius: 4, fontSize: '12px'}}>
70+
{/* {JSON.stringify(data, null, 2)} */}
71+
</pre>
72+
</div>
73+
<div style={{marginTop: 8, fontSize: '12px', color: '#666'}}>
74+
Check the browser console to see the query forwarding logs!
75+
</div>
76+
</div>
77+
)
78+
}
79+
2380
export default function App(): JSX.Element {
2481
return (
2582
<ThemeProvider theme={theme}>
2683
<SanityApp fallback={<Spinner />} config={devConfigs}>
27-
<Suspense>
28-
<div style={{padding: 16}}>SDK app scaffold</div>
29-
</Suspense>
84+
<div style={{height: '100vh', width: '100vw', overflow: 'auto'}}>
85+
<QueryTest />
86+
</div>
3087
</SanityApp>
3188
</ThemeProvider>
3289
)

packages/core/src/comlink/node/comlinkNodeStore.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ describe('nodeStore', () => {
2929
initialState.nodes.set('test-node', {
3030
options: {name: 'test-node', connectTo: 'parent'},
3131
node: mockNode,
32-
refCount: 1,
32+
status: 'idle',
3333
})
3434

3535
const cleanup = comlinkNodeStore.initialize?.({

0 commit comments

Comments
 (0)