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

Feature/share #40

Merged
merged 2 commits into from
Sep 10, 2024
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
37 changes: 37 additions & 0 deletions app/api/get-share/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { FileContent } from "@/data/db";
import { getBuildServerBaseUrl } from "@/lib/env";
import { unzipSync, strFromU8 } from "fflate";

export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const id = searchParams.get("id");

if (!id) {
throw new Error("no id");
}

const res = await fetch(
`${getBuildServerBaseUrl()}/playground/share/get/${id}`
);

const data = await res.arrayBuffer();

try {
const unzipped = unzipSync(Buffer.from(data));

let files: FileContent[] = [];

Object.entries(unzipped).forEach(([k, v]) => {
files.push({
path: k,
contents: strFromU8(v)
})
})

return Response.json(files);
} catch (err) {
if (err instanceof Error) {
return Response.json({ message: err.message }, { status: 500 });
}
}
}
37 changes: 37 additions & 0 deletions app/api/share/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { FileContent } from "@/data/db";
import { getBuildServerBaseUrl } from "@/lib/env";
import { fileContentToZip } from "@/lib/file-content-to-zip";
import { type NextRequest } from "next/server";
import { v4 as uuidv4 } from "uuid";

export async function POST(request: NextRequest) {
const { files } = (await request.json()) as { files: FileContent[] };

const zippedData = fileContentToZip(files);

const formData = new FormData();
const filePath = uuidv4() + ".zip";
formData.append(
"file",
new File([zippedData], filePath, { type: "application/zip" }),
filePath
);

const requestInit: RequestInit = {
method: "POST",
body: formData,
redirect: "follow",
};

const response = await fetch(
`${getBuildServerBaseUrl()}/playground/share/create`,
requestInit
);

if (!response.ok) {
const { message } = await response.json();
return Response.json({ error: message }, { status: response.status });
}

return Response.json(await response.json());
}
3 changes: 1 addition & 2 deletions app/api/test/route.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { FileContent } from "@/data/db";
import { getBuildServerBaseUrl, getSolangBuildServerBaseUrl } from "@/lib/env";
import { getBuildServerBaseUrl } from "@/lib/env";
import { fileContentToZip } from "@/lib/file-content-to-zip";
import { type NextRequest } from "next/server";
import { v4 as uuidv4 } from "uuid";
import { strFromU8, strToU8 } from "fflate";

