Skip to content

Commit 4a64e4b

Browse files
committed
Update useCopiedStore to use different keyed contexts
1 parent ca21204 commit 4a64e4b

File tree

1 file changed

+30
-35
lines changed

1 file changed

+30
-35
lines changed

packages/gitbook/src/components/PageActions/PageActions.tsx

Lines changed: 30 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { Icon, type IconName, IconStyle } from '@gitbook/icons';
1111
import assertNever from 'assert-never';
1212
import QuickLRU from 'quick-lru';
1313
import React from 'react';
14-
import { create } from 'zustand';
14+
import { createStore, useStore } from 'zustand';
1515

1616
type PageActionType = 'button' | 'dropdown-menu-item';
1717

@@ -41,44 +41,49 @@ export function ActionOpenAssistant(props: { assistant: Assistant; type: PageAct
4141
type CopiedStore = {
4242
copied: boolean;
4343
loading: boolean;
44-
key?: string;
44+
setLoading: (loading: boolean) => void;
45+
copy: (data: string, opts?: { onSuccess?: () => void }) => void;
4546
};
4647

47-
// We need to store everything in a store to share the state between every instance of the component.
48-
const useCopiedStore = create<
49-
CopiedStore & {
50-
setLoading: (loading: boolean) => void;
51-
copy: (data: string, opts?: { onSuccess?: () => void; key?: string }) => void;
52-
}
53-
>((set) => {
48+
const createCopiedStateStore = () => {
5449
let timeoutRef: ReturnType<typeof setTimeout> | null = null;
55-
56-
return {
50+
return createStore<CopiedStore>()((set) => ({
5751
copied: false,
5852
loading: false,
59-
key: undefined,
6053
setLoading: (loading: boolean) => set({ loading }),
6154
copy: async (data, opts) => {
62-
const { onSuccess, key } = opts || {};
55+
const { onSuccess } = opts || {};
6356

6457
if (timeoutRef) {
6558
clearTimeout(timeoutRef);
6659
}
6760

6861
await navigator.clipboard.writeText(data);
6962

70-
set({ copied: true, key: key });
63+
set({ copied: true });
7164

7265
timeoutRef = setTimeout(() => {
7366
set({ copied: false });
7467
onSuccess?.();
75-
76-
// Reset the timeout ref to avoid multiple timeouts
7768
timeoutRef = null;
7869
}, 1500);
7970
},
80-
};
81-
});
71+
}));
72+
};
73+
74+
const copiedStores = new Map<string, ReturnType<typeof createCopiedStateStore>>();
75+
76+
const getOrCreateCopiedStoreByKey = (storeKey: string) => {
77+
const existing = copiedStores.get(storeKey);
78+
if (existing) return existing;
79+
const created = createCopiedStateStore();
80+
copiedStores.set(storeKey, created);
81+
return created;
82+
};
83+
84+
function useCopiedStore(stateKey: string) {
85+
return useStore(getOrCreateCopiedStoreByKey(stateKey));
86+
}
8287

8388
/**
8489
* Cache for the markdown versbion of the page.
@@ -98,7 +103,7 @@ export function ActionCopyMarkdown(props: {
98103

99104
const closeDropdown = useDropdownMenuClose();
100105

101-
const { copied, loading, setLoading, copy, key } = useCopiedStore();
106+
const { copied, loading, setLoading, copy } = useCopiedStore('markdown');
102107

103108
// Fetch the markdown from the page
104109
const fetchMarkdown = async () => {
@@ -120,7 +125,6 @@ export function ActionCopyMarkdown(props: {
120125
}
121126

122127
copy(markdownCache.get(markdownPageURL) || (await fetchMarkdown()), {
123-
key: 'markdown',
124128
onSuccess: () => {
125129
// We close the dropdown menu if the action is a dropdown menu item and not the default action.
126130
if (type === 'dropdown-menu-item' && !isDefaultAction) {
@@ -133,17 +137,9 @@ export function ActionCopyMarkdown(props: {
133137
return (
134138
<PageActionWrapper
135139
type={type}
136-
icon={copied && key === 'markdown' ? 'check' : 'copy'}
137-
label={
138-
copied && key === 'markdown'
139-
? tString(language, 'code_copied')
140-
: tString(language, 'copy_page')
141-
}
142-
shortLabel={
143-
copied && key === 'markdown'
144-
? tString(language, 'code_copied')
145-
: tString(language, 'code_copy')
146-
}
140+
icon={copied ? 'check' : 'copy'}
141+
label={copied ? tString(language, 'code_copied') : tString(language, 'copy_page')}
142+
shortLabel={copied ? tString(language, 'code_copied') : tString(language, 'code_copy')}
147143
description={tString(language, 'copy_page_markdown')}
148144
onClick={onClick}
149145
loading={loading}
@@ -308,19 +304,18 @@ export function CopyToClipboard(props: {
308304
const closeDropdown = useDropdownMenuClose();
309305

310306
const language = useLanguage();
311-
const { copied, copy, key } = useCopiedStore();
307+
const { copied, copy } = useCopiedStore('label');
312308

313309
return (
314310
<PageActionWrapper
315311
type={type}
316-
icon={copied && key === label ? 'check' : icon}
317-
label={copied && key === label ? tString(language, 'code_copied') : label}
312+
icon={copied ? 'check' : icon}
313+
label={copied ? tString(language, 'code_copied') : label}
318314
description={description}
319315
onClick={(e) => {
320316
e.preventDefault();
321317

322318
copy(data, {
323-
key: label,
324319
onSuccess: () => {
325320
if (type === 'dropdown-menu-item') {
326321
closeDropdown();

0 commit comments

Comments
 (0)