Skip to content

Commit 34674d5

Browse files
authoredMar 3, 2025··
fix(core): memoize scheduledPublishing calls, trigger only once (#8819)
1 parent aa3dc7f commit 34674d5

File tree

3 files changed

+86
-37
lines changed

3 files changed

+86
-37
lines changed
 

‎packages/sanity/src/core/scheduledPublishing/tool/contexts/ScheduledPublishingEnabledProvider.test.tsx

+11-4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
ScheduledPublishingEnabledProvider,
1111
useScheduledPublishingEnabled,
1212
} from './ScheduledPublishingEnabledProvider'
13+
import {cachedUsedScheduledPublishing} from './useHasUsedScheduledPublishing'
1314

1415
vi.mock('../../../hooks/useFeatureEnabled', () => ({
1516
useFeatureEnabled: vi.fn().mockReturnValue({}),
@@ -55,6 +56,7 @@ describe('ScheduledPublishingEnabledProvider - previously used', () => {
5556
beforeEach(() => {
5657
vi.clearAllMocks()
5758
mockClient(scheduleResponse)
59+
cachedUsedScheduledPublishing.clear()
5860
})
5961

6062
it('should not show scheduled publishing if user opt out and the feature is not enabled (any plan)', async () => {
@@ -68,7 +70,8 @@ describe('ScheduledPublishingEnabledProvider - previously used', () => {
6870
expect(value.result.current).toEqual({
6971
enabled: false,
7072
mode: null,
71-
hasUsedScheduledPublishing: {used: true, loading: false},
73+
// Workspace is not enabled, so we won't do a request to check if they have used it or not.
74+
hasUsedScheduledPublishing: {used: false, loading: false},
7275
})
7376
})
7477
it('should not show scheduled publishing if user opt out and the feature is enabled (any plan)', () => {
@@ -82,7 +85,8 @@ describe('ScheduledPublishingEnabledProvider - previously used', () => {
8285
expect(value.result.current).toEqual({
8386
enabled: false,
8487
mode: null,
85-
hasUsedScheduledPublishing: {used: true, loading: false},
88+
// Workspace is not enabled, so we won't do a request to check if they have used it or not.
89+
hasUsedScheduledPublishing: {used: false, loading: false},
8690
})
8791
})
8892

@@ -166,6 +170,7 @@ describe('ScheduledPublishingEnabledProvider - not previously used', () => {
166170
beforeEach(() => {
167171
vi.clearAllMocks()
168172
mockClient([])
173+
cachedUsedScheduledPublishing.clear()
169174
})
170175

171176
it('should not show scheduled publishing if user opt out and the feature is enabled (any plan)', () => {
@@ -221,7 +226,8 @@ describe('ScheduledPublishingEnabledProvider - not previously used', () => {
221226
expect(value.result.current).toEqual({
222227
enabled: true,
223228
mode: 'default',
224-
hasUsedScheduledPublishing: {used: false, loading: false},
229+
// Users have opted in, so we are not checking if they used it, we are just returning a default true value
230+
hasUsedScheduledPublishing: {used: true, loading: false},
225231
})
226232
})
227233
it('should show upsell mode if they have not used it before and opted in, and feature is not available (free plans)', () => {
@@ -238,7 +244,8 @@ describe('ScheduledPublishingEnabledProvider - not previously used', () => {
238244
expect(value.result.current).toEqual({
239245
enabled: true,
240246
mode: 'upsell',
241-
hasUsedScheduledPublishing: {used: false, loading: false},
247+
// Users have opted in, so we are not checking if they used it, we are just returning a default true value
248+
hasUsedScheduledPublishing: {used: true, loading: false},
242249
})
243250
})
244251
})

‎packages/sanity/src/core/scheduledPublishing/tool/contexts/ScheduledPublishingEnabledProvider.tsx

+8-33
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,12 @@
11
import {useContext, useMemo} from 'react'
2-
import {useObservable} from 'react-rx'
3-
import {catchError, map, type Observable, of} from 'rxjs'
42
import {ScheduledPublishingEnabledContext} from 'sanity/_singletons'
53

6-
import {useClient} from '../../../hooks/useClient'
74
import {useFeatureEnabled} from '../../../hooks/useFeatureEnabled'
85
import {useWorkspace} from '../../../studio/workspace'
9-
import {DEFAULT_STUDIO_CLIENT_OPTIONS} from '../../../studioClient'
10-
import {type Schedule} from '../../types'
11-
12-
interface HasUsedScheduledPublishing {
13-
used: boolean
14-
loading: boolean
15-
}
16-
const HAS_USED_SCHEDULED_PUBLISHING: HasUsedScheduledPublishing = {used: false, loading: true}
6+
import {
7+
type HasUsedScheduledPublishing,
8+
useHasUsedScheduledPublishing,
9+
} from './useHasUsedScheduledPublishing'
1710

1811
/**
1912
* @internal
@@ -41,33 +34,15 @@ interface ScheduledPublishingEnabledProviderProps {
4134
export function ScheduledPublishingEnabledProvider({
4235
children,
4336
}: ScheduledPublishingEnabledProviderProps) {
44-
const client = useClient(DEFAULT_STUDIO_CLIENT_OPTIONS)
45-
4637
const {enabled, isLoading, error} = useFeatureEnabled('scheduledPublishing')
4738
const {scheduledPublishing} = useWorkspace()
4839

4940
const isWorkspaceEnabled = scheduledPublishing.enabled
5041
const explicitEnabled = scheduledPublishing.__internal__workspaceEnabled
51-
52-
const hasUsedScheduledPublishing$: Observable<HasUsedScheduledPublishing> = useMemo(() => {
53-
const {dataset, projectId} = client.config()
54-
return client.observable
55-
.request<{schedules: Schedule[]}>({
56-
uri: `/schedules/${projectId}/${dataset}?limit=1`,
57-
tag: 'scheduled-publishing-used',
58-
})
59-
.pipe(
60-
map((res) => {
61-
return {used: res.schedules?.length > 0, loading: false}
62-
}),
63-
catchError(() => of({used: false, loading: false})),
64-
)
65-
}, [client])
66-
67-
const hasUsedScheduledPublishing = useObservable(
68-
hasUsedScheduledPublishing$,
69-
HAS_USED_SCHEDULED_PUBLISHING,
70-
)
42+
const hasUsedScheduledPublishing = useHasUsedScheduledPublishing({
43+
explicitEnabled,
44+
isWorkspaceEnabled,
45+
})
7146

7247
const value: ScheduledPublishingEnabledContextValue = useMemo(() => {
7348
if (!isWorkspaceEnabled || isLoading || error) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import {type SanityClient} from '@sanity/client'
2+
import {useMemo} from 'react'
3+
import {useObservable} from 'react-rx'
4+
import {catchError, map, type Observable, of, shareReplay} from 'rxjs'
5+
6+
import {useClient} from '../../../hooks/useClient'
7+
import {useWorkspace} from '../../../studio/workspace'
8+
import {DEFAULT_STUDIO_CLIENT_OPTIONS} from '../../../studioClient'
9+
import {type Schedule} from '../../types'
10+
11+
export interface HasUsedScheduledPublishing {
12+
used: boolean
13+
loading: boolean
14+
}
15+
16+
const HAS_USED_SCHEDULED_PUBLISHING: HasUsedScheduledPublishing = {used: false, loading: true}
17+
18+
export const cachedUsedScheduledPublishing = new Map<
19+
string,
20+
Observable<HasUsedScheduledPublishing>
21+
>()
22+
23+
function fetchUsedScheduledPublishing(
24+
client: SanityClient,
25+
): Observable<HasUsedScheduledPublishing> {
26+
const {dataset, projectId} = client.config()
27+
return client.observable
28+
.request<{
29+
schedules: Schedule[]
30+
}>({uri: `/schedules/${projectId}/${dataset}?limit=1`, tag: 'scheduled-publishing-used'})
31+
.pipe(
32+
map((res) => {
33+
return {used: res.schedules?.length > 0, loading: false}
34+
}),
35+
catchError(() => of({used: false, loading: false})),
36+
)
37+
}
38+
39+
export function useHasUsedScheduledPublishing({
40+
explicitEnabled,
41+
isWorkspaceEnabled,
42+
}: {
43+
explicitEnabled?: boolean
44+
isWorkspaceEnabled?: boolean
45+
}) {
46+
const client = useClient(DEFAULT_STUDIO_CLIENT_OPTIONS)
47+
const {projectId, dataset} = useWorkspace()
48+
const key = `${projectId}-${dataset}`
49+
if (!cachedUsedScheduledPublishing.get(key)) {
50+
const hasUsed = fetchUsedScheduledPublishing(client).pipe(shareReplay())
51+
cachedUsedScheduledPublishing.set(key, hasUsed)
52+
}
53+
const hasUsedScheduledPublishing$ = useMemo(() => {
54+
// If the feature is explicitly enabled, we don't need to check if it has been used
55+
if (explicitEnabled) {
56+
return of({used: true, loading: false})
57+
}
58+
// If the workspace has turned off the feature is explicitly enabled, we don't need to check if it has been used
59+
if (!isWorkspaceEnabled) {
60+
return of({used: false, loading: false})
61+
}
62+
63+
return cachedUsedScheduledPublishing.get(key) || of(HAS_USED_SCHEDULED_PUBLISHING)
64+
}, [key, explicitEnabled, isWorkspaceEnabled])
65+
66+
return useObservable(hasUsedScheduledPublishing$, HAS_USED_SCHEDULED_PUBLISHING)
67+
}

0 commit comments

Comments
 (0)
Please sign in to comment.