Skip to content

Commit

Permalink
feat: add save query caching
Browse files Browse the repository at this point in the history
  • Loading branch information
invisal committed Jul 14, 2024
1 parent 015fef9 commit 1dee752
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 28 deletions.
8 changes: 6 additions & 2 deletions src/components/gui/database-gui.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand All @@ -37,6 +38,7 @@ export default function DatabaseGui() {
const [tabs, setTabs] = useState<WindowTabItemProps[]>(() => [
{
title: "Query",
identifier: "query",
key: "query",
component: <QueryWindow initialName="Query" />,
icon: LucideCode,
Expand All @@ -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(
Expand Down
6 changes: 5 additions & 1 deletion src/components/gui/sidebar/saved-doc-tab/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
21 changes: 15 additions & 6 deletions src/components/gui/tabs/query-tab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -58,7 +59,7 @@ export default function QueryWindow({
const [progress, setProgress] = useState<MultipleQueryProgress>();
const [data, setData] = useState<MultipleQueryResult[]>();
const [name, setName] = useState(initialName);
const { renameCurrentTab } = useTabsContext();
const { changeCurrentTab } = useTabsContext();

const [namespaceName, setNamespaceName] = useState(
initialNamespace ?? "Unsaved Query"
Expand Down Expand Up @@ -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 };
Expand All @@ -147,13 +152,15 @@ export default function QueryWindow({
<QueryResult result={queryResult} key={queryResult.order} />
),
key: "query_" + queryResult.order,
identifier: "query_" + queryResult.order,
title: "Query " + (queryIdx + 1),
icon: LucideGrid,
})),
...(progress
? [
{
key: "summary",
identifier: "summary",
title: "Summary",
icon: LucideMessageSquareWarning,
component: (
Expand Down Expand Up @@ -183,7 +190,9 @@ export default function QueryWindow({
</span>
<input
onBlur={(e) => {
renameCurrentTab(e.currentTarget.value || "Unnamed Query");
changeCurrentTab({
title: e.currentTarget.value || "Unnamed Query",
});
}}
placeholder="Please name your query"
spellCheck="false"
Expand Down
17 changes: 10 additions & 7 deletions src/components/gui/windows-tab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export interface WindowTabItemProps {
component: JSX.Element;
icon: LucideIcon;
title: string;
identifier: string;
key: string;
}

Expand All @@ -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");
},
});
Expand Down Expand Up @@ -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]);
}
Expand All @@ -104,8 +107,8 @@ export default function WindowTabs({
);

const contextValue = useMemo(
() => ({ replaceCurrentTab, renameCurrentTab }),
[replaceCurrentTab, renameCurrentTab]
() => ({ replaceCurrentTab, changeCurrentTab }),
[changeCurrentTab, replaceCurrentTab]
);

const handleDragStart = useCallback(
Expand Down
84 changes: 73 additions & 11 deletions src/drivers/saved-doc/remote-saved-doc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, SavedDocData[]> = {};

constructor(databaseId: string) {
this.databaseId = databaseId;
}

getNamespaces(): Promise<SavedDocNamespace[]> {
return getDocNamespaceList(this.databaseId);
async getNamespaces(): Promise<SavedDocNamespace[]> {
if (this.cacheNamespaceList) {
return this.cacheNamespaceList;
}
const t = await getDocNamespaceList(this.databaseId);
this.cacheNamespaceList = t;
return t;
}

createNamespace(name: string): Promise<SavedDocNamespace> {
return createDocNamespace(this.databaseId, name);
async createNamespace(name: string): Promise<SavedDocNamespace> {
const t = await createDocNamespace(this.databaseId, name);

if (this.cacheNamespaceList) {
this.cacheNamespaceList.push(t);

Check warning on line 42 in src/drivers/saved-doc/remote-saved-doc.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement
}

Check warning on line 43 in src/drivers/saved-doc/remote-saved-doc.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch

return t;
}

updateNamespace(id: string, name: string): Promise<SavedDocNamespace> {
return updateDocNamespace(this.databaseId, id, name);
async updateNamespace(id: string, name: string): Promise<SavedDocNamespace> {
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<void> {
return removeDocNamespace(this.databaseId, id);
async removeNamespapce(id: string): Promise<void> {
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;

Check warning on line 72 in src/drivers/saved-doc/remote-saved-doc.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement
}

Check warning on line 73 in src/drivers/saved-doc/remote-saved-doc.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch
}
}

async createDoc(
Expand All @@ -46,22 +80,50 @@ export default class RemoteSavedDocDriver implements SavedDocDriver {
data: SavedDocInput
): Promise<SavedDocData> {
const r = await createSavedDoc(this.databaseId, namespace, type, data);

if (this.cacheDocs[r.namespace.id]) {
this.cacheDocs[r.namespace.id].unshift(r);

Check warning on line 85 in src/drivers/saved-doc/remote-saved-doc.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🧾 Statement is not covered

Warning! Not covered statement
}

Check warning on line 86 in src/drivers/saved-doc/remote-saved-doc.ts

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch

this.triggerChange();
return r;
}

getDocs(namespaceId: string): Promise<SavedDocData[]> {
return getSavedDocList(this.databaseId, namespaceId);
async getDocs(namespaceId: string): Promise<SavedDocData[]> {
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<SavedDocData> {
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<void> {
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;
}
Expand Down
3 changes: 2 additions & 1 deletion src/messages/open-tab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -144,6 +144,7 @@ export function receiveOpenTabMessage({
icon: generateIconFromTab(newTab),
title,
key,
identifier: key,
component: generateComponent(newTab, title),
},
];
Expand Down

0 comments on commit 1dee752

Please sign in to comment.