From e23b38875e08cba400db21208a3ce0fe4bd86586 Mon Sep 17 00:00:00 2001 From: Ishaan Dey Date: Wed, 1 May 2024 02:22:02 -0400 Subject: [PATCH] unsharing logic --- backend/database/src/index.ts | 59 ++++++++++++------- frontend/components/editor/navbar/share.tsx | 58 ++++++++---------- .../components/editor/navbar/sharedUser.tsx | 44 ++++++++++++++ frontend/lib/actions.ts | 12 ++++ 4 files changed, 118 insertions(+), 55 deletions(-) create mode 100644 frontend/components/editor/navbar/sharedUser.tsx diff --git a/backend/database/src/index.ts b/backend/database/src/index.ts index 4e1610f..d6f02a8 100644 --- a/backend/database/src/index.ts +++ b/backend/database/src/index.ts @@ -5,7 +5,7 @@ import { ZodError, z } from "zod"; import { user, sandbox, usersToSandboxes } from "./schema"; import * as schema from "./schema"; -import { eq } from "drizzle-orm"; +import { and, eq } from "drizzle-orm"; export interface Env { DB: D1Database; @@ -91,33 +91,48 @@ export default { console.log(method); return methodNotAllowed; } - } else if (path === "/api/sandbox/share" && method === "POST") { - const shareSchema = z.object({ - sandboxId: z.string(), - email: z.string(), - }); + } else if (path === "/api/sandbox/share") { + if (method === "POST") { + const shareSchema = z.object({ + sandboxId: z.string(), + email: z.string(), + }); - const body = await request.json(); - const { sandboxId, email } = shareSchema.parse(body); + const body = await request.json(); + const { sandboxId, email } = shareSchema.parse(body); - const user = await db.query.user.findFirst({ - where: (user, { eq }) => eq(user.email, email), - with: { - usersToSandboxes: true, - }, - }); + const user = await db.query.user.findFirst({ + where: (user, { eq }) => eq(user.email, email), + with: { + usersToSandboxes: true, + }, + }); - if (!user) { - return new Response("No user associated with email.", { status: 400 }); - } + if (!user) { + return new Response("No user associated with email.", { status: 400 }); + } - if (user.usersToSandboxes.find((uts) => uts.sandboxId === sandboxId)) { - return new Response("User already has access.", { status: 400 }); - } + if (user.usersToSandboxes.find((uts) => uts.sandboxId === sandboxId)) { + return new Response("User already has access.", { status: 400 }); + } - await db.insert(usersToSandboxes).values({ userId: user.id, sandboxId }).get(); + await db.insert(usersToSandboxes).values({ userId: user.id, sandboxId }).get(); - return success; + return success; + } else if (method === "DELETE") { + const deleteShareSchema = z.object({ + sandboxId: z.string(), + userId: z.string(), + }); + + const body = await request.json(); + const { sandboxId, userId } = deleteShareSchema.parse(body); + console.log("DELETE", sandboxId, userId); + + await db.delete(usersToSandboxes).where(and(eq(usersToSandboxes.userId, userId), eq(usersToSandboxes.sandboxId, sandboxId))); + + return success; + } else return methodNotAllowed; } else if (path === "/api/user") { if (method === "GET") { const params = url.searchParams; diff --git a/frontend/components/editor/navbar/share.tsx b/frontend/components/editor/navbar/share.tsx index 67ebf3d..d2ad727 100644 --- a/frontend/components/editor/navbar/share.tsx +++ b/frontend/components/editor/navbar/share.tsx @@ -3,10 +3,8 @@ import { Dialog, DialogContent, - DialogDescription, DialogHeader, DialogTitle, - DialogTrigger, } from "@/components/ui/dialog" import { z } from "zod" import { zodResolver } from "@hookform/resolvers/zod" @@ -15,27 +13,19 @@ import { useForm } from "react-hook-form" import { Form, FormControl, - FormDescription, FormField, FormItem, - FormLabel, FormMessage, } from "@/components/ui/form" import { Input } from "@/components/ui/input" -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select" import { Loader2, UserPlus, X } from "lucide-react" -import { useEffect, useState, useTransition } from "react" -import { Sandbox, User } from "@/lib/types" +import { useState } from "react" +import { Sandbox } from "@/lib/types" import { Button } from "@/components/ui/button" -import Avatar from "@/components/ui/avatar" import { shareSandbox } from "@/lib/actions" import { toast } from "sonner" +import SharedUser from "./sharedUser" +import { DialogDescription } from "@radix-ui/react-dialog" const formSchema = z.object({ email: z.string().email(), @@ -79,9 +69,15 @@ export default function ShareSandboxModal({ return ( -
+
0 ? "pb-3" : null} space-y-6`}> Share Sandbox + {data.visibility === "private" ? ( + + This sandbox is private. Making it public will allow shared + users to view and collaborate. + + ) : null}
@@ -115,25 +111,21 @@ export default function ShareSandboxModal({
-
-
- - Manage Access - -
- {shared.map((user) => ( -
-
- - {user.name} -
- + {shared.length > 0 ? ( + <> +
+
+ + Manage Access + +
+ {shared.map((user) => ( + + ))}
- ))} -
-
+
+ + ) : null}
) diff --git a/frontend/components/editor/navbar/sharedUser.tsx b/frontend/components/editor/navbar/sharedUser.tsx new file mode 100644 index 0000000..6f3c54b --- /dev/null +++ b/frontend/components/editor/navbar/sharedUser.tsx @@ -0,0 +1,44 @@ +"use client" + +import Avatar from "@/components/ui/avatar" +import { Button } from "@/components/ui/button" +import { unshareSandbox } from "@/lib/actions" +import { Loader2, X } from "lucide-react" +import { useState } from "react" + +export default function SharedUser({ + user, + sandboxId, +}: { + user: { id: string; name: string } + sandboxId: string +}) { + const [loading, setLoading] = useState(false) + + async function handleUnshare(id: string) { + setLoading(true) + + await unshareSandbox(sandboxId, user.id) + } + + return ( +
+
+ + {user.name} +
+ +
+ ) +} diff --git a/frontend/lib/actions.ts b/frontend/lib/actions.ts index c87fd8a..e6495bf 100644 --- a/frontend/lib/actions.ts +++ b/frontend/lib/actions.ts @@ -60,3 +60,15 @@ export async function shareSandbox(sandboxId: string, email: string) { revalidatePath(`/code/${sandboxId}`) return { success: true, message: "Shared successfully." } } + +export async function unshareSandbox(sandboxId: string, userId: string) { + const res = await fetch("http://localhost:8787/api/sandbox/share", { + method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ sandboxId, userId }), + }) + + revalidatePath(`/code/${sandboxId}`) +}