From 3143baac875a19a6598100a41db06ea9b162bf3c Mon Sep 17 00:00:00 2001 From: codetorso Date: Sat, 29 Jun 2024 07:02:27 +0530 Subject: 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 +- apps/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 + apps/web/migrations/0000_bitter_electro.sql | 122 ++++ apps/web/migrations/0001_remarkable_avengers.sql | 4 - apps/web/migrations/000_setup.sql | 108 --- 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 +- 17 files changed, 1182 insertions(+), 134 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 create mode 100644 apps/web/migrations/0000_bitter_electro.sql delete mode 100644 apps/web/migrations/0001_remarkable_avengers.sql delete mode 100644 apps/web/migrations/000_setup.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/0000_bitter_electro.sql b/apps/web/migrations/0000_bitter_electro.sql new file mode 100644 index 00000000..3ce840e7 --- /dev/null +++ b/apps/web/migrations/0000_bitter_electro.sql @@ -0,0 +1,122 @@ +CREATE TABLE `account` ( + `userId` text NOT NULL, + `type` text NOT NULL, + `provider` text NOT NULL, + `providerAccountId` text NOT NULL, + `refresh_token` text, + `access_token` text, + `expires_at` integer, + `token_type` text, + `scope` text, + `id_token` text, + `session_state` text, + PRIMARY KEY(`provider`, `providerAccountId`), + FOREIGN KEY (`userId`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade +); +--> statement-breakpoint +CREATE TABLE `authenticator` ( + `credentialID` text NOT NULL, + `userId` text NOT NULL, + `providerAccountId` text NOT NULL, + `credentialPublicKey` text NOT NULL, + `counter` integer NOT NULL, + `credentialDeviceType` text NOT NULL, + `credentialBackedUp` integer NOT NULL, + `transports` text, + PRIMARY KEY(`credentialID`, `userId`), + 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, + `question` text NOT NULL, + `answerParts` text, + `answerSources` text, + `answerJustification` text, + FOREIGN KEY (`threadId`) REFERENCES `chatThread`(`id`) ON UPDATE no action ON DELETE cascade +); +--> statement-breakpoint +CREATE TABLE `chatThread` ( + `id` text PRIMARY KEY NOT NULL, + `firstMessage` text NOT NULL, + `userId` text NOT NULL, + FOREIGN KEY (`userId`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade +); +--> statement-breakpoint +CREATE TABLE `contentToSpace` ( + `contentId` integer NOT NULL, + `spaceId` integer NOT NULL, + PRIMARY KEY(`contentId`, `spaceId`), + FOREIGN KEY (`contentId`) REFERENCES `storedContent`(`id`) ON UPDATE no action ON DELETE cascade, + FOREIGN KEY (`spaceId`) REFERENCES `space`(`id`) ON UPDATE no action ON DELETE cascade +); +--> statement-breakpoint +CREATE TABLE `session` ( + `sessionToken` text PRIMARY KEY NOT NULL, + `userId` text NOT NULL, + `expires` integer NOT NULL, + FOREIGN KEY (`userId`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade +); +--> statement-breakpoint +CREATE TABLE `space` ( + `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, + `name` text DEFAULT 'none' NOT NULL, + `user` text(255), + FOREIGN KEY (`user`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade +); +--> statement-breakpoint +CREATE TABLE `storedContent` ( + `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, + `content` text NOT NULL, + `title` text(255), + `description` text(255), + `url` text NOT NULL, + `savedAt` integer NOT NULL, + `baseUrl` text(255), + `ogImage` text(255), + `type` text DEFAULT 'page', + `image` text(255), + `user` text, + FOREIGN KEY (`user`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade +); +--> statement-breakpoint +CREATE TABLE `user` ( + `id` text PRIMARY KEY NOT NULL, + `name` text, + `email` text NOT NULL, + `emailVerified` integer, + `image` text, + `telegramId` text +); +--> statement-breakpoint +CREATE TABLE `verificationToken` ( + `identifier` text NOT NULL, + `token` text NOT NULL, + `expires` integer NOT NULL, + PRIMARY KEY(`identifier`, `token`) +); +--> 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 +CREATE INDEX `spaces_name_idx` ON `space` (`name`);--> statement-breakpoint +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`);--> 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/000_setup.sql b/apps/web/migrations/000_setup.sql deleted file mode 100644 index a4855ec9..00000000 --- a/apps/web/migrations/000_setup.sql +++ /dev/null @@ -1,108 +0,0 @@ -CREATE TABLE `account` ( - `userId` text NOT NULL, - `type` text NOT NULL, - `provider` text NOT NULL, - `providerAccountId` text NOT NULL, - `refresh_token` text, - `access_token` text, - `expires_at` integer, - `token_type` text, - `scope` text, - `id_token` text, - `session_state` text, - PRIMARY KEY(`provider`, `providerAccountId`), - FOREIGN KEY (`userId`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade -); ---> statement-breakpoint -CREATE TABLE `authenticator` ( - `credentialID` text NOT NULL, - `userId` text NOT NULL, - `providerAccountId` text NOT NULL, - `credentialPublicKey` text NOT NULL, - `counter` integer NOT NULL, - `credentialDeviceType` text NOT NULL, - `credentialBackedUp` integer NOT NULL, - `transports` text, - PRIMARY KEY(`credentialID`, `userId`), - 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, - `question` text NOT NULL, - `answerParts` text, - `answerSources` text, - `answerJustification` text, - FOREIGN KEY (`threadId`) REFERENCES `chatThread`(`id`) ON UPDATE no action ON DELETE cascade -); ---> statement-breakpoint -CREATE TABLE `chatThread` ( - `id` text PRIMARY KEY NOT NULL, - `firstMessage` text NOT NULL, - `userId` text NOT NULL, - FOREIGN KEY (`userId`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade -); ---> statement-breakpoint -CREATE TABLE `contentToSpace` ( - `contentId` integer NOT NULL, - `spaceId` integer NOT NULL, - PRIMARY KEY(`contentId`, `spaceId`), - FOREIGN KEY (`contentId`) REFERENCES `storedContent`(`id`) ON UPDATE no action ON DELETE cascade, - FOREIGN KEY (`spaceId`) REFERENCES `space`(`id`) ON UPDATE no action ON DELETE cascade -); ---> statement-breakpoint -CREATE TABLE `session` ( - `sessionToken` text PRIMARY KEY NOT NULL, - `userId` text NOT NULL, - `expires` integer NOT NULL, - FOREIGN KEY (`userId`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade -); ---> statement-breakpoint -CREATE TABLE `space` ( - `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, - `name` text DEFAULT 'none' NOT NULL, - `user` text(255), - FOREIGN KEY (`user`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade -); ---> statement-breakpoint -CREATE TABLE `storedContent` ( - `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL, - `content` text NOT NULL, - `title` text(255), - `description` text(255), - `url` text NOT NULL, - `savedAt` integer NOT NULL, - `baseUrl` text(255), - `ogImage` text(255), - `type` text DEFAULT 'page', - `image` text(255), - `user` text, - FOREIGN KEY (`user`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade -); ---> statement-breakpoint -CREATE TABLE `user` ( - `id` text PRIMARY KEY NOT NULL, - `name` text, - `email` text NOT NULL, - `emailVerified` integer, - `image` text -); ---> statement-breakpoint -CREATE TABLE `verificationToken` ( - `identifier` text NOT NULL, - `token` text NOT NULL, - `expires` integer NOT NULL, - PRIMARY KEY(`identifier`, `token`) -); ---> statement-breakpoint -CREATE UNIQUE INDEX `authenticator_credentialID_unique` ON `authenticator` (`credentialID`);--> 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 -CREATE INDEX `spaces_name_idx` ON `space` (`name`);--> statement-breakpoint -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`); 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", -- cgit v1.2.3 From ffe9103da6eb7f93819f6c0d0ddf0905ccfc4524 Mon Sep 17 00:00:00 2001 From: codetorso Date: Sat, 29 Jun 2024 20:08:46 +0530 Subject: user canvas (2/2) --- apps/web/app/(canvas)/canvas/[id]/page.tsx | 135 ++---------- .../canvas/_canvas_comp/(components)/canvas.tsx | 96 +++++++++ .../(components)/draggableComponent.tsx | 67 ++++++ .../_canvas_comp/(components)/dropComponent.tsx | 203 ++++++++++++++++++ .../_canvas_comp/(components)/enabledComp copy.tsx | 22 ++ .../_canvas_comp/(components)/enabledComp.tsx | 22 ++ .../_canvas_comp/(components)/resizableLayout.tsx | 170 +++++++++++++++ .../canvas/_canvas_comp/(components)/savesnap.tsx | 33 +++ .../canvas/_canvas_comp/(components)/textCard.tsx | 45 ++++ .../_canvas_comp/(components)/twitterCard.tsx | 84 ++++++++ .../(canvas)/canvas/_canvas_comp/lib/context.ts | 18 ++ .../canvas/_canvas_comp/lib/createAssetUrl.ts | 94 ++++++++ .../canvas/_canvas_comp/lib/createEmbeds.ts | 236 +++++++++++++++++++++ .../(canvas)/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 + .../ui/components/canvas/components/canvas.tsx | 94 -------- .../canvas/components/draggableComponent.tsx | 71 ------- .../components/canvas/components/dropComponent.tsx | 203 ------------------ .../canvas/components/enabledComp copy.tsx | 22 -- .../components/canvas/components/enabledComp.tsx | 22 -- .../ui/components/canvas/components/savesnap.tsx | 43 ---- .../ui/components/canvas/components/textCard.tsx | 45 ---- .../components/canvas/components/twitterCard.tsx | 84 -------- packages/ui/components/canvas/lib/context.ts | 18 -- .../ui/components/canvas/lib/createAssetUrl.ts | 94 -------- packages/ui/components/canvas/lib/createEmbeds.ts | 236 --------------------- packages/ui/components/canvas/lib/loadSnap.ts | 14 -- 30 files changed, 1179 insertions(+), 1068 deletions(-) create mode 100644 apps/web/app/(canvas)/canvas/_canvas_comp/(components)/canvas.tsx create mode 100644 apps/web/app/(canvas)/canvas/_canvas_comp/(components)/draggableComponent.tsx create mode 100644 apps/web/app/(canvas)/canvas/_canvas_comp/(components)/dropComponent.tsx create mode 100644 apps/web/app/(canvas)/canvas/_canvas_comp/(components)/enabledComp copy.tsx create mode 100644 apps/web/app/(canvas)/canvas/_canvas_comp/(components)/enabledComp.tsx create mode 100644 apps/web/app/(canvas)/canvas/_canvas_comp/(components)/resizableLayout.tsx create mode 100644 apps/web/app/(canvas)/canvas/_canvas_comp/(components)/savesnap.tsx create mode 100644 apps/web/app/(canvas)/canvas/_canvas_comp/(components)/textCard.tsx create mode 100644 apps/web/app/(canvas)/canvas/_canvas_comp/(components)/twitterCard.tsx create mode 100644 apps/web/app/(canvas)/canvas/_canvas_comp/lib/context.ts create mode 100644 apps/web/app/(canvas)/canvas/_canvas_comp/lib/createAssetUrl.ts create mode 100644 apps/web/app/(canvas)/canvas/_canvas_comp/lib/createEmbeds.ts create mode 100644 apps/web/app/(canvas)/canvas/_canvas_comp/lib/loadSnap.ts delete mode 100644 packages/ui/components/canvas/components/canvas.tsx delete mode 100644 packages/ui/components/canvas/components/draggableComponent.tsx delete mode 100644 packages/ui/components/canvas/components/dropComponent.tsx delete mode 100644 packages/ui/components/canvas/components/enabledComp copy.tsx delete mode 100644 packages/ui/components/canvas/components/enabledComp.tsx delete mode 100644 packages/ui/components/canvas/components/savesnap.tsx delete mode 100644 packages/ui/components/canvas/components/textCard.tsx delete mode 100644 packages/ui/components/canvas/components/twitterCard.tsx delete mode 100644 packages/ui/components/canvas/lib/context.ts delete mode 100644 packages/ui/components/canvas/lib/createAssetUrl.ts delete mode 100644 packages/ui/components/canvas/lib/createEmbeds.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 { userHasCanvas } from "@/app/actions/fetchers"; +import { redirect } from "next/navigation"; +import { + RectProvider, + ResizaleLayout, +} from "../_canvas_comp/(components)/resizableLayout"; -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); - - 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); +export default async function page({ params }: any) { + const canvasExists = await userHasCanvas(params.id); + if (!canvasExists.success) { + redirect("/canvas"); + } 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/apps/web/app/(canvas)/canvas/_canvas_comp/(components)/canvas.tsx b/apps/web/app/(canvas)/canvas/_canvas_comp/(components)/canvas.tsx new file mode 100644 index 00000000..22fcd709 --- /dev/null +++ b/apps/web/app/(canvas)/canvas/_canvas_comp/(components)/canvas.tsx @@ -0,0 +1,96 @@ +import { useCallback, useEffect, useRef, useState } from "react"; +import { Editor, Tldraw, setUserPreferences, TLStoreWithStatus } from "tldraw"; +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 { SaveStatus } from "./savesnap"; +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(() => { + const [isDraggingOver, setIsDraggingOver] = useState(false); + const Dragref = useRef(null); + + const handleDragOver = (event: any) => { + event.preventDefault(); + setIsDraggingOver(true); + console.log("entere"); + }; + + useEffect(() => { + const divElement = Dragref.current; + if (divElement) { + divElement.addEventListener("dragover", handleDragOver); + } + return () => { + if (divElement) { + divElement.removeEventListener("dragover", handleDragOver); + } + }; + }, []); + + return ( + +
+ +
+
+ ); +}); + +const TldrawComponent = memo(() => { + const { id } = useRect(); + const [storeWithStatus, setStoreWithStatus] = useState({ + status: "loading", + }); + useEffect(() => { + const fetchStore = async () => { + const store = await loadRemoteSnapshot(id); + + setStoreWithStatus({ + store: store, + status: "not-synced", + }); + }; + + fetchStore(); + }, []); + + const handleMount = useCallback((editor: Editor) => { + (window as any).app = editor; + (window as any).editor = editor; + editor.registerExternalAssetHandler("url", createAssetFromUrl); + editor.registerExternalContentHandler("url", ({ url, point, sources }) => { + createEmbedsFromUrl({ url, point, sources, editor }); + }); + }, []); + + 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/app/(canvas)/canvas/_canvas_comp/(components)/draggableComponent.tsx new file mode 100644 index 00000000..8c39c732 --- /dev/null +++ b/apps/web/app/(canvas)/canvas/_canvas_comp/(components)/draggableComponent.tsx @@ -0,0 +1,67 @@ +import Image from "next/image"; +import { useRef, useState } from "react"; + +interface DraggableComponentsProps { + content: string; + extraInfo?: string; + iconAlt: string; +} + +export default function DraggableComponentsContainer({ + content, +}: { + content: DraggableComponentsProps[]; +}) { + return ( +
+ {content.map((i) => { + return ( + + ); + })} +
+ ); +} + +function DraggableComponents({ + content, + extraInfo, + iconAlt, +}: DraggableComponentsProps) { + const [isDragging, setIsDragging] = useState(false); + const containerRef = useRef(null); + + const handleDragStart = (event: React.DragEvent) => { + setIsDragging(true); + if (containerRef.current) { + // Serialize the children as a string for dataTransfer + const childrenHtml = containerRef.current.innerHTML; + event.dataTransfer.setData("text/html", childrenHtml); + } + }; + + const handleDragEnd = () => { + setIsDragging(false); + }; + + return ( +
+
+
+

{content}

+
+

{extraInfo}

+
+
+ ); +} diff --git a/apps/web/app/(canvas)/canvas/_canvas_comp/(components)/dropComponent.tsx b/apps/web/app/(canvas)/canvas/_canvas_comp/(components)/dropComponent.tsx new file mode 100644 index 00000000..0374f367 --- /dev/null +++ b/apps/web/app/(canvas)/canvas/_canvas_comp/(components)/dropComponent.tsx @@ -0,0 +1,203 @@ +import React, { useRef, useCallback, useEffect, useContext } from "react"; +import { useEditor } from "tldraw"; +import DragContext, { DragContextType, useDragContext } from "../lib/context"; +import { handleExternalDroppedContent } from "../lib/createEmbeds"; + +const stripHtmlTags = (html: string): string => { + const div = document.createElement("div"); + div.innerHTML = html; + return div.textContent || div.innerText || ""; +}; + +function formatTextToRatio(text: string) { + const totalWidth = text.length; + const maxLineWidth = Math.floor(totalWidth / 4); + + const words = text.split(" "); + let lines = []; + let currentLine = ""; + + words.forEach((word) => { + // Check if adding the next word exceeds the maximum line width + if ((currentLine + word).length <= maxLineWidth) { + currentLine += (currentLine ? " " : "") + word; + } else { + // If the current line is full, push it to new line + lines.push(currentLine); + currentLine = word; + } + }); + if (currentLine) { + lines.push(currentLine); + } + return lines.join("\n"); +} + +function DropZone() { + const dropRef = useRef(null); + const { isDraggingOver, setIsDraggingOver } = useDragContext(); + + const editor = useEditor(); + + const handleDragLeave = () => { + setIsDraggingOver(false); + console.log("leaver"); + }; + + useEffect(() => { + setInterval(() => { + editor.selectAll(); + const shapes = editor.getSelectedShapes(); + const text = shapes.filter((s) => s.type === "text"); + console.log("hrhh", text); + }, 5000); + }, []); + + const handleDrop = useCallback((event: DragEvent) => { + event.preventDefault(); + setIsDraggingOver(false); + const dt = event.dataTransfer; + if (!dt) { + return; + } + const items = dt.items; + + for (let i = 0; i < items.length; i++) { + if (items[i]!.kind === "file" && items[i]!.type.startsWith("image/")) { + const file = items[i]!.getAsFile(); + if (file) { + const reader = new FileReader(); + reader.onload = (e) => { + if (e.target) { + // setDroppedImage(e.target.result as string); + } + }; + reader.readAsDataURL(file); + } + } else if (items[i]!.kind === "string") { + items[i]!.getAsString((data) => { + const cleanText = stripHtmlTags(data); + const onethree = formatTextToRatio(cleanText); + handleExternalDroppedContent({ editor, text: onethree }); + }); + } + } + }, []); + + useEffect(() => { + const divElement = dropRef.current; + if (divElement) { + divElement.addEventListener("drop", handleDrop); + divElement.addEventListener("dragleave", handleDragLeave); + } + return () => { + if (divElement) { + divElement.removeEventListener("drop", handleDrop); + divElement.addEventListener("dragleave", handleDragLeave); + } + }; + }, []); + + return ( +
+ {isDraggingOver && ( + <> +
+ +
+
+ +
+
+ +
+
+ +
+

Drop here to add Content on Canvas

+ + )} +
+ ); +} + +function TopRight() { + return ( + + + + ); +} + +function TopLeft() { + return ( + + + + ); +} + +function BottomLeft() { + return ( + + + + ); +} + +function BottomRight() { + return ( + + + + ); +} + +export default DropZone; diff --git a/apps/web/app/(canvas)/canvas/_canvas_comp/(components)/enabledComp copy.tsx b/apps/web/app/(canvas)/canvas/_canvas_comp/(components)/enabledComp copy.tsx new file mode 100644 index 00000000..85811b82 --- /dev/null +++ b/apps/web/app/(canvas)/canvas/_canvas_comp/(components)/enabledComp copy.tsx @@ -0,0 +1,22 @@ +import { TLUiComponents } from "tldraw"; + +export const components: Partial = { + ActionsMenu: null, + MainMenu: null, + QuickActions: null, + TopPanel: null, + DebugPanel: null, + DebugMenu: null, + PageMenu: null, + // Minimap: null, + // ContextMenu: null, + // HelpMenu: null, + // ZoomMenu: null, + // StylePanel: null, + // NavigationPanel: null, + // Toolbar: null, + // KeyboardShortcutsDialog: null, + // HelperButtons: null, + // SharePanel: null, + // MenuPanel: null, +}; \ No newline at end of file diff --git a/apps/web/app/(canvas)/canvas/_canvas_comp/(components)/enabledComp.tsx b/apps/web/app/(canvas)/canvas/_canvas_comp/(components)/enabledComp.tsx new file mode 100644 index 00000000..85811b82 --- /dev/null +++ b/apps/web/app/(canvas)/canvas/_canvas_comp/(components)/enabledComp.tsx @@ -0,0 +1,22 @@ +import { TLUiComponents } from "tldraw"; + +export const components: Partial = { + ActionsMenu: null, + MainMenu: null, + QuickActions: null, + TopPanel: null, + DebugPanel: null, + DebugMenu: null, + PageMenu: null, + // Minimap: null, + // ContextMenu: null, + // HelpMenu: null, + // ZoomMenu: null, + // StylePanel: null, + // NavigationPanel: null, + // Toolbar: null, + // KeyboardShortcutsDialog: null, + // HelperButtons: null, + // SharePanel: null, + // MenuPanel: null, +}; \ No newline at end of file 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/apps/web/app/(canvas)/canvas/_canvas_comp/(components)/savesnap.tsx b/apps/web/app/(canvas)/canvas/_canvas_comp/(components)/savesnap.tsx new file mode 100644 index 00000000..45fc7e9d --- /dev/null +++ b/apps/web/app/(canvas)/canvas/_canvas_comp/(components)/savesnap.tsx @@ -0,0 +1,33 @@ +import { useCallback, useEffect, useState } from "react"; +import { debounce, getSnapshot, useEditor } from "tldraw"; +import { SaveCanvas } from "@/app/actions/doers"; + +export function SaveStatus({id}: {id:string}) { + const [save, setSave] = useState("saved!"); + const editor = useEditor(); + + + const debouncedSave = useCallback( + debounce(async () => { + 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 + ); + + useEffect(() => { + const unsubscribe = editor.store.listen( + () => { + setSave("saving..."); + debouncedSave(); + }, + { scope: "document", source: "user" }, + ); + + return () => unsubscribe(); // Cleanup on unmount + }, [editor, debouncedSave]); + + return ; +} \ No newline at end of file diff --git a/apps/web/app/(canvas)/canvas/_canvas_comp/(components)/textCard.tsx b/apps/web/app/(canvas)/canvas/_canvas_comp/(components)/textCard.tsx new file mode 100644 index 00000000..b24dae52 --- /dev/null +++ b/apps/web/app/(canvas)/canvas/_canvas_comp/(components)/textCard.tsx @@ -0,0 +1,45 @@ +import { BaseBoxShapeUtil, HTMLContainer, TLBaseShape } from "tldraw"; + +type ITextCardShape = TLBaseShape< + "Textcard", + { w: number; h: number; content: string; extrainfo: string } +>; + +export class textCardUtil extends BaseBoxShapeUtil { + static override type = "Textcard" as const; + + getDefaultProps(): ITextCardShape["props"] { + return { + w: 100, + h: 50, + content: "", + extrainfo: "", + }; + } + + component(s: ITextCardShape) { + return ( + +
+

{s.props.content}

+

+ {s.props.extrainfo} +

+
+
+ ); + } + + indicator(shape: ITextCardShape) { + return ; + } +} diff --git a/apps/web/app/(canvas)/canvas/_canvas_comp/(components)/twitterCard.tsx b/apps/web/app/(canvas)/canvas/_canvas_comp/(components)/twitterCard.tsx new file mode 100644 index 00000000..c5582a98 --- /dev/null +++ b/apps/web/app/(canvas)/canvas/_canvas_comp/(components)/twitterCard.tsx @@ -0,0 +1,84 @@ +import { BaseBoxShapeUtil, HTMLContainer, TLBaseShape, toDomPrecision } from "tldraw"; + +type ITwitterCardShape = TLBaseShape< + "Twittercard", + { w: number; h: number; url: string } +>; + +export class twitterCardUtil extends BaseBoxShapeUtil { + static override type = "Twittercard" as const; + + getDefaultProps(): ITwitterCardShape["props"] { + return { + w: 500, + h: 550, + url: "", + }; + } + + component(s: ITwitterCardShape) { + return ( + + + + ); + } + + indicator(shape: ITwitterCardShape) { + return ; + } +} + +function TwitterPost({ + isInteractive, + width, + height, + url, +}: { + isInteractive: boolean; + width: number; + height: number; + url: string; +}) { + const link = (() => { + try { + const urlObj = new URL(url); + const path = urlObj.pathname; + return path; + } catch (error) { + console.error("Invalid URL", error); + return null; + } + })(); + + return ( +