export async function POST(request: NextRequest) {
const { files } = (await request.json()) as { files: FileContent[] };
Expand Down
2 changes: 2 additions & 0 deletions app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import clsx from "clsx";
import { GoogleAnalytics } from "@next/third-parties/google";
import { getGoogleAnalyticsTag } from "@/lib/env";
import Providers from "@/components/providers";
import { Toaster } from "@/components/ui/toaster"

const font = Poppins({
weight: ["100", "200", "300", "400", "500", "600", "700", "800", "900"],
Expand All @@ -30,6 +31,7 @@ export default function RootLayout({ children }: PropsWithChildren) {
<main className="h-[calc(100vh-66px)] overflow-auto">
<Suspense>{children}</Suspense>
</main>
<Toaster />
</Providers>
</body>
{gaId ? <GoogleAnalytics gaId={gaId} /> : null}
Expand Down
39 changes: 39 additions & 0 deletions app/share/[id]/_sharepagecomponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
"use client";

import { useShare } from "@/data/client";
import { db } from "@/data/db";
import { useRouter } from "next/navigation";
import { useEffect } from "react";

export function SharePageComponent({ id }: { id: string }) {
const { data, isLoading, error } = useShare(id);
const router = useRouter();

useEffect(() => {
async function importWorkspace() {
if (!data) return;

const existing = await db.workspaces.get(id);
if (existing) {
router.push(`/workspace/${id}`);
} else {
await db.workspaces.add({name: id, template: id, dll: ''});

await db.files.bulkAdd(
data.map(({ path, contents }) => ({
path: `/workspace/${id}/${encodeURIComponent(path)}`,
contents,
}))
);
router.push(`/workspace/${id}`);
}
}

importWorkspace();
}, [id, data])

if (isLoading) return <p>Loading...</p>;
else if (error) return <p>Error: {String(error)}</p>;

return <p>Loading...</p>;
}
10 changes: 10 additions & 0 deletions app/share/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { SharePageComponent } from "./_sharepagecomponent";



export default function Page({ params: {id} }: { params: { id: string } }) {

return (
<SharePageComponent id={id} />
);
}
12 changes: 11 additions & 1 deletion components/build-deploy-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { useCliCommands } from "./workspace/use-cli-commands";
import useSWR, { mutate } from "swr";
import { db } from "@/data/db";
import { useWorkspaceId } from "./workspace/use-workspace-id";
import { Download, Rocket, ShieldCheck, Wrench, TestTube2 } from "lucide-react";
import { Download, Rocket, ShieldCheck, Wrench, TestTube2, Link2 } from "lucide-react";
import UploadModal from "./workspace/upload-modal";
import { Tooltip } from "./tooltip";

Expand Down Expand Up @@ -100,6 +100,16 @@ export function BuildDeployPanel() {
},
icon: Download,
},
{
disabled: false,
title: "Share",
onClick: async () => {
try {
await commands.share();
} catch (err) {}
},
icon: Link2,
},
];

return (
Expand Down
2 changes: 1 addition & 1 deletion components/ui/toaster.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use client"

import { useToast } from "@/components/ui/use-toast"
import {
Toast,
ToastClose,
Expand All @@ -8,7 +9,6 @@ import {
ToastTitle,
ToastViewport,
} from "@/components/ui/toast"
import { useToast } from "@/components/ui/use-toast"

export function Toaster() {
const { toasts } = useToast()
Expand Down
45 changes: 45 additions & 0 deletions components/workspace/share-link.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
"use client";
import { Copy, CopyCheck } from "lucide-react";
import Link from "next/link";
import { useMemo, useState } from "react";
import { CopyToClipboard } from "react-copy-to-clipboard";
import { useToast } from "@/components/ui/use-toast";

export function ShareLink({ id }: { id: string }) {
const { toast } = useToast();
const origin =
typeof window !== "undefined" && window.location.origin
? window.location.origin
: "";
const [copied, setCopied] = useState(false);

const url = useMemo(() => {
return `${origin}/share/${id}`;
}, [id, origin]);

return (
<div className="flex">
<Link href={url} className="mr-4">
{url}
</Link>
<CopyToClipboard
text={url}
onCopy={() => {
setCopied(true);

toast({
title: "Link Copied",
description: "Share link has been copied to your clipboard.",

});
}}
>
{copied ? (
<CopyCheck className="w-4 h-4 cursor-pointer" />
) : (
<Copy className="w-4 h-4 cursor-pointer" />
)}
</CopyToClipboard>
</div>
);
}
45 changes: 44 additions & 1 deletion components/workspace/use-cli-commands.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { fileContentToZip } from "@/lib/file-content-to-zip";
import { saveAs } from "file-saver";
import { useSetSearchParams } from "@/lib/set-search-params";
import { FormatErrors } from "./format-errors";
import { ShareLink } from "./share-link";

export function useCliCommands() {
const terminalContext = useContext(TerminalContext);
Expand All @@ -35,6 +36,7 @@ export function useCliCommands() {
<li className="ml-8">test - tests the current workspace</li>
<li className="ml-8">deploy - deploys the built smart contract</li>
<li className="ml-8">export - exports the current workspace</li>
<li className="ml-8">share - generates a share link for the current workspace</li>
<li className="ml-8">
check txID - checks the result of transaction
</li>
Expand Down Expand Up @@ -260,6 +262,46 @@ export function useCliCommands() {
);
saveAs(file);
},
share: async () => {
if (typeof id !== "string") throw new Error("id is not string");
const start = `${pathname}/`;
terminalContext.setBufferedContent(
<>
<p>Loading files...</p>
</>
);
const files = (
await db.files.filter((file) => file.path.startsWith(start)).toArray()
).map((file) => ({
path: decodeURIComponent(file.path.replace(start, "")),
contents: file.contents,
}));

terminalContext.setBufferedContent(
<>
<p>Loaded files: {files.map((i) => i.path).join(", ")}</p>
<p>
<Loader2 className="h-4 w-4 animate-spin inline" /> Generating share link...
</p>
</>
);

try {
const res = await fetch(`/api/share`, {
method: "POST",
body: JSON.stringify({ files }),
});
const { id } = await res.json();

terminalContext.setBufferedContent(
<ShareLink id={id} />
);
} catch (err) {
if (err instanceof Error)
terminalContext.setBufferedContent(<>{err.message}</>);
return;
}
},
};

return commands;
Expand Down Expand Up @@ -395,4 +437,5 @@ function AuditReportResult({ codeHash }: { codeHash: string }) {
</table>
</>
);
}
}

14 changes: 12 additions & 2 deletions data/client.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";

import useSWR from "swr";
import { db } from "./db";
import { db, FileContent } from "./db";
import { ProposalInfo } from "./proposal-info-types";
import AElf from "aelf-sdk";
import { Transactions } from "./transactions-types";
Expand Down Expand Up @@ -141,4 +141,14 @@ export function useTutorialList() {

return data;
});
}
}

export function useShare(id: string) {
return useSWR<FileContent[]>(`get-share-${id}`, async () => {
const res = await fetch(`/api/get-share?id=${id}`);

const data = await res.json();

return data;
});
}
Loading