Skip to content

Commit

Permalink
Merge pull request #40 from AElfProject/feature/share
Browse files Browse the repository at this point in the history
Feature/share
  • Loading branch information
yongenaelf authored Sep 10, 2024
2 parents 5651a98 + 173dee8 commit 4783729
Show file tree
Hide file tree
Showing 13 changed files with 281 additions and 7 deletions.
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

0 comments on commit 4783729

Please sign in to comment.