Skip to content

Commit

Permalink
Add support for sharing of schemas with a link
Browse files Browse the repository at this point in the history
  • Loading branch information
albingroen committed Jun 21, 2023
1 parent 2dfdaa5 commit b1fad5b
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 28 deletions.
15 changes: 15 additions & 0 deletions components/Models.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ import {
CubeIcon,
EllipsisVerticalIcon,
EyeIcon,
LinkIcon,
ListBulletIcon,
PlusIcon,
ShareIcon,
TrashIcon,
} from "@heroicons/react/24/outline";
import { BoltIcon, CubeIcon as CubeIconSolid } from "@heroicons/react/24/solid";
Expand All @@ -44,6 +46,7 @@ import {
Droppable,
DropResult,
} from "react-beautiful-dnd";
import { copyToClipboard } from "../lib/utils";

export default function Models() {
const { schema, schemas, setSchema, setSchemas } = useSchemaContext();
Expand Down Expand Up @@ -108,6 +111,13 @@ export default function Models() {
push("/");
};

const handleShareSchema = () => {
copyToClipboard(
`${location.origin}/?importSchema=${JSON.stringify(schema)}`,
"link"
);
};

const filteredCommandPaletteItems = filterItems(
[
{
Expand Down Expand Up @@ -332,6 +342,11 @@ export default function Models() {

<Dropdown
items={[
{
label: "Share schema",
icon: LinkIcon,
onClick: handleShareSchema,
},
{
label: "Delete schema",
icon: TrashIcon,
Expand Down
8 changes: 2 additions & 6 deletions components/Schema.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { ClipboardIcon } from "@heroicons/react/24/solid";
import { SchemaContext } from "../lib/context";
import { apiUrl } from "../lib/config";
import { useContext, useEffect, useState } from "react";
import { copyToClipboard } from "../lib/utils";

type SchemaProps = {
onCancel: () => void;
Expand Down Expand Up @@ -38,12 +39,7 @@ const Schema = ({ onCancel }: SchemaProps) => {
<Button
icon={ClipboardIcon}
onClick={() => {
try {
navigator.clipboard.writeText(result);
toast.success("Copied schema to clipboard");
} catch {
toast.error("Failed to copy to clipboard");
}
copyToClipboard(result, "schema");
}}
>
Copy to clipboard
Expand Down
4 changes: 2 additions & 2 deletions lib/context.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { createContext, useContext } from "react";
import { Dispatch, SetStateAction, createContext, useContext } from "react";
import { Schema } from "./types";

export const SchemaContext = createContext<{
schema: Schema;
schemas: Schema[];
setSchema: (schema: Schema) => void;
setSchemas: (schemas: Schema[]) => void;
setSchemas: Dispatch<SetStateAction<any[]>>;
}>({
schema: {} as Schema,
schemas: [],
Expand Down
10 changes: 10 additions & 0 deletions lib/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import toast from "react-hot-toast";

export async function copyToClipboard(content: string, what: string) {
try {
await navigator.clipboard.writeText(content);
toast.success(`Copied ${what} to clipboard`);
} catch {
toast.error(`Failed to copy ${what} to clipboard`);
}
}
4 changes: 2 additions & 2 deletions pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ function MyApp({ Component, pageProps }: AppProps) {
}, []);

useEffect(() => {
if (window && schemas.length) {
localStorage.setItem("schemas", JSON.stringify(schemas));
if (window) {
localStorage.setItem("schemas", JSON.stringify(schemas ?? []));
}
}, [schemas]);

Expand Down
66 changes: 49 additions & 17 deletions pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,62 @@
import Schemas from "../components/Schemas";
import toast from "react-hot-toast";
import { Schema } from "../lib/types";
import { useEffect } from "react";
import { useCallback, useEffect } from "react";
import { useRouter } from "next/dist/client/router";
import { useSchemaContext } from "../lib/context";

const Home = () => {
const { schemas, setSchemas } = useSchemaContext();
const { setSchemas } = useSchemaContext();
const router = useRouter();

useEffect(() => {
const lcSchema = localStorage.getItem("schema");
const schema = lcSchema && JSON.parse(lcSchema);

if (schema && !schemas?.length) {
const newSchema: Schema = {
database: "postgresql",
models: schema.models,
enums: schema.enums,
name: "New schema",
};
setSchemas([...schemas, newSchema]);
localStorage.removeItem("schema");
router.push(`/schemas/${newSchema.name}`);
// URL state
const importSchema = router.query.importSchema as string;

// Side-effects
const importSharedSchema = useCallback(() => {
if (importSchema) {
try {
const parsedImportSchema = JSON.parse(
decodeURIComponent(importSchema)
) as Schema;

if (!parsedImportSchema?.name) {
toast.error("Failed to import schema");

router.push("/");

return;
}

new Promise((res) => {
setSchemas((schemas = []) => {
if (schemas.some(({ name }) => name === parsedImportSchema.name)) {
toast.error(
`You already have a schema called ${parsedImportSchema.name}`
);

return schemas;
}

res(true);

return [...schemas, parsedImportSchema];
});
}).then(() => {
router.push(`/schemas/${parsedImportSchema.name}`);
});
} catch {
toast.error("Failed to import schema");
router.push("/");
}
}

// eslint-disable-next-line react-hooks/exhaustive-deps
}, [schemas]);
}, [importSchema]);

useEffect(() => {
importSharedSchema();
}, [importSharedSchema]);

return <Schemas />;
};
Expand Down
1 change: 0 additions & 1 deletion styles/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@
opacity: 100;
}
}

}

.model-fields {
Expand Down

1 comment on commit b1fad5b

@vercel
Copy link

@vercel vercel bot commented on b1fad5b Jun 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.