diff options
| author | Dhravya <[email protected]> | 2024-07-01 20:32:50 -0500 |
|---|---|---|
| committer | Dhravya <[email protected]> | 2024-07-01 20:32:50 -0500 |
| commit | 46dfc3b4e7ac5958251401a670c1f4cff49706bb (patch) | |
| tree | f9baefd7dfd4cac46ef09cfc8349131e6235dfe1 /apps/web/app | |
| parent | spaces function (diff) | |
| download | supermemory-46dfc3b4e7ac5958251401a670c1f4cff49706bb.tar.xz supermemory-46dfc3b4e7ac5958251401a670c1f4cff49706bb.zip | |
shareable spaces
Diffstat (limited to 'apps/web/app')
| -rw-r--r-- | apps/web/app/(dash)/(memories)/content.tsx | 60 | ||||
| -rw-r--r-- | apps/web/app/(dash)/(memories)/space/[spaceid]/page.tsx | 9 | ||||
| -rw-r--r-- | apps/web/app/actions/doers.ts | 44 | ||||
| -rw-r--r-- | apps/web/app/actions/fetchers.ts | 27 |
4 files changed, 123 insertions, 17 deletions
diff --git a/apps/web/app/(dash)/(memories)/content.tsx b/apps/web/app/(dash)/(memories)/content.tsx index 26aed6a5..23bff46b 100644 --- a/apps/web/app/(dash)/(memories)/content.tsx +++ b/apps/web/app/(dash)/(memories)/content.tsx @@ -30,17 +30,20 @@ import { DropdownMenuTrigger, } from "@repo/ui/shadcn/dropdown-menu"; import { Button } from "@repo/ui/shadcn/button"; -import { deleteItem, moveItem } from "@/app/actions/doers"; +import { addUserToSpace, deleteItem, moveItem } from "@/app/actions/doers"; import { toast } from "sonner"; +import { Input } from "@repo/ui/shadcn/input"; export function MemoriesPage({ memoriesAndSpaces, title = "Your Memories", currentSpace, + usersWithAccess, }: { memoriesAndSpaces: { memories: Content[]; spaces: StoredSpace[] }; title?: string; currentSpace?: StoredSpace; + usersWithAccess?: string[]; }) { const [filter, setFilter] = useState("All"); @@ -70,7 +73,6 @@ export function MemoriesPage({ .filter((item) => { if (filter === "All") return true; if (filter === "Spaces" && item.item === "space") { - console.log(item); return true; } if (filter === "Pages") @@ -105,12 +107,55 @@ export function MemoriesPage({ {title} </h2> {currentSpace && ( - <div className="flex gap-4 items-center"> - Space - <div className="flex items-center gap-2 bg-secondary p-2 rounded-xl"> - <Image src={MemoriesIcon} alt="Spaces icon" className="w-3 h-3" /> - <span className="text-[#fff]">{currentSpace.name}</span> + <div className="flex flex-col gap-2"> + <div className="flex gap-4 items-center"> + Space + <div className="flex items-center gap-2 bg-secondary p-2 rounded-xl"> + <Image src={MemoriesIcon} alt="Spaces icon" className="w-3 h-3" /> + <span className="text-[#fff]">{currentSpace.name}</span> + </div> </div> + + {usersWithAccess && usersWithAccess.length > 0 && ( + <div className="flex gap-4 items-center"> + Users with access + <div className="flex gap-2"> + {usersWithAccess.map((user) => ( + <div className="flex items-center gap-2 bg-secondary p-2 rounded-xl"> + <Image + src={UrlIcon} + alt="Spaces icon" + className="w-3 h-3" + /> + <span className="text-[#fff]">{user}</span> + </div> + ))} + </div> + </div> + )} + + <form + action={async (e: FormData) => { + const email = e.get("email")?.toString(); + + if (!email) { + toast.error("Please enter an email"); + return; + } + + const resp = await addUserToSpace(email, currentSpace.id); + + if (resp.success) { + toast.success("User added to space"); + } else { + toast.error("Failed to add user to space"); + } + }} + className="flex gap-2 max-w-xl mt-2" + > + <Input name="email" placeholder="Add user by email" /> + <Button variant="secondary">Add</Button> + </form> </div> )} @@ -268,7 +313,6 @@ function LinkComponent({ onClick={async () => { toast.info("Adding to space..."); - console.log(id, space.id); const response = await moveItem(id, [space.id]); if (response.success) { diff --git a/apps/web/app/(dash)/(memories)/space/[spaceid]/page.tsx b/apps/web/app/(dash)/(memories)/space/[spaceid]/page.tsx index 723fb29e..759519cb 100644 --- a/apps/web/app/(dash)/(memories)/space/[spaceid]/page.tsx +++ b/apps/web/app/(dash)/(memories)/space/[spaceid]/page.tsx @@ -1,15 +1,24 @@ import { getMemoriesInsideSpace } from "@/app/actions/fetchers"; import { redirect } from "next/navigation"; import MemoriesPage from "../../content"; +import { db } from "@/server/db"; +import { and, eq } from "drizzle-orm"; +import { spacesAccess } from "@/server/db/schema"; async function Page({ params: { spaceid } }: { params: { spaceid: number } }) { const { success, data } = await getMemoriesInsideSpace(spaceid); if (!success ?? !data) return redirect("/home"); + + const hasAccess = await db.query.spacesAccess.findMany({ + where: and(eq(spacesAccess.spaceId, spaceid)), + }); + return ( <MemoriesPage memoriesAndSpaces={{ memories: data.memories, spaces: [] }} title={data.spaces[0]?.name} currentSpace={data.spaces[0]} + usersWithAccess={hasAccess.map((x) => x.userEmail) ?? []} /> ); } diff --git a/apps/web/app/actions/doers.ts b/apps/web/app/actions/doers.ts index 90103092..82263f6e 100644 --- a/apps/web/app/actions/doers.ts +++ b/apps/web/app/actions/doers.ts @@ -9,6 +9,7 @@ import { contentToSpace, sessions, space, + spacesAccess, storedContent, users, } from "../../server/db/schema"; @@ -95,6 +96,47 @@ export const limit = async ( return items <= remainingLimit; }; +export const addUserToSpace = async (userEmail: string, spaceId: number) => { + const data = await auth(); + + if (!data || !data.user || !data.user.id) { + redirect("/signin"); + return { error: "Not authenticated", success: false }; + } + + // We need to make sure that the user owns the space + const spaceData = await db + .select() + .from(space) + .where(and(eq(space.id, spaceId), eq(space.user, data.user.id))) + .all(); + + if (spaceData.length === 0) { + return { + success: false, + error: "You do not own this space", + }; + } + + try { + await db.insert(spacesAccess).values({ + spaceId: spaceId, + userEmail: userEmail, + }); + + revalidatePath("/space/" + spaceId); + + return { + success: true, + }; + } catch (e) { + return { + success: false, + error: (e as Error).message, + }; + } +}; + const getTweetData = async (tweetID: string) => { const url = `https://cdn.syndication.twimg.com/tweet-result?id=${tweetID}&lang=en&features=tfw_timeline_list%3A%3Btfw_follower_count_sunset%3Atrue%3Btfw_tweet_edit_backend%3Aon%3Btfw_refsrc_session%3Aon%3Btfw_fosnr_soft_interventions_enabled%3Aon%3Btfw_show_birdwatch_pivots_enabled%3Aon%3Btfw_show_business_verified_badge%3Aon%3Btfw_duplicate_scribes_to_settings%3Aon%3Btfw_use_profile_image_shape_enabled%3Aon%3Btfw_show_blue_verified_badge%3Aon%3Btfw_legacy_timeline_sunset%3Atrue%3Btfw_show_gov_verified_badge%3Aon%3Btfw_show_business_affiliate_badge%3Aon%3Btfw_tweet_edit_frontend%3Aon&token=4c2mmul6mnh`; @@ -191,8 +233,6 @@ export const createMemory = async (input: { storeToSpaces = []; } - console.log("SAVING URL: ", metadata.baseUrl); - const vectorSaveResponse = await fetch( `${process.env.BACKEND_BASE_URL}/api/add`, { diff --git a/apps/web/app/actions/fetchers.ts b/apps/web/app/actions/fetchers.ts index c33f90d1..1541e4ee 100644 --- a/apps/web/app/actions/fetchers.ts +++ b/apps/web/app/actions/fetchers.ts @@ -1,6 +1,6 @@ "use server"; -import { and, asc, eq, inArray, not, sql } from "drizzle-orm"; +import { and, asc, eq, inArray, not, or, sql } from "drizzle-orm"; import { db } from "../../server/db"; import { canvas, @@ -10,6 +10,7 @@ import { Content, contentToSpace, space, + spacesAccess, storedContent, StoredSpace, users, @@ -90,12 +91,24 @@ export const getMemoriesInsideSpace = async ( .select() .from(storedContent) .where( - inArray( - storedContent.id, - db - .select({ contentId: contentToSpace.contentId }) - .from(contentToSpace) - .where(eq(contentToSpace.spaceId, spaceId)), + and( + inArray( + storedContent.id, + db + .select({ contentId: contentToSpace.contentId }) + .from(contentToSpace) + .where(eq(contentToSpace.spaceId, spaceId)), + ), + or( + eq(storedContent.userId, data.user.id!), + eq( + db + .select({ userId: spacesAccess.userEmail }) + .from(spacesAccess) + .where(eq(spacesAccess.spaceId, spaceId)), + data.user.email, + ), + ), ), ) .execute(); |