diff options
| author | codetorso <[email protected]> | 2024-07-19 01:33:25 +0530 |
|---|---|---|
| committer | codetorso <[email protected]> | 2024-07-19 01:33:25 +0530 |
| commit | 9c4a36463239a355cdbfdd3fbb82ac8be4504ec6 (patch) | |
| tree | 563d6056daa38e3f40869126c5d22e263bda423f /apps/web/app | |
| parent | effect of disappearing text on input (diff) | |
| download | supermemory-9c4a36463239a355cdbfdd3fbb82ac8be4504ec6.tar.xz supermemory-9c4a36463239a355cdbfdd3fbb82ac8be4504ec6.zip | |
recent searches feature and some other cool stuff
Diffstat (limited to 'apps/web/app')
| -rw-r--r-- | apps/web/app/(dash)/home/filterSpaces.tsx | 37 | ||||
| -rw-r--r-- | apps/web/app/(dash)/home/heading.tsx | 28 | ||||
| -rw-r--r-- | apps/web/app/(dash)/home/page.tsx | 56 | ||||
| -rw-r--r-- | apps/web/app/(dash)/home/queryinput.tsx | 4 | ||||
| -rw-r--r-- | apps/web/app/actions/fetchers.ts | 30 |
5 files changed, 116 insertions, 39 deletions
diff --git a/apps/web/app/(dash)/home/filterSpaces.tsx b/apps/web/app/(dash)/home/filterSpaces.tsx index d0e4fa1e..061923d8 100644 --- a/apps/web/app/(dash)/home/filterSpaces.tsx +++ b/apps/web/app/(dash)/home/filterSpaces.tsx @@ -76,30 +76,31 @@ export function FilterSpaces({ </div> <CommandList className="z-10 translate-y-12 translate-x-5 opacity-0 absolute group-focus-within:opacity-100 transition-opacity p-2 rounded-lg max-w-64 bg-[#2C3338]"> <CommandGroup className="pointer-events-none opacity-0 group-focus-within:opacity-100 scale-50 scale-y-50 group-focus-within:scale-y-100 group-focus-within:scale-100 group-focus-within:pointer-events-auto transition-all origin-top"> - {initialSpaces.map((space) => ( - <CommandItem - className="text-[#eaeaea] data-[disabled]:opacity-90" - value={space.name} - key={space.id} - onSelect={() => handleSelect(space)} - > - <Check - className={`mr-2 h-4 w-4 ${selectedSpaces.some((v) => v.id === space.id) ? "opacity-100" : "opacity-0"}`} - /> - {space.name} - </CommandItem> - ))} + {initialSpaces.map((space) => { + if (!selectedSpaces.some((v) => v.id === space.id)) { + return ( + <CommandItem + className="text-[#eaeaea] data-[disabled]:opacity-90" + value={space.name} + key={space.id} + onSelect={() => handleSelect(space)} + > + <Check + className={`mr-2 h-4 w-4 ${selectedSpaces.some((v) => v.id === space.id) ? "opacity-100" : "opacity-0"}`} + /> + {space.name} + </CommandItem> + ); + } + })} </CommandGroup> </CommandList> </Command> </div> - {/* <button + <button type="submit" - className="h-12 w-12 rounded-[14px] all-center shrink-0 hover:brightness-125 outline-none bg-[#369DFD1A] p-3 active:scale-90" + className="rounded-lg bg-[#369DFD1A] p-3 transition-colors" > - <Image src={ArrowRightIcon} alt="Right arrow icon" /> - </button> */} - <button type="submit" className="rounded-lg bg-[#369DFD1A] p-3 transition-colors"> <Image src={ArrowRightIcon} alt="Enter" /> </button> </div> diff --git a/apps/web/app/(dash)/home/heading.tsx b/apps/web/app/(dash)/home/heading.tsx index 75a83dae..e50f509b 100644 --- a/apps/web/app/(dash)/home/heading.tsx +++ b/apps/web/app/(dash)/home/heading.tsx @@ -1,5 +1,5 @@ import { useEffect, useState } from "react"; -import { motion } from "framer-motion"; +import { AnimatePresence, motion } from "framer-motion"; const headings = [ "Unlock your digital brain", @@ -13,17 +13,23 @@ export function Heading({ queryPresent }: { queryPresent: boolean }) { const [showHeading, setShowHeading] = useState<number>(0); useEffect(() => { setShowHeading(Math.floor(Math.random() * headings.length)); - }, []); + }, [queryPresent]); return ( - <div className="h-[3.4rem] overflow-hidden text-white text-center"> - <motion.h1 - animate={{ opacity: queryPresent ? 0 : 1, y: queryPresent ? "20%" : 0 }} - className={`text-[2.45rem] font-semibold ${ - queryPresent ? "pointer-events-none" : "pointer-events-auto" - } transition-opacity`} - > - {headings[showHeading]} - </motion.h1> + <div className="h-[7rem] flex items-end justify-center overflow-hidden text-white"> + <AnimatePresence mode="popLayout"> + {!queryPresent && ( + <motion.h1 + initial={{ opacity: 0, y: "20%" }} + animate={{ opacity: 1, y: "0%" }} + exit={{ opacity: 0, y: "20%" }} + className={`text-[2.45rem] font-semibold ${ + queryPresent ? "pointer-events-none" : "pointer-events-auto" + } transition-opacity`} + > + {headings[showHeading]} + </motion.h1> + )} + </AnimatePresence> </div> ); } diff --git a/apps/web/app/(dash)/home/page.tsx b/apps/web/app/(dash)/home/page.tsx index 84ecf242..7572056a 100644 --- a/apps/web/app/(dash)/home/page.tsx +++ b/apps/web/app/(dash)/home/page.tsx @@ -1,12 +1,18 @@ "use client"; -import React, { useEffect, useState } from "react"; +import React, { Suspense, memo, use, useEffect, useState } from "react"; import QueryInput from "./queryinput"; -import { getSessionAuthToken, getSpaces } from "@/app/actions/fetchers"; +import { + getRecentChats, + getSessionAuthToken, + getSpaces, +} from "@/app/actions/fetchers"; import { useRouter } from "next/navigation"; import { createChatThread, linkTelegramToUser } from "@/app/actions/doers"; import { toast } from "sonner"; import { Heading } from "./heading"; +import { ArrowLongRightIcon } from "@heroicons/react/24/outline"; +import Link from "next/link"; const linkTelegram = async (telegramUser: string) => { const response = await linkTelegramToUser(telegramUser); @@ -23,7 +29,6 @@ function Page({ }: { searchParams: Record<string, string | string[] | undefined>; }) { - const { push } = useRouter(); const [spaces, setSpaces] = useState<{ id: number; name: string }[]>([]); @@ -32,7 +37,7 @@ function Page({ useEffect(() => { // telegram bot - const telegramUser = searchParams.extension as string + const telegramUser = searchParams.extension as string; if (telegramUser) { linkTelegram(telegramUser); } @@ -57,11 +62,11 @@ function Page({ }, []); return ( - <div className="max-w-3xl h-full justify-center flex mx-auto w-full flex-col px-2 md:px-0"> + <div className="max-w-3xl mt-[18vh] mx-auto w-full px-2 md:px-0"> <Heading queryPresent={queryPresent} /> - <div className="w-full pb-20 mt-12"> + <div className="w-full py-12"> <QueryInput - setQueryPresent={(t:boolean)=> setQueryPresent(t)} + setQueryPresent={(t: boolean) => setQueryPresent(t)} handleSubmit={async (q, spaces) => { if (q.length === 0) { toast.error("Query is required"); @@ -82,8 +87,45 @@ function Page({ initialSpaces={spaces} /> </div> + <History /> </div> ); } +const History = memo(() => { + const [chatThreads, setChatThreads] = useState(null); + + useEffect(() => { + (async () => { + const chatThreads = await getRecentChats(); + + setChatThreads(chatThreads); + })(); + }, []); + + if (!chatThreads){ + return <div>Loading</div>; + } + + if (!chatThreads.success || !chatThreads.data) { + return <div>Error fetching chat threads</div>; + } + + return ( + <div className="space-y-5"> + <h3 className="text-lg">Recent Searches</h3> + <ul className="text-base list-none space-y-3 text-[#b9b9b9]"> + {chatThreads.data.map((thread) => ( + <li className="flex items-center gap-2 truncate"> + <ArrowLongRightIcon className="h-5" />{" "} + <Link prefetch={false} href={`/chat/${thread.id}`}> + {thread.firstMessage} + </Link> + </li> + ))} + </ul> + </div> + ); +}); + export default Page; diff --git a/apps/web/app/(dash)/home/queryinput.tsx b/apps/web/app/(dash)/home/queryinput.tsx index 6767d5ea..930f34db 100644 --- a/apps/web/app/(dash)/home/queryinput.tsx +++ b/apps/web/app/(dash)/home/queryinput.tsx @@ -25,9 +25,9 @@ function QueryInput({ >([]); return ( - <div className={`${className}`}> + <div className={`w-full`}> <div - className={`bg-[#1F2428] overflow-hidden border-2 border-gray-700/50 shadow-md shadow-[#1d1d1dc7] rounded-3xl`} + className={`bg-[#1F2428] overflow-hidden border-2 border-gray-700/50 shadow-md shadow-[#1d1d1dc7] rounded-3xl`} > {/* input and action button */} <form diff --git a/apps/web/app/actions/fetchers.ts b/apps/web/app/actions/fetchers.ts index 1838ee1c..74df0b04 100644 --- a/apps/web/app/actions/fetchers.ts +++ b/apps/web/app/actions/fetchers.ts @@ -1,6 +1,6 @@ "use server"; -import { and, asc, eq, exists, inArray, not, or, sql } from "drizzle-orm"; +import { and, asc, desc, eq, exists, inArray, not, or, sql } from "drizzle-orm"; import { db } from "../../server/db"; import { canvas, @@ -228,6 +228,34 @@ export const getFullChatThread = async ( }; }; +export const getRecentChats = async (): ServerActionReturnType<ChatThread[]> => { + const data = await auth(); + + if (!data || !data.user || !data.user.id) { + redirect("/signin"); + return { error: "Not authenticated", success: false }; + } + + try { + const chatHistorys = await db.query.chatThreads.findMany({ + where: eq(chatThreads.userId, data.user.id), + orderBy: desc(chatThreads.createdAt), + limit: 4, + }); + + return { + success: true, + data: chatHistorys, + }; + } catch (e) { + return { + success: false, + error: (e as Error).message, + }; + } +}; + + export const getChatHistory = async (): ServerActionReturnType< ChatThread[] > => { |