Skip to content

Commit

Permalink
feat: save will detect the namespace
Browse files Browse the repository at this point in the history
  • Loading branch information
invisal committed Jul 11, 2024
1 parent 6526ee2 commit e40deb7
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 32 deletions.
56 changes: 48 additions & 8 deletions src/components/gui/tabs/query-tab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useCallback, useMemo, useRef, useState } from "react";
import { identify } from "sql-query-identifier";
import {
LucideGrid,
LucideLoader,
LucideMessageSquareWarning,
LucidePlay,
LucideSave,
Expand Down Expand Up @@ -32,11 +33,15 @@ import { useSchema } from "@/context/schema-provider";
interface QueryWindowProps {
initialCode?: string;
initialName: string;
initialSavedKey?: string;
initialNamespace?: string;
}

export default function QueryWindow({
initialCode,
initialName,
initialSavedKey,
initialNamespace,
}: QueryWindowProps) {
const { schema } = useAutoComplete();
const { databaseDriver, docDriver } = useDatabaseDriver();
Expand All @@ -52,6 +57,12 @@ export default function QueryWindow({
const [name, setName] = useState(initialName);
const { renameCurrentTab } = useTabsContext();

const [namespaceName, setNamespaceName] = useState(
initialNamespace ?? "Unsaved Query"
);
const [savedKey, setSavedKey] = useState<string | undefined>(initialSavedKey);
const [saveLoading, setSaveLoading] = useState(false);

const onRunClicked = (all = false) => {
const statements = identify(code, {
dialect: "sqlite",
Expand Down Expand Up @@ -113,12 +124,32 @@ export default function QueryWindow({

const onSaveQuery = useCallback(() => {
if (docDriver) {
docDriver.createDoc("sql", docDriver.getCurrentNamespace(), {
content: code,
name: name || "Unnamed Query",
});
setSaveLoading(true);
if (savedKey) {
docDriver
.updateDoc(savedKey, {
content: code,
name: name || "Unnamed Query",
})
.finally(() => {
setSaveLoading(false);
});
} else {
docDriver
.createDoc("sql", docDriver.getCurrentNamespace(), {
content: code,
name: name || "Unnamed Query",
})
.then((d) => {
setSavedKey(d.id);
setNamespaceName(d.namespace.name);
})
.finally(() => {
setSaveLoading(false);
});
}
}
}, [docDriver, code, name]);
}, [docDriver, code, name, savedKey]);

const windowTab = useMemo(() => {
return (
Expand Down Expand Up @@ -162,7 +193,7 @@ export default function QueryWindow({
<div className="absolute left-0 right-0 top-0 bottom-0 flex flex-col">
<div className="border-b pl-2 pr-1 py-1 flex">
<div className="text-xs shrink-0 items-center flex text-secondary-foreground p-1">
Unsaved Query /
{namespaceName} /
</div>
<div className="inline-block relative">
<span className="inline-block text-xs p-1 outline-none font-semibold min-w-[175px] border border-background opacity-0">
Expand Down Expand Up @@ -227,8 +258,17 @@ export default function QueryWindow({
<div>Col {columnNumber + 1}</div>
</div>

<Button size="sm" onClick={onSaveQuery} className="mr-2">
<LucideSave className="w-4 h-4 mr-2" />
<Button
size="sm"
onClick={onSaveQuery}
className="mr-2"
disabled={saveLoading}
>
{saveLoading ? (
<LucideLoader className="w-4 h-4 animate-spin mr-2" />
) : (
<LucideSave className="w-4 h-4 mr-2" />
)}
Save
</Button>
</div>
Expand Down
42 changes: 32 additions & 10 deletions src/components/gui/tabs/saved-doc-tab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,33 @@ function mapDoc(data: SavedDocData): ListViewItem<SavedDocData> {
};
}

function SavedDocNamespaceDocList({ namespaceId }: { namespaceId?: string }) {
function SavedDocNamespaceDocList({
namespaceData,
}: {
namespaceData?: SavedDocNamespace;
}) {
const { docDriver } = useDatabaseDriver();
const [selected, setSelected] = useState<string>();
const [docList, setDocList] = useState<ListViewItem<SavedDocData>[]>([]);

useEffect(() => {
const namespaceId = namespaceData?.id;

if (docDriver && namespaceId) {
docDriver?.getDocs(namespaceId).then((r) => {
docDriver.getDocs(namespaceId).then((r) => {
setDocList(r.map(mapDoc));
});

const onDocChange = () => {
docDriver?.getDocs(namespaceId).then((r) => {
setDocList(r.map(mapDoc));
});
};

docDriver.addChangeListener(onDocChange);
return () => docDriver.removeChangeListener(onDocChange);
}
}, [docDriver, namespaceId]);
}, [docDriver, namespaceData]);

return (
<ListView
Expand All @@ -54,6 +69,7 @@ function SavedDocNamespaceDocList({ namespaceId }: { namespaceId?: string }) {
saved: {
key: item.key,
sql: item.data.content,
namespaceName: namespaceData?.name,
},
});
}}
Expand All @@ -69,12 +85,14 @@ export default function SavedDocTab() {
>([]);

useEffect(() => {
docDriver?.getNamespaces().then((r) => {
setNamespaceList(r.map(mapNamespace));
const firstNamespaceId = r[0].id;
setSelectedNamespace(firstNamespaceId);
docDriver.setCurrentNamespace(firstNamespaceId);
});
if (docDriver) {
docDriver.getNamespaces().then((r) => {
setNamespaceList(r.map(mapNamespace));
const firstNamespaceId = r[0].id;
setSelectedNamespace(firstNamespaceId);
docDriver.setCurrentNamespace(firstNamespaceId);
});
}
}, [docDriver]);

return (
Expand All @@ -88,7 +106,11 @@ export default function SavedDocTab() {
</div>
<Separator />
<div className="p-2">
<SavedDocNamespaceDocList namespaceId={selectedNamespace} />
<SavedDocNamespaceDocList
namespaceData={
namespaceList.find((n) => n.key === selectedNamespace)?.data
}
/>
</div>
</div>
);
Expand Down
31 changes: 25 additions & 6 deletions src/drivers/saved-doc/remote-saved-doc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
export default class RemoteSavedDocDriver implements SavedDocDriver {
protected databaseId: string;
protected currentNamespace: string = "";
protected cb: (() => void)[] = [];

constructor(databaseId: string) {
this.databaseId = databaseId;
Expand All @@ -40,24 +41,30 @@ export default class RemoteSavedDocDriver implements SavedDocDriver {
return removeDocNamespace(this.databaseId, id);
}

createDoc(
async createDoc(

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

View workflow job for this annotation

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

🕹️ Function is not covered

Warning! Not covered function
type: SavedDocType,
namespace: string,
data: SavedDocInput
): Promise<SavedDocData> {
return createSavedDoc(this.databaseId, namespace, type, data);
const r = await createSavedDoc(this.databaseId, namespace, type, data);

Check warning on line 49 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
this.triggerChange();

Check warning on line 50 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
return r;

Check warning on line 51 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
}

getDocs(namespaceId: string): Promise<SavedDocData[]> {
return getSavedDocList(this.databaseId, namespaceId);
}

updateDoc(id: string, data: SavedDocInput): Promise<SavedDocData> {
return updateSavedDoc(this.databaseId, id, data);
async updateDoc(id: string, data: SavedDocInput): Promise<SavedDocData> {

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

View workflow job for this annotation

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

🕹️ Function is not covered

Warning! Not covered function
const r = updateSavedDoc(this.databaseId, id, data);

Check warning on line 59 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
this.triggerChange();

Check warning on line 60 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
return r;

Check warning on line 61 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
}

removeDoc(id: string): Promise<void> {
return removeSavedDoc(this.databaseId, id);
async removeDoc(id: string): Promise<void> {

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

View workflow job for this annotation

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

🕹️ Function is not covered

Warning! Not covered function
const r = await removeSavedDoc(this.databaseId, id);

Check warning on line 65 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
this.triggerChange();

Check warning on line 66 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
return r;

Check warning on line 67 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
}

setCurrentNamespace(id: string) {
Expand All @@ -67,4 +74,16 @@ export default class RemoteSavedDocDriver implements SavedDocDriver {
getCurrentNamespace(): string {
return this.currentNamespace;
}

addChangeListener(cb: () => void): void {

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

View workflow job for this annotation

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

🕹️ Function is not covered

Warning! Not covered function
this.cb.push(cb);

Check warning on line 79 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
}

removeChangeListener(cb: () => void): void {

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

View workflow job for this annotation

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

🕹️ Function is not covered

Warning! Not covered function
this.cb = this.cb.filter((c) => c !== cb);

Check warning on line 83 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 83 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 83 in src/drivers/saved-doc/remote-saved-doc.ts

View workflow job for this annotation

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

🕹️ Function is not covered

Warning! Not covered function
}

protected triggerChange() {

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)

🕹️ Function is not covered

Warning! Not covered function
this.cb.forEach((c) => c());

Check warning on line 87 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 87 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 87 in src/drivers/saved-doc/remote-saved-doc.ts

View workflow job for this annotation

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

🕹️ Function is not covered

Warning! Not covered function
}
}
25 changes: 22 additions & 3 deletions src/drivers/saved-doc/saved-doc-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
SavedDocType,
} from "./saved-doc-driver";
import { getDatabaseWithAuth } from "@/lib/with-database-ops";
import { eq } from "drizzle-orm";
import { desc, eq } from "drizzle-orm";
import { dbDoc, dbDocNamespace } from "@/db/schema-doc";
import { generateId } from "lucia";
import { ApiError } from "@/lib/api-error";
Expand Down Expand Up @@ -163,10 +163,14 @@ export async function getSavedDocList(
databaseId: string,
namespaceId: string
): Promise<SavedDocData[]> {
const { db } = await getNamespaceWithAuth(databaseId, namespaceId);
const { db, namespaceData } = await getNamespaceWithAuth(
databaseId,
namespaceId

Check warning on line 168 in src/drivers/saved-doc/saved-doc-actions.ts

View workflow job for this annotation

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

🧾 Statement is not covered

Warning! Not covered statement
);

const docList = await db.query.dbDoc.findMany({
where: eq(dbDoc.namespaceId, namespaceId),
orderBy: desc(dbDoc.createdAt),
});

Check warning on line 174 in src/drivers/saved-doc/saved-doc-actions.ts

View workflow job for this annotation

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

🧾 Statement is not covered

Warning! Not covered statement

return docList.map((item) => {
Expand All @@ -177,6 +181,10 @@ export async function getSavedDocList(
id: item.id ?? "",
name: item.name ?? "",
type: (item.type ?? "sql") as SavedDocType,
namespace: {
id: namespaceData.id,
name: namespaceData.name ?? "",

Check warning on line 186 in src/drivers/saved-doc/saved-doc-actions.ts

View workflow job for this annotation

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

🌿 Branch is not covered

Warning! Not covered branch

Check warning on line 186 in src/drivers/saved-doc/saved-doc-actions.ts

View workflow job for this annotation

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

🌿 Branch is not covered

Warning! Not covered branch
},
};

Check warning on line 188 in src/drivers/saved-doc/saved-doc-actions.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 189 in src/drivers/saved-doc/saved-doc-actions.ts

View workflow job for this annotation

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

🧾 Statement is not covered

Warning! Not covered statement
}
Expand Down Expand Up @@ -214,6 +222,10 @@ export async function createSavedDoc(
updatedAt: now,
name: input.name,
type: type,
namespace: {
id: namespaceData.id,
name: namespaceData.name ?? "",

Check warning on line 227 in src/drivers/saved-doc/saved-doc-actions.ts

View workflow job for this annotation

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

🌿 Branch is not covered

Warning! Not covered branch

Check warning on line 227 in src/drivers/saved-doc/saved-doc-actions.ts

View workflow job for this annotation

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

🌿 Branch is not covered

Warning! Not covered branch
},
};

Check warning on line 229 in src/drivers/saved-doc/saved-doc-actions.ts

View workflow job for this annotation

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

🧾 Statement is not covered

Warning! Not covered statement
}

Expand All @@ -230,7 +242,10 @@ export async function updateSavedDoc(
docId: string,
input: SavedDocInput
): Promise<SavedDocData> {
const { db, docData } = await getDocWithAuth(databaseId, docId);
const { db, docData, namespaceData } = await getDocWithAuth(
databaseId,
docId

Check warning on line 247 in src/drivers/saved-doc/saved-doc-actions.ts

View workflow job for this annotation

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

🧾 Statement is not covered

Warning! Not covered statement
);
const now = Math.floor(Date.now() / 1000);

await db
Expand All @@ -249,5 +264,9 @@ export async function updateSavedDoc(
updatedAt: now,
name: input.name,
type: (docData.type ?? "sql") as SavedDocType,
namespace: {
id: namespaceData.id,
name: namespaceData.name ?? "",

Check warning on line 269 in src/drivers/saved-doc/saved-doc-actions.ts

View workflow job for this annotation

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

🌿 Branch is not covered

Warning! Not covered branch
},
};

Check warning on line 271 in src/drivers/saved-doc/saved-doc-actions.ts

View workflow job for this annotation

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

🧾 Statement is not covered

Warning! Not covered statement
}
3 changes: 3 additions & 0 deletions src/drivers/saved-doc/saved-doc-driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export interface SavedDocInput {
export interface SavedDocData extends SavedDocInput {
id: string;
type: SavedDocType;
namespace: { id: string; name: string };
createdAt: number;
updatedAt: number;
}
Expand All @@ -41,6 +42,8 @@ export abstract class SavedDocDriver {
abstract removeDoc(id: string): Promise<void>;

// This is helper to make code easier
abstract addChangeListener(cb: () => void): void;
abstract removeChangeListener(cb: () => void): void;
abstract setCurrentNamespace(id: string): void;
abstract getCurrentNamespace(): string;
}
17 changes: 12 additions & 5 deletions src/messages/open-tab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ interface OpenQueryTab {
type: "query";
name?: string;
saved?: {
namespaceName?: string;
key: string;
sql: string;
};
Expand Down Expand Up @@ -91,14 +92,19 @@ function generateTitle(tab: OpenTabsProps) {
return tab.name ?? "";
}

function generateComponent(tab: OpenTabsProps) {
function generateComponent(tab: OpenTabsProps, title: string) {
if (tab.type === "query") {
if (tab.saved) {
return (
<QueryWindow initialName={tab.name ?? ""} initialCode={tab.saved.sql} />
<QueryWindow
initialName={title}
initialCode={tab.saved.sql}
initialSavedKey={tab.saved.key}
initialNamespace={tab.saved.namespaceName}
/>
);
}
return <QueryWindow initialName={tab.name ?? ""} />;
return <QueryWindow initialName={title} />;
}
if (tab.type === "table")
return <TableDataWindow tableName={tab.tableName} />;
Expand Down Expand Up @@ -126,14 +132,15 @@ export function receiveOpenTabMessage({
return prev;
}
setSelectedTabIndex(prev.length);
const title = generateTitle(newTab);

return [
...prev,
{
icon: generateIconFromTab(newTab),
title: generateTitle(newTab),
title,
key,
component: generateComponent(newTab),
component: generateComponent(newTab, title),
},
];
});
Expand Down

0 comments on commit e40deb7

Please sign in to comment.