diff options
| author | Dhravya <[email protected]> | 2024-06-30 20:50:24 -0500 |
|---|---|---|
| committer | Dhravya <[email protected]> | 2024-06-30 20:50:24 -0500 |
| commit | ffd141ade4e6074ee486da7f74f31e3905807cb9 (patch) | |
| tree | 505d73b0a7c04cdec93d7f5be88c635642716c15 /apps/web/app/(canvas) | |
| parent | show updates in the extension (diff) | |
| parent | Merge pull request #93 from Dhravya/editor (diff) | |
| download | supermemory-ffd141ade4e6074ee486da7f74f31e3905807cb9.tar.xz supermemory-ffd141ade4e6074ee486da7f74f31e3905807cb9.zip | |
merge conflicts
Diffstat (limited to 'apps/web/app/(canvas)')
| -rw-r--r-- | apps/web/app/(canvas)/canvas/[id]/page.tsx | 140 | ||||
| -rw-r--r-- | apps/web/app/(canvas)/canvas/layout.tsx | 13 | ||||
| -rw-r--r-- | apps/web/app/(canvas)/canvas/page.tsx | 21 | ||||
| -rw-r--r-- | apps/web/app/(canvas)/canvas/search&create.tsx | 45 | ||||
| -rw-r--r-- | apps/web/app/(canvas)/canvas/thinkPad.tsx | 276 | ||||
| -rw-r--r-- | apps/web/app/(canvas)/canvas/thinkPads.tsx | 32 | ||||
| -rw-r--r-- | apps/web/app/(canvas)/canvasStyles.css | 32 | ||||
| -rw-r--r-- | apps/web/app/(canvas)/layout.tsx | 30 |
8 files changed, 434 insertions, 155 deletions
diff --git a/apps/web/app/(canvas)/canvas/[id]/page.tsx b/apps/web/app/(canvas)/canvas/[id]/page.tsx index 6efb6cf4..cad6143e 100644 --- a/apps/web/app/(canvas)/canvas/[id]/page.tsx +++ b/apps/web/app/(canvas)/canvas/[id]/page.tsx @@ -1,129 +1,17 @@ -"use client"; - -import { Canvas } from "@repo/ui/components/canvas/components/canvas"; -import React, { useState } from "react"; -import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels"; -import { SettingsIcon, DragIcon } from "@repo/ui/icons"; -import DraggableComponentsContainer from "@repo/ui/components/canvas/components/draggableComponent"; -import { AutocompleteIcon, blockIcon } from "@repo/ui/icons"; -import Image from "next/image"; -import { Switch } from "@repo/ui/shadcn/switch"; -import { Label } from "@repo/ui/shadcn/label"; -import { useRouter } from "next/router"; - -function page() { - const [fullScreen, setFullScreen] = useState(false); - const [visible, setVisible] = useState(true); - - const router = useRouter(); - router.push("/home"); - +import { userHasCanvas } from "@/app/actions/fetchers"; +import { + RectProvider, + ResizaleLayout, +} from "@/components/canvas/resizableLayout"; +import { redirect } from "next/navigation"; +export default async function page({ params }: any) { + const canvasExists = await userHasCanvas(params.id); + if (!canvasExists.success) { + redirect("/canvas"); + } return ( - <div - className={`h-screen w-full ${!fullScreen ? "px-4 py-6" : "bg-[#1F2428]"} transition-all`} - > - <div> - <PanelGroup - onLayout={(l) => { - l[0]! < 20 ? setVisible(false) : setVisible(true); - }} - className={` ${fullScreen ? "w-[calc(100vw-2rem)]" : "w-screen"} transition-all`} - direction="horizontal" - > - <Panel - onExpand={() => { - setTimeout(() => setFullScreen(false), 50); - }} - onCollapse={() => { - setTimeout(() => setFullScreen(true), 50); - }} - defaultSize={30} - collapsible={true} - > - <div - className={`flex transition-all rounded-2xl ${fullScreen ? "h-screen" : "h-[calc(100vh-3rem)]"} w-full flex-col overflow-hidden bg-[#1F2428]`} - > - <div className="flex items-center justify-between bg-[#2C3439] px-4 py-2 text-lg font-medium text-[#989EA4]"> - Change Filters - <Image src={SettingsIcon} alt="setting-icon" /> - </div> - {visible ? ( - <SidePanel /> - ) : ( - <h1 className="text-center py-10 text-xl"> - Need more space to show! - </h1> - )} - </div> - </Panel> - <PanelResizeHandle - className={`relative flex items-center transition-all justify-center ${!fullScreen && "px-1"}`} - > - <div - className={`rounded-lg bg-[#2F363B] ${!fullScreen && "px-1"} transition-all py-2`} - > - <Image src={DragIcon} alt="drag-icon" /> - </div> - </PanelResizeHandle> - <Panel className="relative" defaultSize={70} minSize={60}> - <div - className={`absolute overflow-hidden transition-all inset-0 ${fullScreen ? "h-screen " : "h-[calc(100vh-3rem)] rounded-2xl"} w-full`} - > - <Canvas /> - </div> - </Panel> - </PanelGroup> - </div> - </div> + <RectProvider id={params.id}> + <ResizaleLayout /> + </RectProvider> ); } - -function SidePanel() { - const [value, setValue] = useState(""); - const [dragAsText, setDragAsText] = useState(false); - return ( - <> - <div className="px-3 py-5"> - <input - placeholder="search..." - onChange={(e) => { - setValue(e.target.value); - }} - value={value} - // rows={1} - className="w-full resize-none rounded-xl bg-[#151515] px-3 py-4 text-xl text-[#989EA4] outline-none focus:outline-none sm:max-h-52" - /> - </div> - <div className="flex items-center justify-end px-3 py-4"> - <Switch - className="bg-[#151515] data-[state=unchecked]:bg-red-400 data-[state=checked]:bg-blue-400" - onCheckedChange={(e) => setDragAsText(e)} - id="drag-text-mode" - /> - <Label htmlFor="drag-text-mode">Drag as Text</Label> - </div> - <DraggableComponentsContainer content={content} /> - </> - ); -} - -export default page; - -const content = [ - { - content: - "Regional growth patterns diverge, with strong performance in the United States and several emerging markets, contrasted by weaker prospects in many advanced economies, particularly in Europe (World Economic Forum) (OECD). The rapid adoption of artificial intelligence (AI) is expected to drive productivity growth, especially in advanced economies, potentially mitigating labor shortages and boosting income levels in emerging markets (World Economic Forum) (OECD). However, ongoing geopolitical tensions and economic fragmentation are likely to maintain a level of uncertainty and volatility in the global economy (World Economic Forum.", - icon: AutocompleteIcon, - iconAlt: "Autocomplete", - extraInfo: - "Page Url: https://chatgpt.com/c/762cd44e-1752-495b-967a-aa3c23c6024a", - }, - { - content: - "As of mid-2024, the global economy is experiencing modest growth with significant regional disparities. Global GDP growth is projected to be around 3.1% in 2024, rising slightly to 3.2% in 2025. This performance, although below the pre-pandemic average, reflects resilience despite various economic pressures, including tight monetary conditions and geopolitical tensions (IMF)(OECD) Inflation is moderating faster than expected, with global headline inflation projected to fall to 5.8% in 2024 and 4.4% in 2025, contributing to improving real incomes and positive trade growth (IMF) (OECD)", - icon: blockIcon, - iconAlt: "Autocomplete", - extraInfo: - "Page Url: https://www.cnbc.com/2024/05/23/nvidia-keeps-hitting-records-can-investors-still-buy-the-stock.html?&qsearchterm=nvidia", - }, -]; diff --git a/apps/web/app/(canvas)/canvas/layout.tsx b/apps/web/app/(canvas)/canvas/layout.tsx deleted file mode 100644 index 9bc3b6d7..00000000 --- a/apps/web/app/(canvas)/canvas/layout.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import "../canvasStyles.css"; - -export default function RootLayout({ - children, -}: { - children: React.ReactNode; -}) { - return ( - <div lang="en" className="bg-[#151515]"> - <div>{children}</div> - </div> - ); -} diff --git a/apps/web/app/(canvas)/canvas/page.tsx b/apps/web/app/(canvas)/canvas/page.tsx index 8b5252af..d0824a20 100644 --- a/apps/web/app/(canvas)/canvas/page.tsx +++ b/apps/web/app/(canvas)/canvas/page.tsx @@ -1,9 +1,22 @@ -import { redirect } from "next/navigation"; import React from "react"; +import { getCanvas } from "@/app/actions/fetchers"; +import SearchandCreate from "./search&create"; +import ThinkPads from "./thinkPads"; -function page() { - redirect("/signin"); - return <div>page</div>; +async function page() { + const canvas = await getCanvas(); + return ( + <div className="h-screen w-full py-32 text-[#FFFFFF] "> + <div className="flex w-full flex-col items-center gap-8"> + <h1 className="text-4xl font-medium">Your thinkpads</h1> + <SearchandCreate /> + { + // @ts-ignore + canvas.success && <ThinkPads data={canvas.data} /> + } + </div> + </div> + ); } export default page; diff --git a/apps/web/app/(canvas)/canvas/search&create.tsx b/apps/web/app/(canvas)/canvas/search&create.tsx new file mode 100644 index 00000000..e73ad76f --- /dev/null +++ b/apps/web/app/(canvas)/canvas/search&create.tsx @@ -0,0 +1,45 @@ +"use client"; + +import { useFormStatus } from "react-dom"; +import Image from "next/image"; +import { SearchIcon } from "@repo/ui/icons"; +import { createCanvas } from "@/app/actions/doers"; +import { toast } from "sonner"; + +export default function SearchandCreate() { + return ( + <div className="flex w-[90%] max-w-2xl gap-2"> + <div className="flex flex-grow items-center overflow-hidden rounded-xl bg-[#1F2428]"> + <input + placeholder="search here..." + className="flex-grow bg-[#1F2428] px-5 py-3 text-xl focus:border-none focus:outline-none" + /> + <button className="h-full border-l-2 border-[#384149] px-2 pl-2"> + <Image src={SearchIcon} alt="search" /> + </button> + </div> + + <form + action={async () => { + const res = await createCanvas(); + if (!res.success) { + toast.warning(res.message, { + style: { backgroundColor: "rgb(22 31 42 / 0.3)" }, + }); + } + }} + > + <Button /> + </form> + </div> + ); +} + +function Button() { + const { pending } = useFormStatus(); + return ( + <button className="rounded-xl bg-[#1F2428] px-5 py-3 text-xl text-[#B8C4C6]"> + {pending ? "Creating.." : "Create New"} + </button> + ); +} diff --git a/apps/web/app/(canvas)/canvas/thinkPad.tsx b/apps/web/app/(canvas)/canvas/thinkPad.tsx new file mode 100644 index 00000000..fff30f26 --- /dev/null +++ b/apps/web/app/(canvas)/canvas/thinkPad.tsx @@ -0,0 +1,276 @@ +import { getCanvasData } from "@/app/actions/fetchers"; +import { AnimatePresence, motion } from "framer-motion"; +import Link from "next/link"; +import { + EllipsisHorizontalCircleIcon, + TrashIcon, + PencilSquareIcon, +} from "@heroicons/react/24/outline"; +import { toast } from "sonner"; +import { Label } from "@repo/ui/shadcn/label"; + +const childVariants = { + hidden: { opacity: 0, y: 10, filter: "blur(2px)" }, + visible: { opacity: 1, y: 0, filter: "blur(0px)" }, +}; + +export default function ThinkPad({ + title, + description, + image, + id, +}: { + title: string; + description: string; + image: string; + id: string; +}) { + const [deleted, setDeleted] = useState(false); + const [info, setInfo] = useState({ title, description }); + return ( + <AnimatePresence mode="sync"> + {!deleted && ( + <motion.div + layout + exit={{ opacity: 0, scaleY: 0 }} + variants={childVariants} + className="flex h-48 origin-top relative gap-4 rounded-2xl bg-[#1F2428] p-2" + > + <Link + className="h-full select-none min-w-[40%] bg-[#363f46] rounded-xl overflow-hidden" + href={`/canvas/${id}`} + > + <Suspense + fallback={ + <div className=" h-full w-full flex justify-center items-center"> + Loading... + </div> + } + > + <ImageComponent id={id} /> + </Suspense> + </Link> + <div className="flex flex-col gap-2"> + <motion.h2 + initial={{ opacity: 0, filter: "blur(3px)" }} + animate={{ opacity: 1, filter: "blur(0px)" }} + key={info.title} + > + {info.title} + </motion.h2> + <motion.h3 + key={info.description} + initial={{ opacity: 0, filter: "blur(3px)" }} + animate={{ opacity: 1, filter: "blur(0px)" }} + className="overflow-hidden text-ellipsis text-[#B8C4C6]" + > + {info.description} + </motion.h3> + </div> + <Menu + info={info} + id={id} + setDeleted={() => setDeleted(true)} + setInfo={(e) => setInfo(e)} + /> + </motion.div> + )} + </AnimatePresence> + ); +} + +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@repo/ui/shadcn/popover"; + +function Menu({ + info, + id, + setDeleted, + setInfo, +}: { + info: { title: string; description: string }; + id: string; + setDeleted: () => void; + setInfo: ({ + title, + description, + }: { + title: string; + description: string; + }) => void; +}) { + return ( + <Popover> + <PopoverTrigger className="absolute z-20 top-0 right-0" asChild> + <Button variant="secondary"> + <EllipsisHorizontalCircleIcon className="size-5 stroke-2 stroke-[#B8C4C6]" /> + </Button> + </PopoverTrigger> + <PopoverContent + align="start" + className="w-32 px-2 py-2 bg-[#161f2a]/30 text-[#B8C4C6] border-border flex flex-col gap-3" + > + <EditToolbar info={info} id={id} setInfo={setInfo} /> + <Button + onClick={async () => { + const res = await deleteCanvas(id); + if (res.success) { + toast.success("Thinkpad removed.", { + style: { backgroundColor: "rgb(22 31 42 / 0.3)" }, + }); + setDeleted(); + } else { + toast.warning("Something went wrong.", { + style: { backgroundColor: "rgb(22 31 42 / 0.3)" }, + }); + } + }} + className="flex gap-2 border-border" + variant="outline" + > + <TrashIcon className="size-8 stroke-1" /> Delete + </Button> + </PopoverContent> + </Popover> + ); +} + +function EditToolbar({ + id, + setInfo, + info, +}: { + id: string; + setInfo: ({ + title, + description, + }: { + title: string; + description: string; + }) => void; + info: { + title: string; + description: string; + }; +}) { + const [open, setOpen] = useState(false); + return ( + <Dialog open={open} onOpenChange={setOpen}> + <DialogTrigger asChild> + <Button className="flex gap-2 border-border" variant="outline"> + <PencilSquareIcon className="size-8 stroke-1" /> Edit + </Button> + </DialogTrigger> + <DialogContent className="sm:max-w-[425px] bg-[#161f2a]/30 border-0"> + <form + action={async (FormData) => { + const data = { + title: FormData.get("title") as string, + description: FormData.get("description") as string, + }; + const res = await AddCanvasInfo({ id, ...data }); + if (res.success) { + setOpen(false); + setInfo(data); + } else { + setOpen(false); + toast.error("Something went wrong.", { + style: { backgroundColor: "rgb(22 31 42 / 0.3)" }, + }); + } + }} + > + <DialogHeader> + <DialogTitle>Edit Canvas</DialogTitle> + <DialogDescription> + Add Description to your canvas. Pro tip: Let AI do the job, as you + add your content into canvas, we will autogenerate your + description. + </DialogDescription> + </DialogHeader> + <div className="grid gap-4 py-4"> + <div className="grid grid-cols-4 items-center gap-4"> + <Label htmlFor="title" className="text-right"> + Title + </Label> + <Input + defaultValue={info.title} + name="title" + id="title" + placeholder="life planning..." + className="col-span-3 border-0" + /> + </div> + <div className="grid grid-cols-4 items-center gap-4"> + <Label htmlFor="description" className="text-right"> + Description + </Label> + <Textarea + defaultValue={info.description} + rows={6} + id="description" + name="description" + placeholder="contains information about..." + className="col-span-3 border-0 resize-none" + /> + </div> + </div> + <DialogFooter> + <Button type="submit">Save changes</Button> + </DialogFooter> + </form> + </DialogContent> + </Dialog> + ); +} + +import { Suspense, memo, use, useState } from "react"; +import { Box, TldrawImage } from "tldraw"; +import { Button } from "@repo/ui/shadcn/button"; +import { AddCanvasInfo, deleteCanvas } from "@/app/actions/doers"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@repo/ui/shadcn/dialog"; +import { Input } from "@repo/ui/shadcn/input"; +import { Textarea } from "@repo/ui/shadcn/textarea"; +import { textCardUtil } from "@/components/canvas/textCard"; +import { twitterCardUtil } from "@/components/canvas/twitterCard"; + +const ImageComponent = memo(({ id }: { id: string }) => { + const snapshot = use(getCanvasData(id)); + if (snapshot.bounds) { + const pageBounds = new Box( + snapshot.bounds.x, + snapshot.bounds.y, + snapshot.bounds.w, + snapshot.bounds.h, + ); + + return ( + <TldrawImage + shapeUtils={[twitterCardUtil, textCardUtil]} + snapshot={snapshot.snapshot} + background={false} + darkMode={true} + bounds={pageBounds} + padding={0} + scale={1} + format="png" + /> + ); + } + return ( + <div className=" h-full w-full flex justify-center items-center"> + Drew things to seee here + </div> + ); +}); diff --git a/apps/web/app/(canvas)/canvas/thinkPads.tsx b/apps/web/app/(canvas)/canvas/thinkPads.tsx new file mode 100644 index 00000000..3e8d7550 --- /dev/null +++ b/apps/web/app/(canvas)/canvas/thinkPads.tsx @@ -0,0 +1,32 @@ +"use client"; +import { motion } from "framer-motion"; +import ThinkPad from "./thinkPad"; + +const containerVariants = { + hidden: { opacity: 0 }, + visible: { + opacity: 1, + transition: { + staggerChildren: 0.1, + }, + }, +}; + +export default function ThinkPads({ + data, +}: { + data: { image: string; title: string; description: string; id: string }[]; +}) { + return ( + <motion.div + variants={containerVariants} + initial="hidden" + animate="visible" + className="w-[90%] max-w-2xl space-y-6" + > + {data.map((item) => { + return <ThinkPad {...item} />; + })} + </motion.div> + ); +} diff --git a/apps/web/app/(canvas)/canvasStyles.css b/apps/web/app/(canvas)/canvasStyles.css index 04da2054..af4740e1 100644 --- a/apps/web/app/(canvas)/canvasStyles.css +++ b/apps/web/app/(canvas)/canvasStyles.css @@ -1,28 +1,36 @@ .tl-background { - background: #1F2428 !important; + background: #1f2428 !important; } -.tlui-style-panel.tlui-style-panel__wrapper, .tlui-navigation-panel::before ,.tlui-menu-zone, .tlui-toolbar__tools, .tlui-popover__content, .tlui-menu, .tlui-button__help, .tlui-help-menu, .tlui-dialog__content { - background: #2C3439 !important; - border-top: #2C3439 !important; - border-right: #2C3439 !important; - border-bottom: #2C3439 !important; - border-left: #2C3439 !important; +.tlui-style-panel.tlui-style-panel__wrapper, +.tlui-navigation-panel::before, +.tlui-menu-zone, +.tlui-toolbar__tools, +.tlui-popover__content, +.tlui-menu, +.tlui-button__help, +.tlui-help-menu, +.tlui-dialog__content { + background: #2c3840 !important; + border-top: #2c3840 !important; + border-right: #2c3840 !important; + border-bottom: #2c3840 !important; + border-left: #2c3840 !important; } .tlui-navigation-panel::before { - border-top: #2C3439 !important; - border-right: #2C3439 !important; + border-top: #2c3840 !important; + border-right: #2c3840 !important; } .tlui-minimap { - background: #2C3439 !important; + background: #2c3840 !important; } .tlui-minimap__canvas { - background: #1F2428 !important; + background: #1f2428 !important; } .tlui-dialog__overlay { position: fixed; -}
\ No newline at end of file +} diff --git a/apps/web/app/(canvas)/layout.tsx b/apps/web/app/(canvas)/layout.tsx new file mode 100644 index 00000000..33e0adfb --- /dev/null +++ b/apps/web/app/(canvas)/layout.tsx @@ -0,0 +1,30 @@ +import { auth } from "@/server/auth"; +import "./canvasStyles.css"; +import { redirect } from "next/navigation"; +import BackgroundPlus from "../(landing)/GridPatterns/PlusGrid"; +import { Toaster } from "@repo/ui/shadcn/sonner"; + +export default async function RootLayout({ + children, +}: { + children: React.ReactNode; +}) { + const info = await auth(); + + if (!info) { + return redirect("/signin"); + } + return ( + <> + <div className="relative flex justify-center z-40 pointer-events-none"> + <div + className="absolute -z-10 left-0 top-[10%] h-32 w-[90%] overflow-x-hidden bg-[rgb(54,157,253)] bg-opacity-100 md:bg-opacity-70 blur-[337.4px]" + style={{ transform: "rotate(-30deg)" }} + /> + </div> + <BackgroundPlus className="absolute top-0 left-0 w-full h-full -z-50 opacity-70" /> + <div>{children}</div> + <Toaster /> + </> + ); +} |