Skip to content

Commit 82901ad

Browse files
committed
wip
1 parent e6d6561 commit 82901ad

14 files changed

+335
-190
lines changed

packages/clerk-js/src/core/clerk.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ import type {
9595
WaitlistResource,
9696
Web3Provider,
9797
} from '@clerk/types';
98-
import type { QueryClient } from '@tanstack/query-core';
98+
import { QueryClient } from '@tanstack/query-core';
9999

100100
import { debugLogger, initDebugLogger } from '@/utils/debug';
101101

@@ -223,7 +223,7 @@ export class Clerk implements ClerkInterface {
223223
// converted to protected environment to support `updateEnvironment` type assertion
224224
protected environment?: EnvironmentResource | null;
225225

226-
#queryClient: QueryClient | undefined;
226+
#queryClient: QueryClient | undefined = new QueryClient();
227227
#publishableKey = '';
228228
#domain: DomainOrProxyUrl['domain'];
229229
#proxyUrl: DomainOrProxyUrl['proxyUrl'];
@@ -242,14 +242,19 @@ export class Clerk implements ClerkInterface {
242242
#touchThrottledUntil = 0;
243243
#publicEventBus = createClerkEventBus();
244244

245-
get __internal_queryClient(): QueryClient | undefined {
246-
return this.#queryClient;
245+
get __internal_queryClient(): { __tag: 'clerk-rq-client'; client: QueryClient } | undefined {
246+
return this.#queryClient
247+
? {
248+
__tag: 'clerk-rq-client', // make this a symbol
249+
client: this.#queryClient,
250+
}
251+
: undefined;
247252
}
248253

249254
public async getInternalQueryClient(): Promise<QueryClient> {
250-
const QueryClient = await import('./query-core').then(module => module.QueryClient);
255+
// const QueryClient = await import('./query-core').then(module => module.QueryClient);
251256
if (!this.#queryClient) {
252-
this.#queryClient = new QueryClient();
257+
// this.#queryClient = new QueryClient();
253258
// @ts-expect-error - queryClientStatus is not typed
254259
this.#publicEventBus.emit('queryClientStatus', 'ready');
255260
}

packages/react/src/contexts/ClerkContextProvider.tsx

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -88,20 +88,23 @@ export function ClerkContextProvider(props: ClerkContextProvider) {
8888
return { value };
8989
}, [orgId, organization]);
9090

91-
const [queryStatus, setQueryStatus] = React.useState('loading');
92-
93-
React.useEffect(() => {
94-
// @ts-expect-error - queryClientStatus is not typed
95-
clerk.on('queryClientStatus', setQueryStatus);
96-
return () => {
97-
// @ts-expect-error - queryClientStatus is not typed
98-
clerk.off('queryClientStatus', setQueryStatus);
99-
};
100-
}, [clerk]);
101-
102-
const queryClient = React.useMemo(() => {
103-
return clerk.__internal_queryClient;
104-
}, [queryStatus, clerkStatus]);
91+
// const [queryStatus, setQueryStatus] = React.useState('loading');
92+
93+
// React.useEffect(() => {
94+
// // @ts-expect-error - queryClientStatus is not typed
95+
// clerk.on('queryClientStatus', (e)=>{
96+
// console.log('on queryClientStatus', e);
97+
// setQueryStatus(e);
98+
// });
99+
// return () => {
100+
// // @ts-expect-error - queryClientStatus is not typed
101+
// clerk.off('queryClientStatus', setQueryStatus);
102+
// };
103+
// }, [clerk]);
104+
105+
// const queryClient = React.useMemo(() => {
106+
// return clerk.__internal_queryClient;
107+
// }, [queryStatus, clerkStatus]);
105108

106109
// console.log('queryStatus', queryStatus, queryClient);
107110

@@ -111,7 +114,7 @@ export function ClerkContextProvider(props: ClerkContextProvider) {
111114
<ClientContext.Provider value={clientCtx}>
112115
<SessionContext.Provider value={sessionCtx}>
113116
<OrganizationProvider
114-
key={clerkStatus + queryStatus}
117+
// key={clerkStatus + queryStatus}
115118
{...organizationCtx.value}
116119
// queryClient={queryClient}
117120
>

packages/shared/global.d.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,11 @@ interface ImportMetaEnv {
1010
interface ImportMeta {
1111
readonly env: ImportMetaEnv;
1212
}
13+
14+
declare module 'virtual:data-hooks/*' {
15+
// Generic export signatures to satisfy type resolution for virtual modules
16+
export const DataClientProvider: any;
17+
export const useSubscription: any;
18+
const mod: any;
19+
export default mod;
20+
}

packages/shared/src/react/contexts.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ import type {
1313
import type { PropsWithChildren } from 'react';
1414
import React from 'react';
1515

16-
import { SWRConfig } from './clerk-swr';
1716
import { createContextAndHook } from './hooks/createContextAndHook';
17+
import { DataClientProvider } from './providers/DataClientProvider';
1818

1919
const [ClerkInstanceContext, useClerkInstanceContext] = createContextAndHook<LoadedClerk>('ClerkInstanceContext');
2020
const [UserContext, useUserContext] = createContextAndHook<UserResource | null | undefined>('UserContext');
@@ -68,15 +68,15 @@ const OrganizationProvider = ({
6868
}
6969
>) => {
7070
return (
71-
<SWRConfig value={swrConfig}>
71+
<DataClientProvider>
7272
<OrganizationContextInternal.Provider
7373
value={{
7474
value: { organization },
7575
}}
7676
>
7777
{children}
7878
</OrganizationContextInternal.Provider>
79-
</SWRConfig>
79+
</DataClientProvider>
8080
);
8181
};
8282

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import type { BillingSubscriptionResource, EnvironmentResource } from '@clerk/types';
2+
import { useQuery } from '@tanstack/react-query';
3+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
4+
5+
import { eventMethodCalled } from '../../telemetry/events';
6+
import {
7+
useAssertWrappedByClerkProvider,
8+
useClerkInstanceContext,
9+
useOrganizationContext,
10+
useUserContext,
11+
} from '../contexts';
12+
import type { SubscriptionResult, UseSubscriptionParams } from './useSubscription.types';
13+
14+
const hookName = 'useSubscription';
15+
16+
/**
17+
* @internal
18+
*/
19+
export function useDebounce<T>(value: T, delay: number): T {
20+
const [throttledValue, setThrottledValue] = useState(value);
21+
const lastUpdated = useRef<number | null>(null);
22+
23+
useEffect(() => {
24+
const now = Date.now();
25+
26+
if (lastUpdated.current && now >= lastUpdated.current + delay) {
27+
lastUpdated.current = now;
28+
setThrottledValue(value);
29+
} else {
30+
const id = window.setTimeout(() => {
31+
lastUpdated.current = now;
32+
setThrottledValue(value);
33+
}, delay);
34+
35+
return () => window.clearTimeout(id);
36+
}
37+
}, [value, delay]);
38+
39+
return throttledValue;
40+
}
41+
42+
const useClerkQueryClient = () => {
43+
const clerk = useClerkInstanceContext();
44+
// // @ts-expect-error - __internal_queryClient is not typed
45+
// console.log('useClerkQueryClient, clerk', clerk.__internal_queryClient);
46+
// @ts-expect-error - __internal_queryClient is not typed
47+
const [queryStatus, setQueryStatus] = useState('__tag' in clerk.__internal_queryClient ? 'ready' : 'loading');
48+
console.log('useClerkQueryClient, queryStatus', queryStatus);
49+
useEffect(() => {
50+
// @ts-expect-error - queryClientStatus is not typed
51+
clerk.on('queryClientStatus', setQueryStatus);
52+
return () => {
53+
// @ts-expect-error - queryClientStatus is not typed
54+
clerk.off('queryClientStatus', setQueryStatus);
55+
};
56+
}, [clerk]);
57+
58+
const queryClient = useMemo(() => {
59+
// @ts-expect-error - __internal_queryClient is not typed
60+
console.log('useClerkQueryClient, clerk.__internal_queryClient', clerk.__internal_queryClient);
61+
// @ts-expect-error - __internal_queryClient is not typed
62+
return clerk.__internal_queryClient;
63+
// @ts-expect-error - __internal_queryClient is not typed
64+
}, [queryStatus, clerk.status, clerk.__internal_queryClient]);
65+
66+
const debouncedQueryStatus = useDebounce(
67+
'__tag' in queryClient && queryClient.__tag === 'clerk-rq-client' ? 'ready' : queryStatus,
68+
5_000,
69+
);
70+
console.log('useClerkQueryClient, debouncedQueryStatus', debouncedQueryStatus);
71+
72+
return [queryClient.client, debouncedQueryStatus];
73+
};
74+
75+
/**
76+
*
77+
*/
78+
export function useSubscription(params?: UseSubscriptionParams): SubscriptionResult<BillingSubscriptionResource> {
79+
useAssertWrappedByClerkProvider(hookName);
80+
81+
const clerk = useClerkInstanceContext();
82+
const user = useUserContext();
83+
const { organization } = useOrganizationContext();
84+
85+
// @ts-expect-error `__unstable__environment` is not typed
86+
const environment = clerk.__unstable__environment as unknown as EnvironmentResource | null | undefined;
87+
88+
clerk.telemetry?.record(eventMethodCalled(hookName));
89+
90+
const isOrganization = params?.for === 'organization';
91+
const billingEnabled = isOrganization
92+
? environment?.commerceSettings.billing.organization.enabled
93+
: environment?.commerceSettings.billing.user.enabled;
94+
95+
const [queryClient, queryStatus] = useClerkQueryClient();
96+
97+
const queryKey = useMemo(() => {
98+
return [
99+
'commerce-subscription',
100+
{
101+
userId: user?.id,
102+
args: { orgId: isOrganization ? organization?.id : undefined },
103+
},
104+
];
105+
}, [user?.id, isOrganization, organization?.id]);
106+
107+
console.log('enabled', Boolean(user?.id && billingEnabled) && clerk.status === 'ready' && queryStatus === 'ready');
108+
109+
const query = useQuery(
110+
{
111+
queryKey,
112+
queryFn: ({ queryKey }) => {
113+
const obj = queryKey[1] as { args: { orgId?: string } };
114+
console.log('queryFn, obj', obj);
115+
return clerk.billing.getSubscription(obj.args);
116+
},
117+
staleTime: 1_000 * 60,
118+
enabled: Boolean(user?.id && billingEnabled) && clerk.status === 'ready' && queryStatus === 'ready',
119+
},
120+
queryClient,
121+
);
122+
123+
const revalidate = useCallback(() => queryClient.invalidateQueries({ queryKey }), [queryClient, queryKey]);
124+
125+
return {
126+
data: query.data,
127+
error: query.error,
128+
isLoading: query.isLoading,
129+
isFetching: query.isFetching,
130+
revalidate,
131+
};
132+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import type { BillingSubscriptionResource, EnvironmentResource } from '@clerk/types';
2+
import { useCallback } from 'react';
3+
4+
import { eventMethodCalled } from '../../telemetry/events';
5+
import { useSWR } from '../clerk-swr';
6+
import {
7+
useAssertWrappedByClerkProvider,
8+
useClerkInstanceContext,
9+
useOrganizationContext,
10+
useUserContext,
11+
} from '../contexts';
12+
import type { SubscriptionResult, UseSubscriptionParams } from './useSubscription.types';
13+
14+
const hookName = 'useSubscription';
15+
16+
/**
17+
* @internal
18+
*/
19+
export function useSubscription(params?: UseSubscriptionParams): SubscriptionResult<BillingSubscriptionResource> {
20+
useAssertWrappedByClerkProvider(hookName);
21+
console.log('useSubscription SWR');
22+
23+
const clerk = useClerkInstanceContext();
24+
const user = useUserContext();
25+
const { organization } = useOrganizationContext();
26+
27+
// @ts-expect-error `__unstable__environment` is not typed
28+
const environment = clerk.__unstable__environment as unknown as EnvironmentResource | null | undefined;
29+
30+
clerk.telemetry?.record(eventMethodCalled(hookName));
31+
32+
const isOrganization = params?.for === 'organization';
33+
const billingEnabled = isOrganization
34+
? environment?.commerceSettings.billing.organization.enabled
35+
: environment?.commerceSettings.billing.user.enabled;
36+
37+
const swr = useSWR(
38+
billingEnabled
39+
? {
40+
type: 'commerce-subscription',
41+
userId: user?.id,
42+
args: { orgId: isOrganization ? organization?.id : undefined },
43+
}
44+
: null,
45+
({ args, userId }) => {
46+
if (userId) {
47+
return clerk.billing.getSubscription(args);
48+
}
49+
return null;
50+
},
51+
{
52+
dedupingInterval: 1_000 * 60,
53+
keepPreviousData: params?.keepPreviousData,
54+
},
55+
);
56+
57+
const revalidate = useCallback(() => {
58+
void swr.mutate();
59+
}, [swr]);
60+
61+
return {
62+
data: swr.data,
63+
error: swr.error,
64+
isLoading: swr.isLoading,
65+
isFetching: swr.isValidating,
66+
revalidate,
67+
};
68+
}

0 commit comments

Comments
 (0)