diff options
| author | Dhravya <[email protected]> | 2024-06-29 09:34:28 -0500 |
|---|---|---|
| committer | Dhravya <[email protected]> | 2024-06-29 09:34:28 -0500 |
| commit | f420dc7616b2e66564a23881a9a2aec7f34df4e0 (patch) | |
| tree | bca7fb126a9aa0a2a945a4b53ff0ce34c0d3d429 | |
| parent | more tweaks (diff) | |
| download | supermemory-f420dc7616b2e66564a23881a9a2aec7f34df4e0.tar.xz supermemory-f420dc7616b2e66564a23881a9a2aec7f34df4e0.zip | |
schema
| -rw-r--r-- | apps/web/app/(dash)/dynamicisland.tsx | 315 | ||||
| -rw-r--r-- | apps/web/app/(dash)/layout.tsx | 2 | ||||
| -rw-r--r-- | apps/web/app/(dash)/memories/page.tsx | 93 | ||||
| -rw-r--r-- | apps/web/app/(dash)/menu.tsx | 5 | ||||
| -rw-r--r-- | apps/web/app/actions/fetchers.ts | 11 | ||||
| -rw-r--r-- | apps/web/app/actions/types.ts | 1 | ||||
| -rw-r--r-- | apps/web/migrations/0000_classy_speed_demon.sql | 113 | ||||
| -rw-r--r-- | apps/web/migrations/meta/0000_snapshot.json | 685 | ||||
| -rw-r--r-- | apps/web/migrations/meta/_journal.json | 13 | ||||
| -rw-r--r-- | apps/web/server/db/schema.ts | 1 | ||||
| -rw-r--r-- | package.json | 6 |
11 files changed, 877 insertions, 368 deletions
diff --git a/apps/web/app/(dash)/dynamicisland.tsx b/apps/web/app/(dash)/dynamicisland.tsx deleted file mode 100644 index 8b1b4633..00000000 --- a/apps/web/app/(dash)/dynamicisland.tsx +++ /dev/null @@ -1,315 +0,0 @@ -"use client"; - -import { AddIcon } from "@repo/ui/icons"; -import Image from "next/image"; - -import { AnimatePresence, useMotionValueEvent, useScroll } from "framer-motion"; -import { useActionState, useEffect, useRef, useState } from "react"; -import { motion } from "framer-motion"; -import { Label } from "@repo/ui/shadcn/label"; -import { Input } from "@repo/ui/shadcn/input"; -import { Textarea } from "@repo/ui/shadcn/textarea"; -import { createMemory, createSpace } from "../actions/doers"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@repo/ui/shadcn/select"; -import { Space } from "../actions/types"; -import { getSpaces } from "../actions/fetchers"; -import { toast } from "sonner"; -import { useFormStatus } from "react-dom"; - -export function DynamicIsland() { - const { scrollYProgress } = useScroll(); - const [visible, setVisible] = useState(true); - - useMotionValueEvent(scrollYProgress, "change", (current) => { - if (typeof current === "number") { - let direction = current! - scrollYProgress.getPrevious()!; - - if (direction < 0 || direction === 1) { - setVisible(true); - } else { - setVisible(false); - } - } - }); - - return ( - <div className=""> - <AnimatePresence mode="wait"> - <motion.div - initial={{ - opacity: 1, - y: -150, - }} - animate={{ - y: visible ? 0 : -150, - opacity: visible ? 1 : 0, - }} - transition={{ - duration: 0.2, - }} - className="flex flex-col items-center" - > - <DynamicIslandContent /> - </motion.div> - </AnimatePresence> - </div> - ); -} - -export default DynamicIsland; - -function DynamicIslandContent() { - const [show, setshow] = useState(true); - function cancelfn() { - setshow(true); - } - - const lastBtn = useRef<string>(); - - useEffect(() => { - document.addEventListener("keydown", (e) => { - if (e.key === "Escape") { - setshow(true); - } - - if (e.key === "a" && lastBtn.current === "Alt") { - setshow(false); - } - lastBtn.current = e.key; - }); - }, []); - return ( - <> - {show ? ( - <button - onClick={() => setshow(!show)} - className="bg-secondary p-2 text-[#989EA4] rounded-full flex items-center justify-between gap-2 px-4 h-10 pr-5 z-[999] shadow-md" - > - <Image src={AddIcon} alt="add icon" /> - Add content - </button> - ) : ( - <ToolBar cancelfn={cancelfn} /> - )} - </> - ); -} - -const fakeitems = ["page", "spaces"]; - -function ToolBar({ cancelfn }: { cancelfn: () => void }) { - const [spaces, setSpaces] = useState<Space[]>([]); - - const [index, setIndex] = useState(0); - - useEffect(() => { - (async () => { - let spaces = await getSpaces(); - - if (!spaces.success || !spaces.data) { - toast.warning("Unable to get spaces", { - richColors: true, - }); - setSpaces([]); - return; - } - setSpaces(spaces.data); - })(); - }, []); - - return ( - <AnimatePresence mode="wait"> - <motion.div - initial={{ - opacity: 0, - y: 20, - }} - animate={{ - y: 0, - opacity: 1, - }} - exit={{ - opacity: 0, - y: 20, - }} - transition={{ - duration: 0.2, - }} - className="flex flex-col items-center" - > - <div className="bg-secondary py-[.35rem] px-[.6rem] rounded-2xl"> - <HoverEffect - items={fakeitems} - index={index} - indexFn={(i) => setIndex(i)} - /> - </div> - {index === 1 ? ( - <SpaceForm cancelfn={cancelfn} /> - ) : ( - <PageForm cancelfn={cancelfn} spaces={spaces} /> - )} - </motion.div> - </AnimatePresence> - ); -} - -export const HoverEffect = ({ - items, - index, - indexFn, -}: { - items: string[]; - index: number; - indexFn: (i: number) => void; -}) => { - return ( - <div className={"flex"}> - {items.map((item, idx) => ( - <button - key={idx} - className="relative block h-full w-full px-2 py-1" - onClick={() => indexFn(idx)} - > - <AnimatePresence> - {index === idx && ( - <motion.span - className="absolute inset-0 block h-full w-full rounded-xl bg-[#2B3237]" - layoutId="hoverBackground" - initial={{ opacity: 0 }} - animate={{ - opacity: 1, - transition: { duration: 0.15 }, - }} - exit={{ - opacity: 0, - transition: { duration: 0.15, delay: 0.2 }, - }} - /> - )} - </AnimatePresence> - <h3 className="text-[#858B92] z-50 relative">{item}</h3> - </button> - ))} - </div> - ); -}; - -function SpaceForm({ cancelfn }: { cancelfn: () => void }) { - return ( - <form - action={createSpace} - className="bg-secondary border border-muted-foreground px-4 py-3 rounded-2xl mt-2 flex flex-col gap-3" - > - <div> - <Label className="text-[#858B92]" htmlFor="name"> - Name - </Label> - <Input - className="bg-[#2B3237] focus-visible:ring-0 border-none focus-visible:ring-offset-0" - id="name" - name="name" - /> - </div> - <div className="flex justify-between"> - {/* <a className="text-blue-500" href=""> - pull from store - </a> */} - {/* <div - onClick={cancelfn} - className="bg-[#2B3237] px-2 py-1 rounded-xl cursor-pointer" - > - cancel - </div> */} - <button - type="submit" - className="bg-[#2B3237] px-2 py-1 rounded-xl cursor-pointer" - > - Submit - </button> - </div> - </form> - ); -} - -function PageForm({ - cancelfn, - spaces, -}: { - cancelfn: () => void; - spaces: Space[]; -}) { - const [loading, setLoading] = useState(false); - - const { pending } = useFormStatus(); - return ( - <form - action={async (e: FormData) => { - const content = e.get("content")?.toString(); - const space = e.get("space")?.toString(); - - toast.info("Creating memory..."); - - if (!content) { - toast.error("Content is required"); - return; - } - cancelfn(); - const cont = await createMemory({ - content: content, - spaces: space ? [space] : undefined, - }); - - if (cont.success) { - toast.success("Memory created"); - } else { - toast.error("Memory creation failed"); - } - }} - className="bg-secondary border border-muted-foreground px-4 py-3 rounded-2xl mt-2 flex flex-col gap-3 w-[100vw] md:w-[400px]" - > - <div> - <Label className="text-[#858B92]" htmlFor="space"> - Space - </Label> - <Select name="space"> - <SelectTrigger> - <SelectValue placeholder="Space" /> - </SelectTrigger> - <SelectContent className="bg-secondary text-white"> - {spaces.map((space) => ( - <SelectItem key={space.id} value={space.id.toString()}> - {space.name} - </SelectItem> - ))} - </SelectContent> - </Select> - </div> - <div> - <Label className="text-[#858B92]" htmlFor="name"> - Resource (URL or content) - </Label> - <Textarea - className="bg-[#2B3237] focus-visible:ring-0 border-none focus-visible:ring-offset-0" - id="input" - name="content" - placeholder="Start typing a note or paste a URL here. I'll remember it." - /> - </div> - <div className="flex justify-end"> - <button - type="submit" - className="bg-[#2B3237] px-2 py-1 rounded-xl cursor-pointer" - > - Submit - </button> - </div> - </form> - ); -} diff --git a/apps/web/app/(dash)/layout.tsx b/apps/web/app/(dash)/layout.tsx index 385e46e0..5c7921e4 100644 --- a/apps/web/app/(dash)/layout.tsx +++ b/apps/web/app/(dash)/layout.tsx @@ -20,7 +20,7 @@ async function Layout({ children }: { children: React.ReactNode }) { <div className="relative flex justify-center z-40 pointer-events-none"> <div - className="absolute -z-10 left-0 top-[10%] h-32 w-[90%] overflow-x-hidden bg-[rgb(54,157,253)] bg-opacity-70 blur-[337.4px]" + className="absolute -z-10 left-0 top-[10%] h-32 w-[90%] overflow-x-hidden bg-[rgb(54,157,253)] bg-opacity-100 md:bg-opacity-70 blur-[337.4px]" style={{ transform: "rotate(-30deg)" }} /> </div> diff --git a/apps/web/app/(dash)/memories/page.tsx b/apps/web/app/(dash)/memories/page.tsx index ff746d1d..ca0417b6 100644 --- a/apps/web/app/(dash)/memories/page.tsx +++ b/apps/web/app/(dash)/memories/page.tsx @@ -2,10 +2,11 @@ import { getAllUserMemoriesAndSpaces } from "@/app/actions/fetchers"; import { Space } from "@/app/actions/types"; -import { Content } from "@/server/db/schema"; +import { Content, StoredSpace } from "@/server/db/schema"; import { NextIcon, SearchIcon, UrlIcon } from "@repo/ui/icons"; import Image from "next/image"; -import React, { useEffect, useState } from "react"; +import React, { useEffect, useMemo, useState } from "react"; +import Masonry, { ResponsiveMasonry } from "react-responsive-masonry"; function Page() { const [filter, setFilter] = useState("All"); @@ -15,9 +16,34 @@ function Page() { const [memoriesAndSpaces, setMemoriesAndSpaces] = useState<{ memories: Content[]; - spaces: Space[]; + spaces: StoredSpace[]; }>({ memories: [], spaces: [] }); + // Sort Both memories and spaces by their savedAt and createdAt dates respectfully. + // The output should be just one single list of items + // And it will look something like { item: "memory" | "space", date: Date, data: Content | StoredSpace } + const sortedItems = useMemo(() => { + // Merge the lists + const unifiedItems = [ + ...memoriesAndSpaces.memories.map((memory) => ({ + item: "memory", + date: new Date(memory.savedAt), // Assuming savedAt is a string date + data: memory, + })), + ...memoriesAndSpaces.spaces.map((space) => ({ + item: "space", + date: new Date(space.createdAt), // Assuming createdAt is a string date + data: space, + })), + ].map((item) => ({ + ...item, + date: Number(item.date), // Convert the date to a number + })); + + // Sort the merged list + return unifiedItems.sort((a, b) => a.date - b.date); + }, [memoriesAndSpaces.memories, memoriesAndSpaces.spaces]); + useEffect(() => { (async () => { const { success, data } = await getAllUserMemoriesAndSpaces(); @@ -33,33 +59,12 @@ function Page() { </h2> <div className="flex flex-col gap-4"> - <div className="w-full relative"> - <input - type="text" - className=" w-full py-3 rounded-md text-lg pl-8 bg-[#1F2428] outline-none" - placeholder="search here..." - /> - <Image - className="absolute top-1/2 -translate-y-1/2 left-2" - src={SearchIcon} - alt="Search icon" - /> - </div> - - <Filters filter={filter} setFilter={setFilterfn} /> - </div> - <div> - <div className="text-[#B3BCC5]">Spaces</div> - {memoriesAndSpaces.spaces.map((space) => ( - <TabComponent title={space.name} description={space.id.toString()} /> - ))} - </div> - - <div> - <div className="text-[#B3BCC5]">Pages</div> - {memoriesAndSpaces.memories.map((memory) => ( - <LinkComponent title={memory.title ?? "No title"} url={memory.url} /> - ))} + <ResponsiveMasonry columnsCountBreakPoints={{ 350: 1, 750: 2, 900: 3 }}> + <Masonry> + <div>Item 1</div> + <div>Item 2</div> + </Masonry> + </ResponsiveMasonry> </div> </div> ); @@ -90,18 +95,22 @@ function TabComponent({ ); } -function LinkComponent({ title, url }: { title: string; url: string }) { +function LinkComponent({ + type, + content, + title, + url, +}: { + type: string; + content: string; + title: string; + url: string; +}) { return ( - <div className="flex items-center my-6"> - <div> - <div className="h-12 w-12 bg-[#1F2428] flex justify-center items-center rounded-md"> - <Image src={UrlIcon} alt="Url icon" /> - </div> - </div> - <div className="grow px-4"> - <div className="text-lg text-[#fff]">{title}</div> - <div>{url}</div> - </div> + <div className="w-full"> + <div className="text-lg text-[#fff]">{title}</div> + <div>{content}</div> + <div>{url}</div> </div> ); } @@ -120,7 +129,7 @@ function Filters({ return ( <div onClick={() => setFilter(i)} - className={`transition px-6 py-2 rounded-xl ${i === filter ? "bg-[#21303D] text-[#369DFD]" : "text-[#B3BCC5] bg-[#1F2428] hover:bg-[#1f262d] hover:text-[#76a3cc]"}`} + className={`transition px-6 py-2 rounded-xl bg-border ${i === filter ? " text-[#369DFD]" : "text-[#B3BCC5] bg-secondary hover:bg-secondary hover:text-[#76a3cc]"}`} > {i} </div> diff --git a/apps/web/app/(dash)/menu.tsx b/apps/web/app/(dash)/menu.tsx index 0622ddc0..d636c8e1 100644 --- a/apps/web/app/(dash)/menu.tsx +++ b/apps/web/app/(dash)/menu.tsx @@ -37,9 +37,10 @@ import { InformationCircleIcon } from "@heroicons/react/24/outline"; import { createMemory, createSpace } from "../actions/doers"; import { Input } from "@repo/ui/shadcn/input"; import ComboboxWithCreate from "@repo/ui/shadcn/combobox"; +import { StoredSpace } from "@/server/db/schema"; function Menu() { - const [spaces, setSpaces] = useState<Space[]>([]); + const [spaces, setSpaces] = useState<StoredSpace[]>([]); useEffect(() => { (async () => { @@ -269,6 +270,8 @@ function Menu() { { name: spaceName, id: creationTask.data!, + createdAt: new Date().toISOString(), + user: null, }, ]); setSelectedSpaces((prev) => [ diff --git a/apps/web/app/actions/fetchers.ts b/apps/web/app/actions/fetchers.ts index 664c20ac..558ef3de 100644 --- a/apps/web/app/actions/fetchers.ts +++ b/apps/web/app/actions/fetchers.ts @@ -9,6 +9,7 @@ import { Content, contentToSpace, storedContent, + StoredSpace, users, } from "../../server/db/schema"; import { ServerActionReturnType, Space } from "./types"; @@ -18,7 +19,7 @@ import { ChatHistory as ChatHistoryType } from "../../server/db/schema"; import { z } from "zod"; import { redirect } from "next/navigation"; -export const getSpaces = async (): ServerActionReturnType<Space[]> => { +export const getSpaces = async (): ServerActionReturnType<StoredSpace[]> => { const data = await auth(); if (!data || !data.user) { @@ -30,11 +31,7 @@ export const getSpaces = async (): ServerActionReturnType<Space[]> => { where: eq(users, data.user.id), }); - const spacesWithoutUser = spaces.map((space) => { - return { ...space, user: undefined }; - }); - - return { success: true, data: spacesWithoutUser }; + return { success: true, data: spaces }; }; export const getAllMemories = async ( @@ -77,7 +74,7 @@ export const getAllMemories = async ( }; export const getAllUserMemoriesAndSpaces = async (): ServerActionReturnType<{ - spaces: Space[]; + spaces: StoredSpace[]; memories: Content[]; }> => { const data = await auth(); diff --git a/apps/web/app/actions/types.ts b/apps/web/app/actions/types.ts index 5c5afc5c..0b1aaece 100644 --- a/apps/web/app/actions/types.ts +++ b/apps/web/app/actions/types.ts @@ -2,6 +2,7 @@ export type Space = { id: number; name: string; numberOfMemories?: number; + createdAt?: string; }; export type ServerActionReturnType<T> = Promise<{ diff --git a/apps/web/migrations/0000_classy_speed_demon.sql b/apps/web/migrations/0000_classy_speed_demon.sql new file mode 100644 index 00000000..a1885925 --- /dev/null +++ b/apps/web/migrations/0000_classy_speed_demon.sql @@ -0,0 +1,113 @@ +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), + `createdAt` integer NOT NULL, + 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 `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/meta/0000_snapshot.json b/apps/web/migrations/meta/0000_snapshot.json new file mode 100644 index 00000000..291848ad --- /dev/null +++ b/apps/web/migrations/meta/0000_snapshot.json @@ -0,0 +1,685 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "6988522d-8117-484d-b52a-94c0fbd75140", + "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": {} + }, + "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 + }, + "createdAt": { + "name": "createdAt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "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": {} + } +} diff --git a/apps/web/migrations/meta/_journal.json b/apps/web/migrations/meta/_journal.json new file mode 100644 index 00000000..552ae12f --- /dev/null +++ b/apps/web/migrations/meta/_journal.json @@ -0,0 +1,13 @@ +{ + "version": "6", + "dialect": "sqlite", + "entries": [ + { + "idx": 0, + "version": "6", + "when": 1719671614406, + "tag": "0000_classy_speed_demon", + "breakpoints": true + } + ] +} diff --git a/apps/web/server/db/schema.ts b/apps/web/server/db/schema.ts index 69372a35..b5d9bbdb 100644 --- a/apps/web/server/db/schema.ts +++ b/apps/web/server/db/schema.ts @@ -151,6 +151,7 @@ export const space = createTable( user: text("user", { length: 255 }).references(() => users.id, { onDelete: "cascade", }), + createdAt: int("createdAt", { mode: "timestamp" }).notNull(), }, (space) => ({ nameIdx: index("spaces_name_idx").on(space.name), diff --git a/package.json b/package.json index 5d6e79ed..223b7584 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.21.2", "eslint-plugin-next-on-pages": "^1.11.3", "lint-staged": "^15.2.5", "postcss": "^8.4.38", @@ -68,12 +68,13 @@ "@radix-ui/react-toast": "^1.1.5", "@radix-ui/react-tooltip": "^1.1.2", "@tldraw/assets": "^2.2.0", + "@types/react-responsive-masonry": "^2.1.3", "@types/readline-sync": "^1.4.8", "ai": "^3.1.14", "aws4fetch": "^1.0.18", "cheerio": "^1.0.0-rc.12", "compromise": "^14.13.0", - "drizzle-orm": "^0.31.2", + "drizzle-orm": "0.30.0", "framer-motion": "^11.2.6", "geist": "^1.3.0", "grammy": "^1.25.1", @@ -86,6 +87,7 @@ "react-dropzone": "^14.2.3", "react-hook-form": "^7.51.5", "react-markdown": "^9.0.1", + "react-responsive-masonry": "^2.2.1", "react-tweet": "^3.2.1", "rehype-highlight": "^7.0.0", "rehype-katex": "^7.0.0", |