Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Dolt database version control support #229

Merged
merged 2 commits into from
Jan 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions src/app/(theme)/embed/dolt/page-client.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
"use client";
import { Studio } from "@/components/gui/studio";
import { IframeDoltDriver } from "@/drivers/iframe-driver";
import { useSearchParams } from "next/navigation";
import { useEffect, useMemo } from "react";

export default function EmbedPageClient() {
const searchParams = useSearchParams();
const driver = useMemo(() => new IframeDoltDriver(), []);

useEffect(() => {
return driver.listen();
}, [driver]);

return (
<Studio
driver={driver}
name={searchParams.get("name") || "Unnamed Connection"}
color={searchParams.get("color") || "gray"}
/>
);
}
37 changes: 37 additions & 0 deletions src/app/(theme)/embed/dolt/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import ThemeLayout from "../../theme_layout";
import EmbedPageClient from "./page-client";

export default async function EmbedPage(props: {
searchParams: {
theme?: string;
disableThemeToggle?: string;
[key: string]: any;
};
}) {
let overrideTheme: "dark" | "light" | undefined = undefined;
const disableToggle = props.searchParams.disableThemeToggle === "1";

if (props.searchParams.theme) {
overrideTheme = props.searchParams.theme === "dark" ? "dark" : "light";
}

const overrideThemeVariables: Record<string, string> = {};

for (const key in props.searchParams) {
if (!key.startsWith("themeVariables[")) {
continue;
}

overrideThemeVariables[key.slice(15, -1)] = props.searchParams[key];
}

return (
<ThemeLayout
overrideTheme={overrideTheme}
disableToggle={disableToggle}
overrideThemeVariables={overrideThemeVariables}
>
<EmbedPageClient />
</ThemeLayout>
);
}
27 changes: 15 additions & 12 deletions src/components/common-dialog/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "../ui/dialog";
import { Button } from "../ui/button";
import { Icon } from "@phosphor-icons/react";
Expand Down Expand Up @@ -70,20 +71,22 @@ export function CommonDialogProvider({ children }: PropsWithChildren) {
<DialogHeader
className={dialogOption?.destructive ? "text-red-500" : ""}
>
{dialogOption.title}
<DialogTitle>{dialogOption.title}</DialogTitle>
</DialogHeader>
<DialogDescription className="flex flex-col gap-2">
{errorMessage && (
<div className="text-sm text-red-500 font-mono flex gap-4 items-end">
<p>{errorMessage}</p>
</div>
)}

<div>{dialogOption.content}</div>
{dialogOption.previewCode && (
<CodePreview code={dialogOption.previewCode} />
)}
</DialogDescription>
{errorMessage && (
<div className="text-sm text-red-500 font-mono flex gap-4 items-end">
<p>{errorMessage}</p>
</div>
)}

<div>{dialogOption.content}</div>
{dialogOption.previewCode && (
<CodePreview code={dialogOption.previewCode} />
)}

<DialogDescription className="flex flex-col gap-2"></DialogDescription>

<DialogFooter>
{dialogOption.actions?.map((action) => (
<Button
Expand Down
34 changes: 23 additions & 11 deletions src/components/gui/database-gui.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import { useDatabaseDriver } from "@/context/driver-provider";
import SavedDocTab from "./sidebar/saved-doc-tab";
import { useSchema } from "@/context/schema-provider";
import { Binoculars, GearSix, Table } from "@phosphor-icons/react";
import DoltSidebar from "./database-specified/dolt/dolt-sidebar";
import { DoltIcon } from "../icons/outerbase-icon";

export default function DatabaseGui() {
const DEFAULT_WIDTH = 300;
Expand All @@ -30,6 +32,8 @@ export default function DatabaseGui() {
}, []);

const { databaseDriver, docDriver } = useDatabaseDriver();
const databaseDriverDialect = databaseDriver.getFlags().dialect;

const [selectedTabIndex, setSelectedTabIndex] = useState(0);
const { currentSchemaName } = useSchema();
const [tabs, setTabs] = useState<WindowTabItemProps[]>(() => [
Expand Down Expand Up @@ -91,20 +95,28 @@ export default function DatabaseGui() {
},
docDriver
? {
key: "saved",
name: "Queries",
content: <SavedDocTab />,
icon: <Binoculars weight="light" size={24} />,
}
key: "saved",
name: "Queries",
content: <SavedDocTab />,
icon: <Binoculars weight="light" size={24} />,
}
: undefined,
{
key: "tools",
name: "Tools",
content: <ToolSidebar />,
icon: <GearSix weight="light" size={24} />,
},
databaseDriverDialect
? {
key: "dolt",
name: "Dolt",
content: <DoltSidebar />,
icon: <DoltIcon className="w-7 h-7 text-green-500" />,
}
: undefined,
].filter(Boolean) as SidebarTabItem[];
}, [docDriver]);
}, [docDriver, databaseDriverDialect]);

