From 3143baac875a19a6598100a41db06ea9b162bf3c Mon Sep 17 00:00:00 2001 From: codetorso Date: Sat, 29 Jun 2024 07:02:27 +0530 Subject: [PATCH 1/6] User Canvas (1/2) --- apps/web/app/(canvas)/canvas/[id]/page.tsx | 4 - apps/web/app/(canvas)/canvas/layout.tsx | 13 - apps/web/app/(canvas)/canvas/page.tsx | 22 +- .../web/app/(canvas)/canvas/search&create.tsx | 35 + apps/web/app/(canvas)/canvas/thinkPad.tsx | 36 + apps/web/app/(canvas)/canvas/thinkPads.tsx | 30 + apps/web/app/(canvas)/layout.tsx | 19 + apps/web/app/actions/doers.ts | 35 + apps/web/app/actions/fetchers.ts | 24 + apps/web/cf-env.d.ts | 6 + ...{000_setup.sql => 0000_bitter_electro.sql} | 18 +- .../migrations/0001_remarkable_avengers.sql | 4 - apps/web/migrations/meta/0000_snapshot.json | 824 ++++++++++++++++++ apps/web/migrations/meta/_journal.json | 13 + apps/web/server/db/schema.ts | 19 + package.json | 2 +- 16 files changed, 1076 insertions(+), 28 deletions(-) delete mode 100644 apps/web/app/(canvas)/canvas/layout.tsx create mode 100644 apps/web/app/(canvas)/canvas/search&create.tsx create mode 100644 apps/web/app/(canvas)/canvas/thinkPad.tsx create mode 100644 apps/web/app/(canvas)/canvas/thinkPads.tsx create mode 100644 apps/web/app/(canvas)/layout.tsx rename apps/web/migrations/{000_setup.sql => 0000_bitter_electro.sql} (85%) delete mode 100644 apps/web/migrations/0001_remarkable_avengers.sql create mode 100644 apps/web/migrations/meta/0000_snapshot.json create mode 100644 apps/web/migrations/meta/_journal.json diff --git a/apps/web/app/(canvas)/canvas/[id]/page.tsx b/apps/web/app/(canvas)/canvas/[id]/page.tsx index 6efb6cf4..e58ab35e 100644 --- a/apps/web/app/(canvas)/canvas/[id]/page.tsx +++ b/apps/web/app/(canvas)/canvas/[id]/page.tsx @@ -9,15 +9,11 @@ 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"); - return (
-
{children}
-
- ); -} diff --git a/apps/web/app/(canvas)/canvas/page.tsx b/apps/web/app/(canvas)/canvas/page.tsx index 8b5252af..815d0b93 100644 --- a/apps/web/app/(canvas)/canvas/page.tsx +++ b/apps/web/app/(canvas)/canvas/page.tsx @@ -1,9 +1,23 @@ -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
page
; +async function page() { + const canvas = await getCanvas(); + return ( +
+
+

Your thinkpads

+

{JSON.stringify(canvas)}

+ + { + // @ts-ignore + canvas.success && + } +
+
+ ); } 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..3998dde7 --- /dev/null +++ b/apps/web/app/(canvas)/canvas/search&create.tsx @@ -0,0 +1,35 @@ +"use client" + +import { useFormStatus } from "react-dom"; +import Image from "next/image"; +import { SearchIcon } from "@repo/ui/icons"; +import { createCanvas } from "@/app/actions/doers"; + +export default function SearchandCreate() { + return ( +
+
+ + +
+ +
+
+ ); +} + +function Button() { + const {pending} = useFormStatus() + return ( + + ); +} \ No newline at end of file diff --git a/apps/web/app/(canvas)/canvas/thinkPad.tsx b/apps/web/app/(canvas)/canvas/thinkPad.tsx new file mode 100644 index 00000000..dad2de48 --- /dev/null +++ b/apps/web/app/(canvas)/canvas/thinkPad.tsx @@ -0,0 +1,36 @@ +import {motion} from "framer-motion" +import Link from "next/link"; + +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; +}) { + return ( + + +
+ +
+
{title}
+
+ {description} +
+
+
+ ); +} \ No newline at end of file diff --git a/apps/web/app/(canvas)/canvas/thinkPads.tsx b/apps/web/app/(canvas)/canvas/thinkPads.tsx new file mode 100644 index 00000000..b83d4142 --- /dev/null +++ b/apps/web/app/(canvas)/canvas/thinkPads.tsx @@ -0,0 +1,30 @@ +"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 ( + + {data.map((item) => { + return ; + })} + + ); +} diff --git a/apps/web/app/(canvas)/layout.tsx b/apps/web/app/(canvas)/layout.tsx new file mode 100644 index 00000000..5c925573 --- /dev/null +++ b/apps/web/app/(canvas)/layout.tsx @@ -0,0 +1,19 @@ +import { auth } from "@/server/auth"; +import "./canvasStyles.css"; +import { redirect } from "next/navigation"; + +export default async function RootLayout({ + children, +}: { + children: React.ReactNode; +}) { + + const info = await auth(); + + if (!info) { + return redirect("/signin"); + } + return ( +
{children}
+ ); +} diff --git a/apps/web/app/actions/doers.ts b/apps/web/app/actions/doers.ts index 99a1b719..30ddbc8e 100644 --- a/apps/web/app/actions/doers.ts +++ b/apps/web/app/actions/doers.ts @@ -3,6 +3,7 @@ import { revalidatePath } from "next/cache"; import { db } from "../../server/db"; import { + canvas, chatHistory, chatThreads, contentToSpace, @@ -381,3 +382,37 @@ export const linkTelegramToUser = async ( data: true, }; }; + +export const createCanvas = async () => { + const data = await auth(); + + if (!data || !data.user || !data.user.id) { + redirect("/signin"); + return { error: "Not authenticated", success: false }; + } + + const resp = await db + .insert(canvas) + .values({ userId: data.user.id }).returning({id: canvas.id}); + redirect(`/canvas/${resp[0]!.id}`); + // TODO INVESTIGATE: NO REDIRECT INSIDE TRY CATCH BLOCK + // try { + // const resp = await db + // .insert(canvas) + // .values({ userId: data.user.id }).returning({id: canvas.id}); + // return redirect(`/canvas/${resp[0]!.id}`); + // } catch (e: unknown) { + // const error = e as Error; + // if ( + // error.message.includes("D1_ERROR: UNIQUE constraint failed: space.name") + // ) { + // return { success: false, data: 0, error: "Space already exists" }; + // } else { + // return { + // success: false, + // data: 0, + // error: "Failed to create space with error: " + error.message, + // }; + // } + // } +}; \ No newline at end of file diff --git a/apps/web/app/actions/fetchers.ts b/apps/web/app/actions/fetchers.ts index 664c20ac..f485c28d 100644 --- a/apps/web/app/actions/fetchers.ts +++ b/apps/web/app/actions/fetchers.ts @@ -3,6 +3,7 @@ import { and, asc, eq, inArray, not, sql } from "drizzle-orm"; import { db } from "../../server/db"; import { + canvas, chatHistory, ChatThread, chatThreads, @@ -189,3 +190,26 @@ export const getChatHistory = async (): ServerActionReturnType< }; } }; + +export const getCanvas = async () => { + const data = await auth(); + + if (!data || !data.user || !data.user.id) { + redirect("/signin"); + return { error: "Not authenticated", success: false }; + } + + try { + const canvases = await db.select().from(canvas).where(eq(canvas.userId, data.user.id)) + + return { + success: true, + data: canvases.map(({ userId, ...rest }) => rest), + }; + } catch (e) { + return { + success: false, + error: (e as Error).message, + }; + } +}; diff --git a/apps/web/cf-env.d.ts b/apps/web/cf-env.d.ts index be5c991a..e645f7b1 100644 --- a/apps/web/cf-env.d.ts +++ b/apps/web/cf-env.d.ts @@ -4,13 +4,19 @@ declare global { GOOGLE_CLIENT_ID: string; GOOGLE_CLIENT_SECRET: string; AUTH_SECRET: string; + R2_ENDPOINT: string; R2_ACCESS_KEY_ID: string; R2_SECRET_ACCESS_KEY: string; R2_PUBLIC_BUCKET_ADDRESS: string; R2_BUCKET_NAME: string; + BACKEND_SECURITY_KEY: string; BACKEND_BASE_URL: string; + + CLOUDFLARE_ACCOUNT_ID: string, + CLOUDFLARE_DATABASE_ID: string, + CLOUDFLARE_D1_TOKEN: string, } } } diff --git a/apps/web/migrations/000_setup.sql b/apps/web/migrations/0000_bitter_electro.sql similarity index 85% rename from apps/web/migrations/000_setup.sql rename to apps/web/migrations/0000_bitter_electro.sql index a4855ec9..3ce840e7 100644 --- a/apps/web/migrations/000_setup.sql +++ b/apps/web/migrations/0000_bitter_electro.sql @@ -27,6 +27,15 @@ CREATE TABLE `authenticator` ( FOREIGN KEY (`userId`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade ); --> statement-breakpoint +CREATE TABLE `canvas` ( + `id` text PRIMARY KEY NOT NULL, + `title` text DEFAULT 'Untitled' NOT NULL, + `description` text DEFAULT 'Untitled' NOT NULL, + `url` text DEFAULT '' NOT NULL, + `userId` text NOT NULL, + FOREIGN KEY (`userId`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade +); +--> statement-breakpoint CREATE TABLE `chatHistory` ( `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, `threadId` text NOT NULL, @@ -86,7 +95,8 @@ CREATE TABLE `user` ( `name` text, `email` text NOT NULL, `emailVerified` integer, - `image` text + `image` text, + `telegramId` text ); --> statement-breakpoint CREATE TABLE `verificationToken` ( @@ -97,6 +107,7 @@ CREATE TABLE `verificationToken` ( ); --> statement-breakpoint CREATE UNIQUE INDEX `authenticator_credentialID_unique` ON `authenticator` (`credentialID`);--> statement-breakpoint +CREATE INDEX `canvas_user_userId` ON `canvas` (`userId`);--> statement-breakpoint CREATE INDEX `chatHistory_thread_idx` ON `chatHistory` (`threadId`);--> statement-breakpoint CREATE INDEX `chatThread_user_idx` ON `chatThread` (`userId`);--> statement-breakpoint CREATE UNIQUE INDEX `space_name_unique` ON `space` (`name`);--> statement-breakpoint @@ -105,4 +116,7 @@ CREATE INDEX `spaces_user_idx` ON `space` (`user`);--> statement-breakpoint CREATE INDEX `storedContent_url_idx` ON `storedContent` (`url`);--> statement-breakpoint CREATE INDEX `storedContent_savedAt_idx` ON `storedContent` (`savedAt`);--> statement-breakpoint CREATE INDEX `storedContent_title_idx` ON `storedContent` (`title`);--> statement-breakpoint -CREATE INDEX `storedContent_user_idx` ON `storedContent` (`user`); +CREATE INDEX `storedContent_user_idx` ON `storedContent` (`user`);--> statement-breakpoint +CREATE INDEX `users_email_idx` ON `user` (`email`);--> statement-breakpoint +CREATE INDEX `users_telegram_idx` ON `user` (`telegramId`);--> statement-breakpoint +CREATE INDEX `users_id_idx` ON `user` (`id`); \ No newline at end of file diff --git a/apps/web/migrations/0001_remarkable_avengers.sql b/apps/web/migrations/0001_remarkable_avengers.sql deleted file mode 100644 index cb2fa422..00000000 --- a/apps/web/migrations/0001_remarkable_avengers.sql +++ /dev/null @@ -1,4 +0,0 @@ -ALTER TABLE `user` ADD `telegramId` text;--> statement-breakpoint -CREATE INDEX `users_email_idx` ON `user` (`email`);--> statement-breakpoint -CREATE INDEX `users_telegram_idx` ON `user` (`telegramId`);--> statement-breakpoint -CREATE INDEX `users_id_idx` ON `user` (`id`); \ No newline at end of file diff --git a/apps/web/migrations/meta/0000_snapshot.json b/apps/web/migrations/meta/0000_snapshot.json new file mode 100644 index 00000000..30920063 --- /dev/null +++ b/apps/web/migrations/meta/0000_snapshot.json @@ -0,0 +1,824 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "58ec8bd8-5aad-4ade-a718-74eaf6056d36", + "prevId": "00000000-0000-0000-0000-000000000000", + "tables": { + "account": { + "name": "account", + "columns": { + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "providerAccountId": { + "name": "providerAccountId", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "token_type": { + "name": "token_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "session_state": { + "name": "session_state", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "account_userId_user_id_fk": { + "name": "account_userId_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "account_provider_providerAccountId_pk": { + "columns": [ + "provider", + "providerAccountId" + ], + "name": "account_provider_providerAccountId_pk" + } + }, + "uniqueConstraints": {} + }, + "authenticator": { + "name": "authenticator", + "columns": { + "credentialID": { + "name": "credentialID", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "providerAccountId": { + "name": "providerAccountId", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "credentialPublicKey": { + "name": "credentialPublicKey", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "counter": { + "name": "counter", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "credentialDeviceType": { + "name": "credentialDeviceType", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "credentialBackedUp": { + "name": "credentialBackedUp", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "transports": { + "name": "transports", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "authenticator_credentialID_unique": { + "name": "authenticator_credentialID_unique", + "columns": [ + "credentialID" + ], + "isUnique": true + } + }, + "foreignKeys": { + "authenticator_userId_user_id_fk": { + "name": "authenticator_userId_user_id_fk", + "tableFrom": "authenticator", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "authenticator_userId_credentialID_pk": { + "columns": [ + "credentialID", + "userId" + ], + "name": "authenticator_userId_credentialID_pk" + } + }, + "uniqueConstraints": {} + }, + "canvas": { + "name": "canvas", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'Untitled'" + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'Untitled'" + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "canvas_user_userId": { + "name": "canvas_user_userId", + "columns": [ + "userId" + ], + "isUnique": false + } + }, + "foreignKeys": { + "canvas_userId_user_id_fk": { + "name": "canvas_userId_user_id_fk", + "tableFrom": "canvas", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "chatHistory": { + "name": "chatHistory", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "threadId": { + "name": "threadId", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "question": { + "name": "question", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "answerParts": { + "name": "answerParts", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "answerSources": { + "name": "answerSources", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "answerJustification": { + "name": "answerJustification", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "chatHistory_thread_idx": { + "name": "chatHistory_thread_idx", + "columns": [ + "threadId" + ], + "isUnique": false + } + }, + "foreignKeys": { + "chatHistory_threadId_chatThread_id_fk": { + "name": "chatHistory_threadId_chatThread_id_fk", + "tableFrom": "chatHistory", + "tableTo": "chatThread", + "columnsFrom": [ + "threadId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "chatThread": { + "name": "chatThread", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "firstMessage": { + "name": "firstMessage", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "chatThread_user_idx": { + "name": "chatThread_user_idx", + "columns": [ + "userId" + ], + "isUnique": false + } + }, + "foreignKeys": { + "chatThread_userId_user_id_fk": { + "name": "chatThread_userId_user_id_fk", + "tableFrom": "chatThread", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "contentToSpace": { + "name": "contentToSpace", + "columns": { + "contentId": { + "name": "contentId", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "spaceId": { + "name": "spaceId", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "contentToSpace_contentId_storedContent_id_fk": { + "name": "contentToSpace_contentId_storedContent_id_fk", + "tableFrom": "contentToSpace", + "tableTo": "storedContent", + "columnsFrom": [ + "contentId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "contentToSpace_spaceId_space_id_fk": { + "name": "contentToSpace_spaceId_space_id_fk", + "tableFrom": "contentToSpace", + "tableTo": "space", + "columnsFrom": [ + "spaceId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "contentToSpace_contentId_spaceId_pk": { + "columns": [ + "contentId", + "spaceId" + ], + "name": "contentToSpace_contentId_spaceId_pk" + } + }, + "uniqueConstraints": {} + }, + "session": { + "name": "session", + "columns": { + "sessionToken": { + "name": "sessionToken", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires": { + "name": "expires", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "session_userId_user_id_fk": { + "name": "session_userId_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "space": { + "name": "space", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'none'" + }, + "user": { + "name": "user", + "type": "text(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "space_name_unique": { + "name": "space_name_unique", + "columns": [ + "name" + ], + "isUnique": true + }, + "spaces_name_idx": { + "name": "spaces_name_idx", + "columns": [ + "name" + ], + "isUnique": false + }, + "spaces_user_idx": { + "name": "spaces_user_idx", + "columns": [ + "user" + ], + "isUnique": false + } + }, + "foreignKeys": { + "space_user_user_id_fk": { + "name": "space_user_user_id_fk", + "tableFrom": "space", + "tableTo": "user", + "columnsFrom": [ + "user" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "storedContent": { + "name": "storedContent", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "savedAt": { + "name": "savedAt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "baseUrl": { + "name": "baseUrl", + "type": "text(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "ogImage": { + "name": "ogImage", + "type": "text(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'page'" + }, + "image": { + "name": "image", + "type": "text(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "user": { + "name": "user", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "storedContent_url_idx": { + "name": "storedContent_url_idx", + "columns": [ + "url" + ], + "isUnique": false + }, + "storedContent_savedAt_idx": { + "name": "storedContent_savedAt_idx", + "columns": [ + "savedAt" + ], + "isUnique": false + }, + "storedContent_title_idx": { + "name": "storedContent_title_idx", + "columns": [ + "title" + ], + "isUnique": false + }, + "storedContent_user_idx": { + "name": "storedContent_user_idx", + "columns": [ + "user" + ], + "isUnique": false + } + }, + "foreignKeys": { + "storedContent_user_user_id_fk": { + "name": "storedContent_user_user_id_fk", + "tableFrom": "storedContent", + "tableTo": "user", + "columnsFrom": [ + "user" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "user": { + "name": "user", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "emailVerified": { + "name": "emailVerified", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "telegramId": { + "name": "telegramId", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "users_email_idx": { + "name": "users_email_idx", + "columns": [ + "email" + ], + "isUnique": false + }, + "users_telegram_idx": { + "name": "users_telegram_idx", + "columns": [ + "telegramId" + ], + "isUnique": false + }, + "users_id_idx": { + "name": "users_id_idx", + "columns": [ + "id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "verificationToken": { + "name": "verificationToken", + "columns": { + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires": { + "name": "expires", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "verificationToken_identifier_token_pk": { + "columns": [ + "identifier", + "token" + ], + "name": "verificationToken_identifier_token_pk" + } + }, + "uniqueConstraints": {} + } + }, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/apps/web/migrations/meta/_journal.json b/apps/web/migrations/meta/_journal.json new file mode 100644 index 00000000..68c578aa --- /dev/null +++ b/apps/web/migrations/meta/_journal.json @@ -0,0 +1,13 @@ +{ + "version": "7", + "dialect": "sqlite", + "entries": [ + { + "idx": 0, + "version": "6", + "when": 1719619409551, + "tag": "0000_bitter_electro", + "breakpoints": true + } + ] +} \ No newline at end of file diff --git a/apps/web/server/db/schema.ts b/apps/web/server/db/schema.ts index 69372a35..c40e50f1 100644 --- a/apps/web/server/db/schema.ts +++ b/apps/web/server/db/schema.ts @@ -198,5 +198,24 @@ export const chatHistory = createTable( }), ); +export const canvas = createTable( + "canvas", + { + id: text("id") + .notNull() + .primaryKey() + .$defaultFn(() => crypto.randomUUID()), + title: text("title").default("Untitled").notNull(), + description: text("description").default("Untitled").notNull(), + imageUrl: text("url").default("").notNull(), + userId: text("userId") + .notNull() + .references(() => users.id, { onDelete: "cascade" }), + }, + (canvas) => ({ + userIdx: index("canvas_user_userId").on(canvas.userId), + }), +); + export type ChatThread = typeof chatThreads.$inferSelect; export type ChatHistory = typeof chatHistory.$inferSelect; diff --git a/package.json b/package.json index c94610a6..e95e630c 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "@repo/ui": "*", "@tailwindcss/typography": "^0.5.13", "autoprefixer": "^10.4.19", - "drizzle-kit": "^0.21.2", + "drizzle-kit": "^0.22.7", "eslint-plugin-next-on-pages": "^1.11.3", "lint-staged": "^15.2.5", "postcss": "^8.4.38", From ffe9103da6eb7f93819f6c0d0ddf0905ccfc4524 Mon Sep 17 00:00:00 2001 From: codetorso Date: Sat, 29 Jun 2024 20:08:46 +0530 Subject: [PATCH 2/6] user canvas (2/2) --- apps/web/app/(canvas)/canvas/[id]/page.tsx | 135 ++------------ .../_canvas_comp/(components)}/canvas.tsx | 8 +- .../(components)}/draggableComponent.tsx | 4 - .../(components)}/dropComponent.tsx | 0 .../(components)}/enabledComp copy.tsx | 0 .../(components)}/enabledComp.tsx | 0 .../(components)/resizableLayout.tsx | 170 ++++++++++++++++++ .../_canvas_comp/(components)}/savesnap.tsx | 24 +-- .../_canvas_comp/(components)}/textCard.tsx | 0 .../(components)}/twitterCard.tsx | 0 .../canvas/_canvas_comp}/lib/context.ts | 0 .../_canvas_comp}/lib/createAssetUrl.ts | 0 .../canvas/_canvas_comp}/lib/createEmbeds.ts | 0 .../canvas/_canvas_comp/lib/loadSnap.ts | 13 ++ apps/web/app/actions/doers.ts | 15 +- apps/web/app/actions/fetchers.ts | 43 +++++ apps/web/env.d.ts | 1 + apps/web/wrangler.toml | 4 + packages/ui/components/canvas/lib/loadSnap.ts | 14 -- 19 files changed, 271 insertions(+), 160 deletions(-) rename {packages/ui/components/canvas/components => apps/web/app/(canvas)/canvas/_canvas_comp/(components)}/canvas.tsx (92%) rename {packages/ui/components/canvas/components => apps/web/app/(canvas)/canvas/_canvas_comp/(components)}/draggableComponent.tsx (93%) rename {packages/ui/components/canvas/components => apps/web/app/(canvas)/canvas/_canvas_comp/(components)}/dropComponent.tsx (100%) rename {packages/ui/components/canvas/components => apps/web/app/(canvas)/canvas/_canvas_comp/(components)}/enabledComp copy.tsx (100%) rename {packages/ui/components/canvas/components => apps/web/app/(canvas)/canvas/_canvas_comp/(components)}/enabledComp.tsx (100%) create mode 100644 apps/web/app/(canvas)/canvas/_canvas_comp/(components)/resizableLayout.tsx rename {packages/ui/components/canvas/components => apps/web/app/(canvas)/canvas/_canvas_comp/(components)}/savesnap.tsx (55%) rename {packages/ui/components/canvas/components => apps/web/app/(canvas)/canvas/_canvas_comp/(components)}/textCard.tsx (100%) rename {packages/ui/components/canvas/components => apps/web/app/(canvas)/canvas/_canvas_comp/(components)}/twitterCard.tsx (100%) rename {packages/ui/components/canvas => apps/web/app/(canvas)/canvas/_canvas_comp}/lib/context.ts (100%) rename {packages/ui/components/canvas => apps/web/app/(canvas)/canvas/_canvas_comp}/lib/createAssetUrl.ts (100%) rename {packages/ui/components/canvas => apps/web/app/(canvas)/canvas/_canvas_comp}/lib/createEmbeds.ts (100%) create mode 100644 apps/web/app/(canvas)/canvas/_canvas_comp/lib/loadSnap.ts delete mode 100644 packages/ui/components/canvas/lib/loadSnap.ts diff --git a/apps/web/app/(canvas)/canvas/[id]/page.tsx b/apps/web/app/(canvas)/canvas/[id]/page.tsx index e58ab35e..7cf5b91c 100644 --- a/apps/web/app/(canvas)/canvas/[id]/page.tsx +++ b/apps/web/app/(canvas)/canvas/[id]/page.tsx @@ -1,125 +1,18 @@ -"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"; - -function page() { - const [fullScreen, setFullScreen] = useState(false); - const [visible, setVisible] = useState(true); +import { userHasCanvas } from "@/app/actions/fetchers"; +import { redirect } from "next/navigation"; +import { + RectProvider, + ResizaleLayout, +} from "../_canvas_comp/(components)/resizableLayout"; +export default async function page({ params }: any) { + const canvasExists = await userHasCanvas(params.id); + if (!canvasExists.success) { + redirect("/canvas"); + } return ( -
-
- { - l[0]! < 20 ? setVisible(false) : setVisible(true); - }} - className={` ${fullScreen ? "w-[calc(100vw-2rem)]" : "w-screen"} transition-all`} - direction="horizontal" - > - { - setTimeout(() => setFullScreen(false), 50); - }} - onCollapse={() => { - setTimeout(() => setFullScreen(true), 50); - }} - defaultSize={30} - collapsible={true} - > -
-
- Change Filters - setting-icon -
- {visible ? ( - - ) : ( -

- Need more space to show! -

- )} -
-
- -
- drag-icon -
-
- -
- -
-
-
-
-
+ + + ); } - -function SidePanel() { - const [value, setValue] = useState(""); - const [dragAsText, setDragAsText] = useState(false); - return ( - <> -
- { - 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" - /> -
-
- setDragAsText(e)} - id="drag-text-mode" - /> - -
- - - ); -} - -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/packages/ui/components/canvas/components/canvas.tsx b/apps/web/app/(canvas)/canvas/_canvas_comp/(components)/canvas.tsx similarity index 92% rename from packages/ui/components/canvas/components/canvas.tsx rename to apps/web/app/(canvas)/canvas/_canvas_comp/(components)/canvas.tsx index f3e4c090..22fcd709 100644 --- a/packages/ui/components/canvas/components/canvas.tsx +++ b/apps/web/app/(canvas)/canvas/_canvas_comp/(components)/canvas.tsx @@ -12,6 +12,7 @@ import { getAssetUrls } from "@tldraw/assets/selfHosted"; import { memo } from "react"; import DragContext from "../lib/context"; import DropZone from "./dropComponent"; +import { useRect } from "./resizableLayout"; // import "./canvas.css"; export const Canvas = memo(() => { @@ -46,12 +47,13 @@ export const Canvas = memo(() => { }); const TldrawComponent = memo(() => { + const { id } = useRect(); const [storeWithStatus, setStoreWithStatus] = useState({ status: "loading", }); useEffect(() => { const fetchStore = async () => { - const store = await loadRemoteSnapshot(); + const store = await loadRemoteSnapshot(id); setStoreWithStatus({ store: store, @@ -71,7 +73,7 @@ const TldrawComponent = memo(() => { }); }, []); - setUserPreferences({ id: "supermemory" }); + setUserPreferences({ id: "supermemory",colorScheme: "dark" }); const assetUrls = getAssetUrls(); return ( @@ -85,7 +87,7 @@ const TldrawComponent = memo(() => { onMount={handleMount} >
- +
diff --git a/packages/ui/components/canvas/components/draggableComponent.tsx b/apps/web/app/(canvas)/canvas/_canvas_comp/(components)/draggableComponent.tsx similarity index 93% rename from packages/ui/components/canvas/components/draggableComponent.tsx rename to apps/web/app/(canvas)/canvas/_canvas_comp/(components)/draggableComponent.tsx index d0832e81..8c39c732 100644 --- a/packages/ui/components/canvas/components/draggableComponent.tsx +++ b/apps/web/app/(canvas)/canvas/_canvas_comp/(components)/draggableComponent.tsx @@ -4,7 +4,6 @@ import { useRef, useState } from "react"; interface DraggableComponentsProps { content: string; extraInfo?: string; - icon: string; iconAlt: string; } @@ -19,7 +18,6 @@ export default function DraggableComponentsContainer({ return ( @@ -32,7 +30,6 @@ export default function DraggableComponentsContainer({ function DraggableComponents({ content, extraInfo, - icon, iconAlt, }: DraggableComponentsProps) { const [isDragging, setIsDragging] = useState(false); @@ -59,7 +56,6 @@ function DraggableComponents({ draggable className={`flex gap-4 px-1 rounded-md text-[#989EA4] border-2 transition ${isDragging ? "border-blue-600" : "border-[#1F2428]"}`} > - {iconAlt}

{content}

diff --git a/packages/ui/components/canvas/components/dropComponent.tsx b/apps/web/app/(canvas)/canvas/_canvas_comp/(components)/dropComponent.tsx similarity index 100% rename from packages/ui/components/canvas/components/dropComponent.tsx rename to apps/web/app/(canvas)/canvas/_canvas_comp/(components)/dropComponent.tsx diff --git a/packages/ui/components/canvas/components/enabledComp copy.tsx b/apps/web/app/(canvas)/canvas/_canvas_comp/(components)/enabledComp copy.tsx similarity index 100% rename from packages/ui/components/canvas/components/enabledComp copy.tsx rename to apps/web/app/(canvas)/canvas/_canvas_comp/(components)/enabledComp copy.tsx diff --git a/packages/ui/components/canvas/components/enabledComp.tsx b/apps/web/app/(canvas)/canvas/_canvas_comp/(components)/enabledComp.tsx similarity index 100% rename from packages/ui/components/canvas/components/enabledComp.tsx rename to apps/web/app/(canvas)/canvas/_canvas_comp/(components)/enabledComp.tsx diff --git a/apps/web/app/(canvas)/canvas/_canvas_comp/(components)/resizableLayout.tsx b/apps/web/app/(canvas)/canvas/_canvas_comp/(components)/resizableLayout.tsx new file mode 100644 index 00000000..5ba6780b --- /dev/null +++ b/apps/web/app/(canvas)/canvas/_canvas_comp/(components)/resizableLayout.tsx @@ -0,0 +1,170 @@ +"use client"; + +import { Canvas } from "./canvas"; +import React, { createContext, useContext, useState } from "react"; +import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels"; +import { SettingsIcon, DragIcon } from "@repo/ui/icons"; +import DraggableComponentsContainer from "./draggableComponent"; +import Image from "next/image"; +import { Label } from "@repo/ui/shadcn/label"; + +interface RectContextType { + fullScreen: boolean; + setFullScreen: React.Dispatch>; + visible: boolean; + setVisible: React.Dispatch>; + id: string +} + +const RectContext = createContext(undefined); + +export const RectProvider = ({ id, children }: {id: string, children: React.ReactNode}) => { + const [fullScreen, setFullScreen] = useState(false); + const [visible, setVisible] = useState(true); + + const value = { + id, + fullScreen, + setFullScreen, + visible, + setVisible, + }; + + return {children}; +}; + +export const useRect = () => { + const context = useContext(RectContext); + if (context === undefined) { + throw new Error('useRect must be used within a RectProvider'); + } + return context; +}; + + +export function ResizaleLayout() { + const { setVisible, fullScreen, setFullScreen } = useRect(); + + return ( +
+ { + l[0]! < 20 ? setVisible(false) : setVisible(true); + }} + className={` ${fullScreen ? "w-[calc(100vw-2rem)]" : "w-screen"} transition-all`} + direction="horizontal" + > + { + setTimeout(() => setFullScreen(false), 50); + }} + onCollapse={() => { + setTimeout(() => setFullScreen(true), 50); + }} + defaultSize={30} + collapsible={true} + > + + + + + + + + + +
+ ); +} + +function DragIconContainer() { + const { fullScreen} = useRect(); + return ( +
+ drag-icon +
+ ); +} + +function CanvasContainer() { + const { fullScreen} = useRect(); + return ( +
+ +
+ ); +} + +function SidePanelContainer() { + const { fullScreen, visible} = useRect(); + return ( +
+
+ Change Filters + setting-icon +
+ {visible ? ( + + ) : ( +

Need more space to show!

+ )} +
+ ); +} + +function SidePanel() { + const [value, setValue] = useState(""); + // const [dragAsText, setDragAsText] = useState(false); + return ( + <> +
+ { + 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" + /> +
+
+ {/* setDragAsText(e)} + id="drag-text-mode" + /> */} + +
+ + + ); +} + + +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.", + 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)", + 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/packages/ui/components/canvas/components/savesnap.tsx b/apps/web/app/(canvas)/canvas/_canvas_comp/(components)/savesnap.tsx similarity index 55% rename from packages/ui/components/canvas/components/savesnap.tsx rename to apps/web/app/(canvas)/canvas/_canvas_comp/(components)/savesnap.tsx index f82e97e3..45fc7e9d 100644 --- a/packages/ui/components/canvas/components/savesnap.tsx +++ b/apps/web/app/(canvas)/canvas/_canvas_comp/(components)/savesnap.tsx @@ -1,27 +1,17 @@ import { useCallback, useEffect, useState } from "react"; -import { debounce, useEditor } from "tldraw"; +import { debounce, getSnapshot, useEditor } from "tldraw"; +import { SaveCanvas } from "@/app/actions/doers"; -export function SaveStatus() { +export function SaveStatus({id}: {id:string}) { const [save, setSave] = useState("saved!"); const editor = useEditor(); + const debouncedSave = useCallback( debounce(async () => { - const snapshot = editor.store.getSnapshot(); - localStorage.setItem("saved", JSON.stringify(snapshot)); - - const res = await fetch( - "https://learning-cf.pruthvirajthinks.workers.dev/post/page3", - { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - data: snapshot, - }), - }, - ); - - console.log(await res.json()); + const snapshot = getSnapshot(editor.store) + SaveCanvas({id, data: JSON.stringify(snapshot)}) + setSave("saved!"); }, 3000), [editor], // Dependency array ensures the function is not recreated on every render diff --git a/packages/ui/components/canvas/components/textCard.tsx b/apps/web/app/(canvas)/canvas/_canvas_comp/(components)/textCard.tsx similarity index 100% rename from packages/ui/components/canvas/components/textCard.tsx rename to apps/web/app/(canvas)/canvas/_canvas_comp/(components)/textCard.tsx diff --git a/packages/ui/components/canvas/components/twitterCard.tsx b/apps/web/app/(canvas)/canvas/_canvas_comp/(components)/twitterCard.tsx similarity index 100% rename from packages/ui/components/canvas/components/twitterCard.tsx rename to apps/web/app/(canvas)/canvas/_canvas_comp/(components)/twitterCard.tsx diff --git a/packages/ui/components/canvas/lib/context.ts b/apps/web/app/(canvas)/canvas/_canvas_comp/lib/context.ts similarity index 100% rename from packages/ui/components/canvas/lib/context.ts rename to apps/web/app/(canvas)/canvas/_canvas_comp/lib/context.ts diff --git a/packages/ui/components/canvas/lib/createAssetUrl.ts b/apps/web/app/(canvas)/canvas/_canvas_comp/lib/createAssetUrl.ts similarity index 100% rename from packages/ui/components/canvas/lib/createAssetUrl.ts rename to apps/web/app/(canvas)/canvas/_canvas_comp/lib/createAssetUrl.ts diff --git a/packages/ui/components/canvas/lib/createEmbeds.ts b/apps/web/app/(canvas)/canvas/_canvas_comp/lib/createEmbeds.ts similarity index 100% rename from packages/ui/components/canvas/lib/createEmbeds.ts rename to apps/web/app/(canvas)/canvas/_canvas_comp/lib/createEmbeds.ts diff --git a/apps/web/app/(canvas)/canvas/_canvas_comp/lib/loadSnap.ts b/apps/web/app/(canvas)/canvas/_canvas_comp/lib/loadSnap.ts new file mode 100644 index 00000000..ee4dc22f --- /dev/null +++ b/apps/web/app/(canvas)/canvas/_canvas_comp/lib/loadSnap.ts @@ -0,0 +1,13 @@ +import { createTLStore, defaultShapeUtils, loadSnapshot } from "tldraw"; +import { twitterCardUtil } from "../(components)/twitterCard"; +import { textCardUtil } from "../(components)/textCard"; +import { getCanvasData } from "@/app/actions/fetchers"; +export async function loadRemoteSnapshot(id:string) { + const snapshot = await getCanvasData(id); + + const newStore = createTLStore({ + shapeUtils: [...defaultShapeUtils, twitterCardUtil, textCardUtil], + }); + loadSnapshot(newStore, snapshot); + return newStore; +} diff --git a/apps/web/app/actions/doers.ts b/apps/web/app/actions/doers.ts index 30ddbc8e..00feaa17 100644 --- a/apps/web/app/actions/doers.ts +++ b/apps/web/app/actions/doers.ts @@ -415,4 +415,17 @@ export const createCanvas = async () => { // }; // } // } -}; \ No newline at end of file +}; + +export const SaveCanvas = async ({id, data}: {id: string, data: string}) => { + console.log({id, data}) + try { + await process.env.CANVAS_SNAPS.put(id, data) + return { + success: true, + message: "in-sync" + } + } catch (error) { + return {success: false, error, message:"An error occured while saving your canvas"} + } +} \ No newline at end of file diff --git a/apps/web/app/actions/fetchers.ts b/apps/web/app/actions/fetchers.ts index f485c28d..d93cd4d0 100644 --- a/apps/web/app/actions/fetchers.ts +++ b/apps/web/app/actions/fetchers.ts @@ -213,3 +213,46 @@ export const getCanvas = async () => { }; } }; + +export const userHasCanvas = async (canvasId: string) => { + const data = await auth(); + + if (!data || !data.user || !data.user.id) { + redirect("/signin"); + return { error: "Not authenticated", success: false }; + } + + try { + const canvases = await db.select().from(canvas).where(eq(canvas.userId, data.user.id)) + const exists = !!canvases.find(canvas => canvas.id === canvasId); + return { + success: exists, + }; + } catch (e) { + return { + success: false, + error: (e as Error).message, + }; + } +} + +export const getCanvasData = async (canvasId: string) => { + + const data = await auth(); + + if (!data || !data.user || !data.user.id) { + redirect("/signin"); + return { error: "Not authenticated", success: false }; + } + + const canvas = await process.env.CANVAS_SNAPS.get(canvasId) + + console.log({canvas, canvasId}) + if (canvas){ + return JSON.parse(canvas); + } else { + return {} + } +} + + diff --git a/apps/web/env.d.ts b/apps/web/env.d.ts index ee41239f..d52ab7b5 100644 --- a/apps/web/env.d.ts +++ b/apps/web/env.d.ts @@ -5,4 +5,5 @@ interface CloudflareEnv { STORAGE: R2Bucket; DATABASE: D1Database; DEV_IMAGES: R2Bucket; + CANVAS_SNAPS: KVNamespace; } diff --git a/apps/web/wrangler.toml b/apps/web/wrangler.toml index 594614c7..a5f9b461 100644 --- a/apps/web/wrangler.toml +++ b/apps/web/wrangler.toml @@ -16,6 +16,10 @@ binding = "DATABASE" database_name = "dev-d1-anycontext" database_id = "fc562605-157a-4f60-b439-2a24ffed5b4c" +[[kv_namespaces]] +binding = "CANVAS_SNAPS" +id = "c6446f7190dd4afebe1c318df3400518" + [[env.production.d1_databases]] binding = "DATABASE" database_name = "prod-d1-supermemory" diff --git a/packages/ui/components/canvas/lib/loadSnap.ts b/packages/ui/components/canvas/lib/loadSnap.ts deleted file mode 100644 index 846b1967..00000000 --- a/packages/ui/components/canvas/lib/loadSnap.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { createTLStore, defaultShapeUtils, loadSnapshot } from "tldraw"; -import { twitterCardUtil } from "../components/twitterCard"; -import { textCardUtil } from "../components/textCard"; -export async function loadRemoteSnapshot() { - const res = await fetch( - "https://learning-cf.pruthvirajthinks.workers.dev/get/page3", - ); - const snapshot = JSON.parse(await res.json()); - const newStore = createTLStore({ - shapeUtils: [...defaultShapeUtils, twitterCardUtil, textCardUtil], - }); - loadSnapshot(newStore, snapshot); - return newStore; -} From 6294f5fa3384f82f51631e43685bba0e49abab4f Mon Sep 17 00:00:00 2001 From: codetorso Date: Sat, 29 Jun 2024 20:34:48 +0530 Subject: [PATCH 3/6] fix typescript errors --- apps/web/app/(canvas)/canvas/[id]/page.tsx | 6 +----- apps/web/app/api/canvas/route.ts | 10 ++++++++++ .../(components) => components/canvas}/canvas.tsx | 10 +++++----- .../canvas}/draggableComponent.tsx | 0 .../canvas}/dropComponent.tsx | 7 +++++-- .../canvas}/enabledComp copy.tsx | 0 .../(components) => components/canvas}/enabledComp.tsx | 0 .../canvas}/resizableLayout.tsx | 0 .../(components) => components/canvas}/savesnap.tsx | 0 .../(components) => components/canvas}/textCard.tsx | 0 .../(components) => components/canvas}/twitterCard.tsx | 0 .../(canvas)/canvas/_canvas_comp => }/lib/context.ts | 0 .../canvas/_canvas_comp => }/lib/createAssetUrl.ts | 0 .../canvas/_canvas_comp => }/lib/createEmbeds.ts | 0 .../(canvas)/canvas/_canvas_comp => }/lib/loadSnap.ts | 7 ++++--- apps/web/tsconfig.json | 3 ++- 16 files changed, 27 insertions(+), 16 deletions(-) create mode 100644 apps/web/app/api/canvas/route.ts rename apps/web/{app/(canvas)/canvas/_canvas_comp/(components) => components/canvas}/canvas.tsx (90%) rename apps/web/{app/(canvas)/canvas/_canvas_comp/(components) => components/canvas}/draggableComponent.tsx (100%) rename apps/web/{app/(canvas)/canvas/_canvas_comp/(components) => components/canvas}/dropComponent.tsx (96%) rename apps/web/{app/(canvas)/canvas/_canvas_comp/(components) => components/canvas}/enabledComp copy.tsx (100%) rename apps/web/{app/(canvas)/canvas/_canvas_comp/(components) => components/canvas}/enabledComp.tsx (100%) rename apps/web/{app/(canvas)/canvas/_canvas_comp/(components) => components/canvas}/resizableLayout.tsx (100%) rename apps/web/{app/(canvas)/canvas/_canvas_comp/(components) => components/canvas}/savesnap.tsx (100%) rename apps/web/{app/(canvas)/canvas/_canvas_comp/(components) => components/canvas}/textCard.tsx (100%) rename apps/web/{app/(canvas)/canvas/_canvas_comp/(components) => components/canvas}/twitterCard.tsx (100%) rename apps/web/{app/(canvas)/canvas/_canvas_comp => }/lib/context.ts (100%) rename apps/web/{app/(canvas)/canvas/_canvas_comp => }/lib/createAssetUrl.ts (100%) rename apps/web/{app/(canvas)/canvas/_canvas_comp => }/lib/createEmbeds.ts (100%) rename apps/web/{app/(canvas)/canvas/_canvas_comp => }/lib/loadSnap.ts (64%) diff --git a/apps/web/app/(canvas)/canvas/[id]/page.tsx b/apps/web/app/(canvas)/canvas/[id]/page.tsx index 7cf5b91c..76f5da30 100644 --- a/apps/web/app/(canvas)/canvas/[id]/page.tsx +++ b/apps/web/app/(canvas)/canvas/[id]/page.tsx @@ -1,10 +1,6 @@ import { userHasCanvas } from "@/app/actions/fetchers"; +import { RectProvider, ResizaleLayout } from "@/components/canvas/resizableLayout"; import { redirect } from "next/navigation"; -import { - RectProvider, - ResizaleLayout, -} from "../_canvas_comp/(components)/resizableLayout"; - export default async function page({ params }: any) { const canvasExists = await userHasCanvas(params.id); if (!canvasExists.success) { diff --git a/apps/web/app/api/canvas/route.ts b/apps/web/app/api/canvas/route.ts new file mode 100644 index 00000000..f6af5109 --- /dev/null +++ b/apps/web/app/api/canvas/route.ts @@ -0,0 +1,10 @@ +export function GET(req: Request){ + const id = new URL(req.url).searchParams.get("id"); + return new Response(JSON.stringify(id)); +} + +export async function POST(req: Request){ + const body = await req.json(); + const id = new URL(req.url).searchParams.get("id"); + return new Response(JSON.stringify({body, id})); +} \ No newline at end of file diff --git a/apps/web/app/(canvas)/canvas/_canvas_comp/(components)/canvas.tsx b/apps/web/components/canvas/canvas.tsx similarity index 90% rename from apps/web/app/(canvas)/canvas/_canvas_comp/(components)/canvas.tsx rename to apps/web/components/canvas/canvas.tsx index 22fcd709..1fbff4b8 100644 --- a/apps/web/app/(canvas)/canvas/_canvas_comp/(components)/canvas.tsx +++ b/apps/web/components/canvas/canvas.tsx @@ -1,16 +1,16 @@ import { useCallback, useEffect, useRef, useState } from "react"; import { Editor, Tldraw, setUserPreferences, TLStoreWithStatus } from "tldraw"; -import { createAssetFromUrl } from "../lib/createAssetUrl"; +import { createAssetFromUrl } from "../../lib/createAssetUrl"; import "tldraw/tldraw.css"; import { components } from "./enabledComp"; import { twitterCardUtil } from "./twitterCard"; import { textCardUtil } from "./textCard"; -import createEmbedsFromUrl from "../lib/createEmbeds"; -import { loadRemoteSnapshot } from "../lib/loadSnap"; +import createEmbedsFromUrl from "../../lib/createEmbeds"; +import { loadRemoteSnapshot } from "../../lib/loadSnap"; import { SaveStatus } from "./savesnap"; import { getAssetUrls } from "@tldraw/assets/selfHosted"; import { memo } from "react"; -import DragContext from "../lib/context"; +import DragContext from "../../lib/context"; import DropZone from "./dropComponent"; import { useRect } from "./resizableLayout"; // import "./canvas.css"; @@ -73,7 +73,7 @@ const TldrawComponent = memo(() => { }); }, []); - setUserPreferences({ id: "supermemory",colorScheme: "dark" }); + setUserPreferences({ id: "supermemory", colorScheme: "dark" }); const assetUrls = getAssetUrls(); return ( diff --git a/apps/web/app/(canvas)/canvas/_canvas_comp/(components)/draggableComponent.tsx b/apps/web/components/canvas/draggableComponent.tsx similarity index 100% rename from apps/web/app/(canvas)/canvas/_canvas_comp/(components)/draggableComponent.tsx rename to apps/web/components/canvas/draggableComponent.tsx diff --git a/apps/web/app/(canvas)/canvas/_canvas_comp/(components)/dropComponent.tsx b/apps/web/components/canvas/dropComponent.tsx similarity index 96% rename from apps/web/app/(canvas)/canvas/_canvas_comp/(components)/dropComponent.tsx rename to apps/web/components/canvas/dropComponent.tsx index 0374f367..5ea383a1 100644 --- a/apps/web/app/(canvas)/canvas/_canvas_comp/(components)/dropComponent.tsx +++ b/apps/web/components/canvas/dropComponent.tsx @@ -1,7 +1,10 @@ import React, { useRef, useCallback, useEffect, useContext } from "react"; import { useEditor } from "tldraw"; -import DragContext, { DragContextType, useDragContext } from "../lib/context"; -import { handleExternalDroppedContent } from "../lib/createEmbeds"; +import DragContext, { + DragContextType, + useDragContext, +} from "../../lib/context"; +import { handleExternalDroppedContent } from "../../lib/createEmbeds"; const stripHtmlTags = (html: string): string => { const div = document.createElement("div"); diff --git a/apps/web/app/(canvas)/canvas/_canvas_comp/(components)/enabledComp copy.tsx b/apps/web/components/canvas/enabledComp copy.tsx similarity index 100% rename from apps/web/app/(canvas)/canvas/_canvas_comp/(components)/enabledComp copy.tsx rename to apps/web/components/canvas/enabledComp copy.tsx diff --git a/apps/web/app/(canvas)/canvas/_canvas_comp/(components)/enabledComp.tsx b/apps/web/components/canvas/enabledComp.tsx similarity index 100% rename from apps/web/app/(canvas)/canvas/_canvas_comp/(components)/enabledComp.tsx rename to apps/web/components/canvas/enabledComp.tsx diff --git a/apps/web/app/(canvas)/canvas/_canvas_comp/(components)/resizableLayout.tsx b/apps/web/components/canvas/resizableLayout.tsx similarity index 100% rename from apps/web/app/(canvas)/canvas/_canvas_comp/(components)/resizableLayout.tsx rename to apps/web/components/canvas/resizableLayout.tsx diff --git a/apps/web/app/(canvas)/canvas/_canvas_comp/(components)/savesnap.tsx b/apps/web/components/canvas/savesnap.tsx similarity index 100% rename from apps/web/app/(canvas)/canvas/_canvas_comp/(components)/savesnap.tsx rename to apps/web/components/canvas/savesnap.tsx diff --git a/apps/web/app/(canvas)/canvas/_canvas_comp/(components)/textCard.tsx b/apps/web/components/canvas/textCard.tsx similarity index 100% rename from apps/web/app/(canvas)/canvas/_canvas_comp/(components)/textCard.tsx rename to apps/web/components/canvas/textCard.tsx diff --git a/apps/web/app/(canvas)/canvas/_canvas_comp/(components)/twitterCard.tsx b/apps/web/components/canvas/twitterCard.tsx similarity index 100% rename from apps/web/app/(canvas)/canvas/_canvas_comp/(components)/twitterCard.tsx rename to apps/web/components/canvas/twitterCard.tsx diff --git a/apps/web/app/(canvas)/canvas/_canvas_comp/lib/context.ts b/apps/web/lib/context.ts similarity index 100% rename from apps/web/app/(canvas)/canvas/_canvas_comp/lib/context.ts rename to apps/web/lib/context.ts diff --git a/apps/web/app/(canvas)/canvas/_canvas_comp/lib/createAssetUrl.ts b/apps/web/lib/createAssetUrl.ts similarity index 100% rename from apps/web/app/(canvas)/canvas/_canvas_comp/lib/createAssetUrl.ts rename to apps/web/lib/createAssetUrl.ts diff --git a/apps/web/app/(canvas)/canvas/_canvas_comp/lib/createEmbeds.ts b/apps/web/lib/createEmbeds.ts similarity index 100% rename from apps/web/app/(canvas)/canvas/_canvas_comp/lib/createEmbeds.ts rename to apps/web/lib/createEmbeds.ts diff --git a/apps/web/app/(canvas)/canvas/_canvas_comp/lib/loadSnap.ts b/apps/web/lib/loadSnap.ts similarity index 64% rename from apps/web/app/(canvas)/canvas/_canvas_comp/lib/loadSnap.ts rename to apps/web/lib/loadSnap.ts index ee4dc22f..fb4647f6 100644 --- a/apps/web/app/(canvas)/canvas/_canvas_comp/lib/loadSnap.ts +++ b/apps/web/lib/loadSnap.ts @@ -1,7 +1,8 @@ import { createTLStore, defaultShapeUtils, loadSnapshot } from "tldraw"; -import { twitterCardUtil } from "../(components)/twitterCard"; -import { textCardUtil } from "../(components)/textCard"; -import { getCanvasData } from "@/app/actions/fetchers"; +import { getCanvasData } from "../app/actions/fetchers"; +import { twitterCardUtil } from "../components/canvas/twitterCard"; +import { textCardUtil } from "../components/canvas/textCard"; + export async function loadRemoteSnapshot(id:string) { const snapshot = await getCanvasData(id); diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json index 34e0a9b0..6efa20e9 100644 --- a/apps/web/tsconfig.json +++ b/apps/web/tsconfig.json @@ -17,7 +17,8 @@ "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", - "../../packages/ui/" + "../../packages/ui/", + "./components" ], "exclude": ["node_modules/"] } From 4d381dd00f95e174a6d771dafe71b8ed673f8fab Mon Sep 17 00:00:00 2001 From: codetorso Date: Mon, 1 Jul 2024 06:08:17 +0530 Subject: [PATCH 4/6] canvas (3/3) --- apps/web/app/(canvas)/canvas/page.tsx | 3 +- .../web/app/(canvas)/canvas/search&create.tsx | 22 +- apps/web/app/(canvas)/canvas/thinkPad.tsx | 274 ++++++++++++++++-- apps/web/app/(canvas)/canvasStyles.css | 2 +- apps/web/app/(canvas)/layout.tsx | 13 +- apps/web/app/actions/doers.ts | 35 +++ apps/web/app/actions/fetchers.ts | 2 +- apps/web/app/api/canvasai/route.ts | 27 ++ .../components/canvas/draggableComponent.tsx | 28 +- .../web/components/canvas/resizableLayout.tsx | 57 ++-- apps/web/components/canvas/savesnap.tsx | 5 +- apps/web/lib/loadSnap.ts | 2 +- 12 files changed, 397 insertions(+), 73 deletions(-) create mode 100644 apps/web/app/api/canvasai/route.ts diff --git a/apps/web/app/(canvas)/canvas/page.tsx b/apps/web/app/(canvas)/canvas/page.tsx index 815d0b93..d0824a20 100644 --- a/apps/web/app/(canvas)/canvas/page.tsx +++ b/apps/web/app/(canvas)/canvas/page.tsx @@ -6,10 +6,9 @@ import ThinkPads from "./thinkPads"; async function page() { const canvas = await getCanvas(); return ( -
+

Your thinkpads

-

{JSON.stringify(canvas)}

{ // @ts-ignore diff --git a/apps/web/app/(canvas)/canvas/search&create.tsx b/apps/web/app/(canvas)/canvas/search&create.tsx index 3998dde7..a52a4cbe 100644 --- a/apps/web/app/(canvas)/canvas/search&create.tsx +++ b/apps/web/app/(canvas)/canvas/search&create.tsx @@ -1,9 +1,10 @@ -"use client" +"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 ( @@ -18,18 +19,27 @@ export default function SearchandCreate() {
-
-
); } function Button() { - const {pending} = useFormStatus() + const { pending } = useFormStatus(); return ( ); -} \ No newline at end of file +} diff --git a/apps/web/app/(canvas)/canvas/thinkPad.tsx b/apps/web/app/(canvas)/canvas/thinkPad.tsx index dad2de48..00a83fda 100644 --- a/apps/web/app/(canvas)/canvas/thinkPad.tsx +++ b/apps/web/app/(canvas)/canvas/thinkPad.tsx @@ -1,5 +1,13 @@ -import {motion} from "framer-motion" +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)" }, @@ -10,27 +18,259 @@ export default function ThinkPad({ title, description, image, - id + id, }: { title: string; description: string; image: string; id: string; }) { + const [deleted, setDeleted] = useState(false); + const [info, setInfo] = useState({ title, description }); return ( - - -
- -
-
{title}
-
- {description} -
-
-
+ + {!deleted && ( + + + + Loading... +
+ } + > + + + +
+ + {info.title} + + + {info.description} + +
+ setDeleted(true)} + setInfo={(e) => setInfo(e)} + /> + + )} + ); -} \ No newline at end of file +} + +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 ( + + + + + + + + + + ); +} + +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 ( + + + + + +
{ + 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)" }, + }); + } + }} + > + + Edit Canvas + + Add Description to your canvas. Pro tip: Let AI do the job, as you + add your content into canvas, we will autogenerate your + description. + + +
+
+ + +
+
+ +