Skip to content

Commit 5872c3a

Browse files
committed
feat: Add SeamQueryProvider
1 parent abcc3b0 commit 5872c3a

File tree

5 files changed

+308
-195
lines changed

5 files changed

+308
-195
lines changed

src/lib/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export * from './seam/components/index.js'
22
export * from './seam/index.js'
33
export * from './seam/SeamProvider.js'
4+
export * from './seam/SeamQueryProvider.js'

src/lib/seam/SeamProvider.tsx

Lines changed: 28 additions & 183 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
1-
import type {
2-
SeamHttp,
3-
SeamHttpOptionsWithClientSessionToken,
4-
} from '@seamapi/http/connect'
5-
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
6-
import {
7-
createContext,
8-
type PropsWithChildren,
9-
useContext,
10-
useEffect,
11-
useMemo,
12-
} from 'react'
1+
import type { SeamHttp } from '@seamapi/http/connect'
2+
import type { QueryClient } from '@tanstack/react-query'
3+
import { createContext, type PropsWithChildren, useMemo } from 'react'
134

5+
import {
6+
SeamQueryProvider,
7+
type SeamQueryProviderPropsWithClient,
8+
type SeamQueryProviderPropsWithClientSessionToken,
9+
type SeamQueryProviderPropsWithPublishableKey,
10+
} from 'lib/seam/SeamQueryProvider.js'
1411
import { useSeamFont } from 'lib/seam/use-seam-font.js'
1512
import { useSeamStyles } from 'lib/seam/use-seam-styles.js'
1613
import {
@@ -19,8 +16,6 @@ import {
1916
useUserTelemetry,
2017
} from 'lib/telemetry/index.js'
2118

22-
import { useSeamClient } from './use-seam-client.js'
23-
2419
declare global {
2520
// eslint-disable-next-line no-var
2621
var seam: SeamProviderProps | undefined
@@ -30,33 +25,28 @@ declare global {
3025
var seamTelemetryClient: TelemetryClient | undefined
3126
}
3227

33-
export interface SeamContext {
34-
client: SeamHttp | null
35-
clientOptions?: SeamProviderClientOptions | undefined
36-
publishableKey?: string | undefined
37-
userIdentifierKey?: string | undefined
38-
clientSessionToken?: string | undefined
39-
}
28+
// eslint-disable-next-line @typescript-eslint/no-empty-interface
29+
export interface SeamContext {}
4030

4131
export type SeamProviderProps =
4232
| SeamProviderPropsWithClient
4333
| SeamProviderPropsWithPublishableKey
4434
| SeamProviderPropsWithClientSessionToken
4535

46-
export interface SeamProviderPropsWithClient extends SeamProviderBaseProps {
47-
client: SeamHttp
48-
}
36+
export interface SeamProviderPropsWithClient
37+
extends SeamQueryProviderPropsWithClient,
38+
SeamProviderBaseProps {}
4939

5040
export interface SeamProviderPropsWithPublishableKey
5141
extends SeamProviderBaseProps,
52-
SeamProviderClientOptions {
42+
SeamQueryProviderPropsWithPublishableKey {
5343
publishableKey: string
5444
userIdentifierKey?: string
5545
}
5646

5747
export interface SeamProviderPropsWithClientSessionToken
5848
extends SeamProviderBaseProps,
59-
SeamProviderClientOptions {
49+
SeamQueryProviderPropsWithClientSessionToken {
6050
clientSessionToken: string
6151
}
6252

@@ -70,12 +60,6 @@ interface SeamProviderBaseProps extends PropsWithChildren {
7060
onSessionUpdate?: (client: SeamHttp) => void
7161
}
7262

73-
type SeamClientOptions = SeamHttpOptionsWithClientSessionToken
74-
75-
export type SeamProviderClientOptions = Pick<SeamClientOptions, 'endpoint'>
76-
77-
const defaultQueryClient = new QueryClient()
78-
7963
export const seamComponentsClassName = 'seam-components'
8064

8165
export function SeamProvider({
@@ -84,198 +68,59 @@ export function SeamProvider({
8468
disableCssInjection = false,
8569
disableFontInjection = false,
8670
unminifiyCss = false,
87-
onSessionUpdate = () => {},
88-
queryClient,
8971
telemetryClient,
9072
...props
9173
}: SeamProviderProps): JSX.Element {
9274
useSeamStyles({ disabled: disableCssInjection, unminified: unminifiyCss })
9375
useSeamFont({ disabled: disableFontInjection })
9476

77+
const { Provider } = seamContext
78+
79+
const endpoint = 'endpoint' in props ? props.endpoint : undefined
9580
const value = useMemo(() => {
9681
const context = createSeamContextValue(props)
97-
if (
98-
context.client == null &&
99-
context.publishableKey == null &&
100-
context.clientSessionToken == null
101-
) {
102-
return defaultSeamContextValue
103-
}
10482
return context
10583
}, [props])
10684

107-
if (
108-
value.client == null &&
109-
value.publishableKey == null &&
110-
value.clientSessionToken == null
111-
) {
112-
throw new Error(
113-
`Must provide either a Seam client, clientSessionToken, or a publishableKey.`
114-
)
115-
}
116-
117-
const { Provider } = seamContext
118-
119-
const endpoint = 'endpoint' in props ? props.endpoint : undefined
120-
12185
return (
12286
<div className={seamComponentsClassName}>
12387
<TelemetryProvider
12488
client={telemetryClient ?? globalThis.seamTelemetryClient}
12589
disabled={disableTelemetry}
12690
endpoint={endpoint}
12791
>
128-
<QueryClientProvider
129-
client={
130-
queryClient ?? globalThis.seamQueryClient ?? defaultQueryClient
131-
}
132-
>
92+
<SeamQueryProvider {...props}>
13393
<Provider value={value}>
134-
<Wrapper onSessionUpdate={onSessionUpdate}>{children}</Wrapper>
94+
<Telemetry>{children}</Telemetry>
13595
</Provider>
136-
</QueryClientProvider>
96+
</SeamQueryProvider>
13797
</TelemetryProvider>
13898
</div>
13999
)
140100
}
141101

142-
function Wrapper({
143-
onSessionUpdate,
144-
children,
145-
}: Required<Pick<SeamProviderProps, 'onSessionUpdate'>> &
146-
PropsWithChildren): JSX.Element | null {
102+
function Telemetry({ children }: PropsWithChildren): JSX.Element | null {
147103
useUserTelemetry()
148-
149-
const { client } = useSeamClient()
150-
useEffect(() => {
151-
if (client != null) onSessionUpdate(client)
152-
}, [onSessionUpdate, client])
153-
154104
return <>{children}</>
155105
}
156106

157107
const createDefaultSeamContextValue = (): SeamContext => {
158108
try {
159109
if (globalThis.seam == null) {
160-
return { client: null }
110+
return {}
161111
}
162112
return createSeamContextValue(globalThis.seam)
163113
} catch (err) {
164114
// eslint-disable-next-line no-console
165115
console.warn(err)
166-
return { client: null }
116+
return {}
167117
}
168118
}
169119

170-
const createSeamContextValue = (options: SeamProviderProps): SeamContext => {
171-
if (isSeamProviderPropsWithClient(options)) {
172-
return options
173-
}
174-
175-
if (isSeamProviderPropsWithClientSessionToken(options)) {
176-
const { clientSessionToken, ...clientOptions } = options
177-
return {
178-
clientSessionToken,
179-
clientOptions,
180-
client: null,
181-
}
182-
}
183-
184-
if (isSeamProviderPropsWithPublishableKey(options)) {
185-
const { publishableKey, userIdentifierKey, ...clientOptions } = options
186-
return {
187-
publishableKey,
188-
userIdentifierKey,
189-
clientOptions,
190-
client: null,
191-
}
192-
}
193-
194-
return { client: null }
120+
const createSeamContextValue = (_options: SeamProviderProps): SeamContext => {
121+
return {}
195122
}
196123

197124
const defaultSeamContextValue = createDefaultSeamContextValue()
198125

199-
export const seamContext = createContext<SeamContext>(defaultSeamContextValue)
200-
201-
export function useSeamContext(): SeamContext {
202-
return useContext(seamContext)
203-
}
204-
205-
const isSeamProviderPropsWithClient = (
206-
props: SeamProviderProps
207-
): props is SeamProviderPropsWithClient => {
208-
if (!('client' in props)) return false
209-
210-
const { client, ...otherProps } = props
211-
if (client == null) return false
212-
213-
const otherNonNullProps = Object.values(otherProps).filter((v) => v != null)
214-
if (otherNonNullProps.length > 0) {
215-
throw new InvalidSeamProviderProps(
216-
`The client prop cannot be used with ${otherNonNullProps.join(' or ')}.`
217-
)
218-
}
219-
220-
return true
221-
}
222-
223-
const isSeamProviderPropsWithPublishableKey = (
224-
props: SeamProviderProps
225-
): props is SeamProviderPropsWithPublishableKey & SeamProviderClientOptions => {
226-
if (!('publishableKey' in props)) return false
227-
228-
const { publishableKey } = props
229-
if (publishableKey == null) return false
230-
231-
if ('client' in props && props.client != null) {
232-
throw new InvalidSeamProviderProps(
233-
'The client prop cannot be used with the publishableKey prop.'
234-
)
235-
}
236-
237-
if ('clientSessionToken' in props && props.clientSessionToken != null) {
238-
throw new InvalidSeamProviderProps(
239-
'The clientSessionToken prop cannot be used with the publishableKey prop.'
240-
)
241-
}
242-
243-
return true
244-
}
245-
246-
const isSeamProviderPropsWithClientSessionToken = (
247-
props: SeamProviderProps
248-
): props is SeamProviderPropsWithClientSessionToken &
249-
SeamProviderClientOptions => {
250-
if (!('clientSessionToken' in props)) return false
251-
252-
const { clientSessionToken } = props
253-
if (clientSessionToken == null) return false
254-
255-
if ('client' in props && props.client != null) {
256-
throw new InvalidSeamProviderProps(
257-
'The client prop cannot be used with the clientSessionToken prop.'
258-
)
259-
}
260-
261-
if ('publishableKey' in props && props.publishableKey != null) {
262-
throw new InvalidSeamProviderProps(
263-
'The publishableKey prop cannot be used with the clientSessionToken prop.'
264-
)
265-
}
266-
267-
if ('userIdentifierKey' in props && props.userIdentifierKey != null) {
268-
throw new InvalidSeamProviderProps(
269-
'The userIdentifierKey prop cannot be used with the clientSessionToken prop.'
270-
)
271-
}
272-
273-
return true
274-
}
275-
276-
class InvalidSeamProviderProps extends Error {
277-
constructor(message: string) {
278-
super(`SeamProvider received invalid props: ${message}`)
279-
this.name = this.constructor.name
280-
}
281-
}
126+
const seamContext = createContext<SeamContext>(defaultSeamContextValue)

0 commit comments

Comments
 (0)