From 1dee7520c2399f6235616e157c61d188f5ea0cb9 Mon Sep 17 00:00:00 2001 From: "Visal .In" Date: Sun, 14 Jul 2024 22:48:23 +0700 Subject: [PATCH] feat: add save query caching --- src/components/gui/database-gui.tsx | 8 +- .../gui/sidebar/saved-doc-tab/index.tsx | 6 +- src/components/gui/tabs/query-tab.tsx | 21 +++-- src/components/gui/windows-tab.tsx | 17 ++-- src/drivers/saved-doc/remote-saved-doc.ts | 84 ++++++++++++++++--- src/messages/open-tab.tsx | 3 +- 6 files changed, 111 insertions(+), 28 deletions(-) diff --git a/src/components/gui/database-gui.tsx b/src/components/gui/database-gui.tsx index 83f53ead..4c708788 100644 --- a/src/components/gui/database-gui.tsx +++ b/src/components/gui/database-gui.tsx @@ -20,6 +20,7 @@ import { import SidebarTab, { SidebarTabItem } from "./sidebar-tab"; import SchemaView from "./schema-sidebar"; import SettingSidebar from "./sidebar/setting-sidebar"; + import { useDatabaseDriver } from "@/context/driver-provider"; import SavedDocTab from "./sidebar/saved-doc-tab"; @@ -37,6 +38,7 @@ export default function DatabaseGui() { const [tabs, setTabs] = useState(() => [ { title: "Query", + identifier: "query", key: "query", component: , icon: LucideCode, @@ -58,11 +60,13 @@ export default function DatabaseGui() { if (keys) { setTabs((currentTabs) => { const selectedTab = currentTabs[selectedTabIndex]; - const newTabs = currentTabs.filter((t) => !keys?.includes(t.key)); + const newTabs = currentTabs.filter( + (t) => !keys?.includes(t.identifier) + ); if (selectedTab) { const selectedTabNewIndex = newTabs.findIndex( - (t) => t.key === selectedTab.key + (t) => t.identifier === selectedTab.identifier ); if (selectedTabNewIndex < 0) { setSelectedTabIndex( diff --git a/src/components/gui/sidebar/saved-doc-tab/index.tsx b/src/components/gui/sidebar/saved-doc-tab/index.tsx index c55c978e..63705f48 100644 --- a/src/components/gui/sidebar/saved-doc-tab/index.tsx +++ b/src/components/gui/sidebar/saved-doc-tab/index.tsx @@ -184,13 +184,17 @@ export default function SavedDocTab() { onClose={() => setNamespaceToRemove(undefined)} onComplete={(docs) => { if (docDriver) { - closeTabs(docs.map((d) => d.id)); + closeTabs(docs.map((d) => TAB_PREFIX_SAVED_QUERY + d.id)); // Refresh new namespace list docDriver .getNamespaces() .then((r) => { setNamespaceList(r.map(mapNamespace)); + + if (selectedNamespace === namespaceToRemove.id) { + setSelectedNamespace(r[0].id); + } }) .catch(console.error); } diff --git a/src/components/gui/tabs/query-tab.tsx b/src/components/gui/tabs/query-tab.tsx index c8c91ad5..6bd8e692 100644 --- a/src/components/gui/tabs/query-tab.tsx +++ b/src/components/gui/tabs/query-tab.tsx @@ -32,6 +32,7 @@ import { SavedDocData, SavedDocInput, } from "@/drivers/saved-doc/saved-doc-driver"; +import { TAB_PREFIX_SAVED_QUERY } from "@/const"; interface QueryWindowProps { initialCode?: string; @@ -58,7 +59,7 @@ export default function QueryWindow({ const [progress, setProgress] = useState(); const [data, setData] = useState(); const [name, setName] = useState(initialName); - const { renameCurrentTab } = useTabsContext(); + const { changeCurrentTab } = useTabsContext(); const [namespaceName, setNamespaceName] = useState( initialNamespace ?? "Unsaved Query" @@ -124,10 +125,14 @@ export default function QueryWindow({ } }; - const onSaveComplete = useCallback((doc: SavedDocData) => { - setNamespaceName(doc.namespace.name); - setSavedKey(doc.id); - }, []); + const onSaveComplete = useCallback( + (doc: SavedDocData) => { + setNamespaceName(doc.namespace.name); + setSavedKey(doc.id); + changeCurrentTab({ identifier: TAB_PREFIX_SAVED_QUERY + doc.id }); + }, + [changeCurrentTab] + ); const onPrepareSaveContent = useCallback((): SavedDocInput => { return { content: code, name }; @@ -147,6 +152,7 @@ export default function QueryWindow({ ), key: "query_" + queryResult.order, + identifier: "query_" + queryResult.order, title: "Query " + (queryIdx + 1), icon: LucideGrid, })), @@ -154,6 +160,7 @@ export default function QueryWindow({ ? [ { key: "summary", + identifier: "summary", title: "Summary", icon: LucideMessageSquareWarning, component: ( @@ -183,7 +190,9 @@ export default function QueryWindow({ { - renameCurrentTab(e.currentTarget.value || "Unnamed Query"); + changeCurrentTab({ + title: e.currentTarget.value || "Unnamed Query", + }); }} placeholder="Please name your query" spellCheck="false" diff --git a/src/components/gui/windows-tab.tsx b/src/components/gui/windows-tab.tsx index 254e231b..c20d49b2 100644 --- a/src/components/gui/windows-tab.tsx +++ b/src/components/gui/windows-tab.tsx @@ -33,6 +33,7 @@ export interface WindowTabItemProps { component: JSX.Element; icon: LucideIcon; title: string; + identifier: string; key: string; } @@ -47,12 +48,12 @@ interface WindowTabsProps { const WindowTabsContext = createContext<{ replaceCurrentTab: (tab: WindowTabItemProps) => void; - renameCurrentTab: (name: string) => void; + changeCurrentTab: (value: { title?: string; identifier?: string }) => void; }>({ replaceCurrentTab: () => { throw new Error("Not implemented"); }, - renameCurrentTab: () => { + changeCurrentTab: () => { throw new Error("Not implemented"); }, }); @@ -91,10 +92,12 @@ export default function WindowTabs({ [tabs, selected, onTabsChange] ); - const renameCurrentTab = useCallback( - (name: string) => { + const changeCurrentTab = useCallback( + (value: { title?: string; identifier?: string }) => { if (tabs[selected]) { - tabs[selected].title = name; + if (value.title) tabs[selected].title = value.title; + if (value.identifier) tabs[selected].identifier = value.identifier; + if (onTabsChange) { onTabsChange([...tabs]); } @@ -104,8 +107,8 @@ export default function WindowTabs({ ); const contextValue = useMemo( - () => ({ replaceCurrentTab, renameCurrentTab }), - [replaceCurrentTab, renameCurrentTab] + () => ({ replaceCurrentTab, changeCurrentTab }), + [changeCurrentTab, replaceCurrentTab] ); const handleDragStart = useCallback( diff --git a/src/drivers/saved-doc/remote-saved-doc.ts b/src/drivers/saved-doc/remote-saved-doc.ts index 4f87c08a..1d97e590 100644 --- a/src/drivers/saved-doc/remote-saved-doc.ts +++ b/src/drivers/saved-doc/remote-saved-doc.ts @@ -19,25 +19,59 @@ import { export default class RemoteSavedDocDriver implements SavedDocDriver { protected databaseId: string; protected cb: (() => void)[] = []; + protected cacheNamespaceList: SavedDocNamespace[] | null = null; + protected cacheDocs: Record = {}; constructor(databaseId: string) { this.databaseId = databaseId; } - getNamespaces(): Promise { - return getDocNamespaceList(this.databaseId); + async getNamespaces(): Promise { + if (this.cacheNamespaceList) { + return this.cacheNamespaceList; + } + const t = await getDocNamespaceList(this.databaseId); + this.cacheNamespaceList = t; + return t; } - createNamespace(name: string): Promise { - return createDocNamespace(this.databaseId, name); + async createNamespace(name: string): Promise { + const t = await createDocNamespace(this.databaseId, name); + + if (this.cacheNamespaceList) { + this.cacheNamespaceList.push(t); + } + + return t; } - updateNamespace(id: string, name: string): Promise { - return updateDocNamespace(this.databaseId, id, name); + async updateNamespace(id: string, name: string): Promise { + const t = await updateDocNamespace(this.databaseId, id, name); + + if (this.cacheNamespaceList) { + this.cacheNamespaceList = this.cacheNamespaceList.map((n) => { + if (n.id === t.id) { + return t; + } + return n; + }); + } + + return t; } - removeNamespapce(id: string): Promise { - return removeDocNamespace(this.databaseId, id); + async removeNamespapce(id: string): Promise { + await removeDocNamespace(this.databaseId, id); + + if (this.cacheNamespaceList) { + this.cacheNamespaceList = this.cacheNamespaceList.filter( + (n) => n.id !== id + ); + + if (this.cacheNamespaceList.length === 0) { + this.cacheNamespaceList = null; + } + } } async createDoc( @@ -46,22 +80,50 @@ export default class RemoteSavedDocDriver implements SavedDocDriver { data: SavedDocInput ): Promise { const r = await createSavedDoc(this.databaseId, namespace, type, data); + + if (this.cacheDocs[r.namespace.id]) { + this.cacheDocs[r.namespace.id].unshift(r); + } + this.triggerChange(); return r; } - getDocs(namespaceId: string): Promise { - return getSavedDocList(this.databaseId, namespaceId); + async getDocs(namespaceId: string): Promise { + if (this.cacheDocs[namespaceId]) { + return this.cacheDocs[namespaceId]; + } + + const t = await getSavedDocList(this.databaseId, namespaceId); + this.cacheDocs[namespaceId] = t; + return t; } async updateDoc(id: string, data: SavedDocInput): Promise { - const r = updateSavedDoc(this.databaseId, id, data); + const r = await updateSavedDoc(this.databaseId, id, data); + + if (this.cacheDocs[r.namespace.id]) { + this.cacheDocs[r.namespace.id] = this.cacheDocs[r.namespace.id].map( + (d) => { + if (d.id === r.id) return r; + return d; + } + ); + } + this.triggerChange(); return r; } async removeDoc(id: string): Promise { const r = await removeSavedDoc(this.databaseId, id); + + for (const namespaceId of Object.keys(this.cacheDocs)) { + this.cacheDocs[namespaceId] = this.cacheDocs[namespaceId].filter( + (d) => d.id !== id + ); + } + this.triggerChange(); return r; } diff --git a/src/messages/open-tab.tsx b/src/messages/open-tab.tsx index 9b6b1584..824f7619 100644 --- a/src/messages/open-tab.tsx +++ b/src/messages/open-tab.tsx @@ -129,7 +129,7 @@ export function receiveOpenTabMessage({ }) { setTabs((prev) => { const key = generateKeyFromTab(newTab); - const foundIndex = prev.findIndex((tab) => tab.key === key); + const foundIndex = prev.findIndex((tab) => tab.identifier === key); if (foundIndex >= 0) { setSelectedTabIndex(foundIndex); @@ -144,6 +144,7 @@ export function receiveOpenTabMessage({ icon: generateIconFromTab(newTab), title, key, + identifier: key, component: generateComponent(newTab, title), }, ];