const tabSideMenu = useMemo(() => {
return [
Expand All @@ -116,11 +128,11 @@ export default function DatabaseGui() {
},
databaseDriver.getFlags().supportCreateUpdateTable
? {
text: "New Table",
onClick: () => {
openTab({ type: "schema", schemaName: currentSchemaName });
},
}
text: "New Table",
onClick: () => {
openTab({ type: "schema", schemaName: currentSchemaName });
},
}
: undefined,
].filter(Boolean) as { text: string; onClick: () => void }[];
}, [currentSchemaName, databaseDriver]);
Expand Down
104 changes: 104 additions & 0 deletions src/components/gui/database-specified/dolt/dolt-create-branch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { useDatabaseDriver } from "@/context/driver-provider";
import { GitBranch, Loader } from "lucide-react";
import { useCallback, useMemo, useState } from "react";

export default function useDoltCreateBranchModal(refreshStatus: () => void) {
const { databaseDriver } = useDatabaseDriver();
const [open, setOpen] = useState(false);
const [loading, setLoading] = useState(false);
const [errorMessage, setErrorMessage] = useState<string | undefined>();
const [branchName, setBranchName] = useState<string>();
const [commitHash, setCommitHash] = useState<string | undefined>();

const onSaveClicked = useCallback(() => {
const command = commitHash
? `CALL DOLT_CHECKOUT('-b', ${databaseDriver.escapeValue(branchName)}, ${databaseDriver.escapeValue(commitHash)});`
: `CALL DOLT_CHECKOUT('-b', ${databaseDriver.escapeValue(branchName)});`;

setLoading(true);
databaseDriver
.query(command)
.then(() => {
refreshStatus();
setOpen(false);
})
.catch((e) => {
setLoading(false);
if (e instanceof Error) {
setErrorMessage(e.message);
} else {
setErrorMessage("An error occurred");
}
});
}, [databaseDriver, refreshStatus, commitHash, branchName]);

const modal = useMemo(() => {
if (!open) return null;

return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogContent>
<DialogHeader>
<DialogTitle>Create Branch</DialogTitle>
</DialogHeader>

<DialogDescription>
{commitHash ? (
<>
Creating new branch from{" "}
<span className="bg-muted rounded inline-flex font-mono px-2">
{commitHash}
</span>
</>
) : (
"Creating new branch"
)}
</DialogDescription>

{errorMessage && (
<div className="py-2 font-mono text-red-500">{errorMessage}</div>
)}

<div>
<Input
value={branchName}
placeholder="Branch Name"
onChange={(e) => setBranchName(e.currentTarget.value)}
/>
</div>

<DialogFooter>
<Button disabled={!branchName || loading} onClick={onSaveClicked}>
{loading ? (
<Loader className="mr-2 w-4 h-4 animate-spin" />
) : (
<GitBranch className="mr-2 w-4 h-4" />
)}
Create
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}, [open, branchName, commitHash, onSaveClicked, loading, errorMessage]);

const openModal = (commitHash?: string) => {
setCommitHash(commitHash);
setBranchName("");
setLoading(false);
setOpen(true);
setErrorMessage(undefined);
};

return { modal, openModal };
}
Loading
Loading