diff options
| author | Dhravya <[email protected]> | 2024-04-05 18:05:55 -0700 |
|---|---|---|
| committer | Dhravya <[email protected]> | 2024-04-05 18:05:55 -0700 |
| commit | 0375128bd96e538051f4ac58a9003fa15198f06b (patch) | |
| tree | 52f346abfe5ed782bed8a1e259327c4a182abe12 /apps/web/src | |
| parent | (NEEDS MIGRATION)made sidebar functional (diff) | |
| parent | remove unused imports (diff) | |
| download | supermemory-0375128bd96e538051f4ac58a9003fa15198f06b.tar.xz supermemory-0375128bd96e538051f4ac58a9003fa15198f06b.zip | |
changed data structure to fetch spaces
Diffstat (limited to 'apps/web/src')
| -rw-r--r-- | apps/web/src/app/page.tsx | 4 | ||||
| -rw-r--r-- | apps/web/src/app/ui/content.tsx | 15 | ||||
| -rw-r--r-- | apps/web/src/app/ui/page.tsx | 18 | ||||
| -rw-r--r-- | apps/web/src/components/Main.tsx | 58 | ||||
| -rw-r--r-- | apps/web/src/components/Sidebar/FilterCombobox.tsx | 156 | ||||
| -rw-r--r-- | apps/web/src/components/Sidebar/MemoriesBar.tsx | 76 | ||||
| -rw-r--r-- | apps/web/src/components/Sidebar/PagesItem.tsx | 189 | ||||
| -rw-r--r-- | apps/web/src/components/Sidebar/index.tsx | 53 | ||||
| -rw-r--r-- | apps/web/src/contexts/MemoryContext.tsx | 39 |
9 files changed, 161 insertions, 447 deletions
diff --git a/apps/web/src/app/page.tsx b/apps/web/src/app/page.tsx index c25de8c7..221ce2b4 100644 --- a/apps/web/src/app/page.tsx +++ b/apps/web/src/app/page.tsx @@ -61,13 +61,9 @@ export default async function Home() { .where(eq(storedContent.user, userData.id)) .all(); - console.log(contents); - const collectedSpaces = contents.length > 0 ? await transformContent(contents) : []; - console.log('collected', collectedSpaces); - return ( <div className="flex w-screen"> <Sidebar selectChange={setSelectedItem} spaces={collectedSpaces} /> diff --git a/apps/web/src/app/ui/content.tsx b/apps/web/src/app/ui/content.tsx new file mode 100644 index 00000000..8bfebcb9 --- /dev/null +++ b/apps/web/src/app/ui/content.tsx @@ -0,0 +1,15 @@ +"use client"; +import Main from "@/components/Main"; +import Sidebar from "@/components/Sidebar/index"; +import { useState } from "react"; + +export default function Content() { + const [selectedItem, setSelectedItem] = useState<string | null>(null); + + return ( + <div className="flex w-screen"> + <Sidebar selectChange={setSelectedItem} /> + <Main sidebarOpen={selectedItem !== null} /> + </div> + ); +} diff --git a/apps/web/src/app/ui/page.tsx b/apps/web/src/app/ui/page.tsx index b19aa4e0..35175334 100644 --- a/apps/web/src/app/ui/page.tsx +++ b/apps/web/src/app/ui/page.tsx @@ -1,18 +1,10 @@ -import Main from '@/components/Main'; -import Sidebar from '@/components/Sidebar/index'; -import { cookies } from 'next/headers'; +import { MemoryProvider } from "@/contexts/MemoryContext"; +import Content from "./content"; export default function Home() { - const selectedItem = cookies().get('selectedItem')?.value; - const setSelectedItem = async (selectedItem: string | null) => { - 'use server'; - cookies().set('selectedItem', selectedItem!); - }; - return ( - <div className="flex w-screen"> - {/* <Sidebar selectChange={setSelectedItem} spaces={spaces} /> */} - <Main sidebarOpen={selectedItem !== null} /> - </div> + <MemoryProvider spaces={[]}> + <Content /> + </MemoryProvider> ); } diff --git a/apps/web/src/components/Main.tsx b/apps/web/src/components/Main.tsx index dcbd3612..e9a7071d 100644 --- a/apps/web/src/components/Main.tsx +++ b/apps/web/src/components/Main.tsx @@ -1,34 +1,32 @@ -'use client'; -import { useEffect, useRef, useState } from 'react'; -import { FilterCombobox } from './Sidebar/FilterCombobox'; -import { Textarea2 } from './ui/textarea'; -import { ArrowRight } from 'lucide-react'; -import { MemoryDrawer } from './MemoryDrawer'; -import useViewport from '@/hooks/useViewport'; -import { motion } from 'framer-motion'; -import { cn } from '@/lib/utils'; +"use client"; +import { useEffect, useRef, useState } from "react"; +import { FilterCombobox } from "./Sidebar/FilterCombobox"; +import { Textarea2 } from "./ui/textarea"; +import { ArrowRight } from "lucide-react"; +import { MemoryDrawer } from "./MemoryDrawer"; +import useViewport from "@/hooks/useViewport"; +import { motion } from "framer-motion"; +import { cn } from "@/lib/utils"; + +function supportsDVH() { + try { + return CSS.supports("height: 100dvh"); + } catch { + return false; + } +} export default function Main({ sidebarOpen }: { sidebarOpen: boolean }) { const [hide, setHide] = useState(false); - const [value, setValue] = useState(''); + const [value, setValue] = useState(""); const { width } = useViewport(); const textArea = useRef<HTMLTextAreaElement>(null); const main = useRef<HTMLDivElement>(null); - console.log('main px', sidebarOpen); - useEffect(() => { function onResize() { if (!main.current || !window.visualViewport) return; - // setValue( - // (prev) => - // prev + - // " changed to " + - // window.visualViewport?.height + - // " " + - // window.innerHeight, - // ); if ( window.visualViewport.height < window.innerHeight + 20 && window.visualViewport.height > window.innerHeight - 20 @@ -41,9 +39,9 @@ export default function Main({ sidebarOpen }: { sidebarOpen: boolean }) { } } - window.visualViewport?.addEventListener('resize', onResize); + window.visualViewport?.addEventListener("resize", onResize); return () => { - window.visualViewport?.removeEventListener('resize', onResize); + window.visualViewport?.removeEventListener("resize", onResize); }; }, []); @@ -52,24 +50,20 @@ export default function Main({ sidebarOpen }: { sidebarOpen: boolean }) { data-sidebar-open={sidebarOpen} ref={main} className={cn( - "sidebar flex w-full flex-col items-end justify-center gap-5 px-5 pt-5 transition-[padding-left,padding-top,padding-right] delay-200 duration-200 md:items-center md:px-72 [&[data-sidebar-open='true']]:pr-10 [&[data-sidebar-open='true']]:delay-0 md:[&[data-sidebar-open='true']]:pl-[calc(2.5rem+30vw)]", - hide - ? 'pb-5' - : CSS.supports('height: 100dvh') - ? 'pb-[13vh]' - : 'pb-[20vh]', + "sidebar flex w-full flex-col items-end justify-center gap-5 px-5 pt-5 transition-[padding-left,padding-top,padding-right] delay-200 duration-200 md:items-center md:gap-10 md:px-72 [&[data-sidebar-open='true']]:pr-10 [&[data-sidebar-open='true']]:delay-0 md:[&[data-sidebar-open='true']]:pl-[calc(2.5rem+30vw)]", + hide ? "pb-5" : supportsDVH() ? "pb-[13vh]" : "pb-[20vh]", )} > - <h1 className="text-rgray-11 mt-auto w-full text-center text-3xl"> + <h1 className="text-rgray-11 mt-auto w-full text-center text-3xl md:mt-0"> Ask your Second brain </h1> <Textarea2 ref={textArea} - className="mt-auto h-max max-h-[30em] min-h-[3em] resize-y flex-row items-start justify-center overflow-auto py-5 md:h-[20vh] md:resize-none md:flex-col md:items-center md:justify-center md:p-2 md:pb-2 md:pt-2" + className="mt-auto h-max max-h-[30em] min-h-[3em] resize-y flex-row items-start justify-center overflow-auto py-5 md:mt-0 md:h-[20vh] md:resize-none md:flex-col md:items-center md:justify-center md:p-2 md:pb-2 md:pt-2" textAreaProps={{ - placeholder: 'Ask your SuperMemory...', + placeholder: "Ask your SuperMemory...", className: - 'h-auto overflow-auto md:h-full md:resize-none text-lg py-0 px-2 md:py-0 md:p-5 resize-y text-rgray-11 w-full min-h-[1em]', + "h-auto overflow-auto md:h-full md:resize-none text-lg py-0 px-2 md:py-0 md:p-5 resize-y text-rgray-11 w-full min-h-[1em]", value, autoFocus: true, onChange: (e) => setValue(e.target.value), diff --git a/apps/web/src/components/Sidebar/FilterCombobox.tsx b/apps/web/src/components/Sidebar/FilterCombobox.tsx index d22e8d8c..ae433c95 100644 --- a/apps/web/src/components/Sidebar/FilterCombobox.tsx +++ b/apps/web/src/components/Sidebar/FilterCombobox.tsx @@ -20,141 +20,17 @@ import { } from "@/components/ui/popover"; import { SpaceIcon } from "@/assets/Memories"; import { AnimatePresence, LayoutGroup, motion } from "framer-motion"; - -const spaces = [ - { - value: "1", - label: "Cool Tech", - }, - { - value: "2", - label: "Cool Courses", - }, - { - value: "3", - label: "Cool Libraries", - }, - { - value: "4", - label: "Cool People", - }, - { - value: "5", - label: "Cool Projects", - }, - { - value: "6", - label: "Cool Tools", - }, - { - value: "7", - label: "Cool Websites", - }, - { - value: "8", - label: "Cool Books", - }, - { - value: "9", - label: "Cool Videos", - }, - { - value: "10", - label: "Cool Podcasts", - }, - { - value: "11", - label: "Cool Articles", - }, - { - value: "12", - label: "Cool Blogs", - }, - { - value: "13", - label: "Cool News", - }, - { - value: "14", - label: "Cool Forums", - }, - { - value: "15", - label: "Cool Communities", - }, - { - value: "16", - label: "Cool Events", - }, - { - value: "17", - label: "Cool Jobs", - }, - { - value: "18", - label: "Cool Companies", - }, - { - value: "19", - label: "Cool Startups", - }, - { - value: "20", - label: "Cool Investors", - }, - { - value: "21", - label: "Cool Funds", - }, - { - value: "22", - label: "Cool Incubators", - }, - { - value: "23", - label: "Cool Accelerators", - }, - { - value: "24", - label: "Cool Hackathons", - }, - { - value: "25", - label: "Cool Conferences", - }, - { - value: "26", - label: "Cool Workshops", - }, - { - value: "27", - label: "Cool Seminars", - }, - { - value: "28", - label: "Cool Webinars", - }, - { - value: "29", - label: "Cool Courses", - }, - { - value: "30", - label: "Cool Bootcamps", - }, - { - value: "31", - label: "Cool Certifications", - }, -]; +import { useMemory } from "@/contexts/MemoryContext"; export interface Props extends React.ButtonHTMLAttributes<HTMLButtonElement> {} export function FilterCombobox({ className, ...props }: Props) { + const { spaces, addSpace } = useMemory(); + const [open, setOpen] = React.useState(false); - const [values, setValues] = React.useState<string[]>([]); + const [values, setValues] = React.useState<number[]>([]); - const sortedSpaces = spaces.sort(({ value: a }, { value: b }) => + const sortedSpaces = spaces.sort(({ id: a }, { id: b }) => values.includes(a) && !values.includes(b) ? -1 : values.includes(b) && !values.includes(a) @@ -192,8 +68,8 @@ export function FilterCombobox({ className, ...props }: Props) { <Command filter={(val, search) => spaces - .find((s) => s.value === val) - ?.label.toLowerCase() + .find((s) => s.id.toString() === val) + ?.title.toLowerCase() .includes(search.toLowerCase().trim()) ? 1 : 0 @@ -206,13 +82,13 @@ export function FilterCombobox({ className, ...props }: Props) { <CommandGroup> {sortedSpaces.map((space) => ( <CommandItem - key={space.value} - value={space.value} + key={space.id} + value={space.id.toString()} onSelect={(val) => { setValues((prev) => - prev.includes(val) - ? prev.filter((v) => v !== val) - : [...prev, val], + prev.includes(parseInt(val)) + ? prev.filter((v) => v !== parseInt(val)) + : [...prev, parseInt(val)], ); }} asChild @@ -222,14 +98,14 @@ export function FilterCombobox({ className, ...props }: Props) { animate={{ opacity: 1, transition: { delay: 0.05 } }} transition={{ duration: 0.15 }} layout - layoutId={`space-combobox-${space.value}`} + layoutId={`space-combobox-${space.id}`} className="text-rgray-11" > <SpaceIcon className="mr-2 h-4 w-4" /> - {space.label} - {values.includes(space.value)} + {space.title} + {values.includes(space.id)} <Check - data-state-on={values.includes(space.value)} + data-state-on={values.includes(space.id)} className={cn( "on:opacity-100 ml-auto h-4 w-4 opacity-0", )} diff --git a/apps/web/src/components/Sidebar/MemoriesBar.tsx b/apps/web/src/components/Sidebar/MemoriesBar.tsx index 889a3ab7..c889e43d 100644 --- a/apps/web/src/components/Sidebar/MemoriesBar.tsx +++ b/apps/web/src/components/Sidebar/MemoriesBar.tsx @@ -1,41 +1,31 @@ -import { useAutoAnimate } from '@formkit/auto-animate/react'; +import { useAutoAnimate } from "@formkit/auto-animate/react"; import { MemoryWithImage, MemoryWithImages3, MemoryWithImages2, -} from '@/assets/MemoryWithImages'; -import { type CollectedSpaces } from '../../../types/memory'; -import { InputWithIcon } from '../ui/input'; +} from "@/assets/MemoryWithImages"; +import { type CollectedSpaces } from "../../../types/memory"; +import { InputWithIcon } from "../ui/input"; import { ArrowUpRight, Edit3, MoreHorizontal, Search, Trash2, -} from 'lucide-react'; +} from "lucide-react"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, -} from '../ui/dropdown-menu'; -import { - animate, - AnimatePresence, - LayoutGroup, - motion, - useAnimate, - Variant, -} from 'framer-motion'; -import { useRef, useState } from 'react'; - +} from "../ui/dropdown-menu"; +import { useState } from "react"; +import { Variant, useAnimate, motion } from "framer-motion"; export function MemoriesBar({ spaces }: { spaces: CollectedSpaces[] }) { const [parent, enableAnimations] = useAutoAnimate(); const [currentSpaces, setCurrentSpaces] = useState(spaces); - console.log('currentSpaces: ', currentSpaces); - return ( <div className="text-rgray-11 flex w-full flex-col items-start py-8 text-left"> <div className="w-full px-8"> @@ -67,8 +57,8 @@ export function MemoriesBar({ spaces }: { spaces: CollectedSpaces[] }) { const SpaceExitVariant: Variant = { opacity: 0, scale: 0, - borderRadius: '50%', - background: 'var(--gray-1)', + borderRadius: "50%", + background: "var(--gray-1)", transition: { duration: 0.2, }, @@ -93,22 +83,22 @@ export function SpaceItem({ <SpaceMoreButton onDelete={() => { if (!itemRef.current) return; - const trash = document.querySelector('#trash')! as HTMLDivElement; - const trashBin = document.querySelector('#trash-button')!; + const trash = document.querySelector("#trash")! as HTMLDivElement; + const trashBin = document.querySelector("#trash-button")!; const trashRect = trashBin.getBoundingClientRect(); const scopeRect = itemRef.current.getBoundingClientRect(); - const el = document.createElement('div'); - el.style.position = 'fixed'; - el.style.top = '0'; - el.style.left = '0'; - el.style.width = '15px'; - el.style.height = '15px'; - el.style.backgroundColor = 'var(--gray-7)'; - el.style.zIndex = '60'; - el.style.borderRadius = '50%'; - el.style.transform = 'scale(5)'; - el.style.opacity = '0'; - trash.dataset['open'] = 'true'; + const el = document.createElement("div"); + el.style.position = "fixed"; + el.style.top = "0"; + el.style.left = "0"; + el.style.width = "15px"; + el.style.height = "15px"; + el.style.backgroundColor = "var(--gray-7)"; + el.style.zIndex = "60"; + el.style.borderRadius = "50%"; + el.style.transform = "scale(5)"; + el.style.opacity = "0"; + trash.dataset["open"] = "true"; const initial = { x: scopeRect.left + scopeRect.width / 2, y: scopeRect.top + scopeRect.height / 2, @@ -133,35 +123,35 @@ export function SpaceItem({ animateItem(itemRef.current, SpaceExitVariant, { duration: 0.2, }).then(() => { - itemRef.current.style.scale = '0'; + itemRef.current.style.scale = "0"; onDelete(); }); document.body.appendChild(el); el.animate( { - transform: ['scale(5)', 'scale(1)'], + transform: ["scale(5)", "scale(1)"], opacity: [0, 0.3, 1], }, { duration: 200, - easing: 'cubic-bezier(0.64, 0.57, 0.67, 1.53)', - fill: 'forwards', + easing: "cubic-bezier(0.64, 0.57, 0.67, 1.53)", + fill: "forwards", }, ); el.animate( { - offsetDistance: ['0%', '100%'], + offsetDistance: ["0%", "100%"], }, { duration: 2000, - easing: 'cubic-bezier(0.64, 0.57, 0.67, 1.53)', - fill: 'forwards', + easing: "cubic-bezier(0.64, 0.57, 0.67, 1.53)", + fill: "forwards", delay: 200, }, ).onfinish = () => { el.animate( - { transform: 'scale(0)', opacity: 0 }, - { duration: 200, fill: 'forwards' }, + { transform: "scale(0)", opacity: 0 }, + { duration: 200, fill: "forwards" }, ).onfinish = () => { el.remove(); }; diff --git a/apps/web/src/components/Sidebar/PagesItem.tsx b/apps/web/src/components/Sidebar/PagesItem.tsx deleted file mode 100644 index fea8bf33..00000000 --- a/apps/web/src/components/Sidebar/PagesItem.tsx +++ /dev/null @@ -1,189 +0,0 @@ -"use client"; -import { cleanUrl } from "@/lib/utils"; -import { StoredContent } from "@/server/db/schema"; -import { - DropdownMenu, - DropdownMenuTrigger, - DropdownMenuContent, - DropdownMenuItem, -} from "../ui/dropdown-menu"; -import { Label } from "../ui/label"; -import { - ArrowUpRight, - MoreHorizontal, - Edit3, - Trash2, - Save, - ChevronRight, - Plus, -} from "lucide-react"; -import { useState } from "react"; -import { - Drawer, - DrawerContent, - DrawerHeader, - DrawerTitle, - DrawerDescription, - DrawerFooter, - DrawerClose, -} from "../ui/drawer"; -import { Input } from "../ui/input"; -import { Textarea } from "../ui/textarea"; -import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover"; - -export const PageItem: React.FC<{ item: StoredContent }> = ({ item }) => { - const [isDropdownOpen, setIsDropdownOpen] = useState(false); - const [isEditDrawerOpen, setIsEditDrawerOpen] = useState(false); - - return ( - <div className="hover:bg-rgray-5 has-[a:focus]:bg-rgray-5 flex w-full items-center rounded-full py-1 pl-3 pr-2 transition [&:hover>a>div>[data-icon]]:scale-125 [&:hover>a>div>[data-icon]]:opacity-100 [&:hover>a>div>[data-icon]]:delay-150 [&:hover>a>div>img]:scale-75 [&:hover>a>div>img]:opacity-0 [&:hover>a>div>img]:delay-0 [&:hover>button]:opacity-100"> - <a - href={item.url} - target="_blank" - className="flex w-[90%] items-center gap-2 focus-visible:outline-none" - > - <div className="relative h-4 min-w-4"> - <img - src={item.image ?? "/brain.png"} - alt={item.title ?? "Untitiled website"} - className="z-1 h-4 w-4 transition-[transform,opacity] delay-150 duration-150" - /> - <ArrowUpRight - data-icon - className="absolute left-1/2 top-1/2 z-[2] h-4 w-4 min-w-4 -translate-x-1/2 -translate-y-1/2 scale-75 opacity-0 transition-[transform,opacity] duration-150" - strokeWidth={1.5} - /> - </div> - - <span className="w-full truncate text-nowrap"> - {item.title ?? "Untitled website"} - </span> - </a> - <DropdownMenu open={isDropdownOpen} onOpenChange={setIsDropdownOpen}> - <DropdownMenuTrigger asChild> - <button className="ml-auto w-4 min-w-4 rounded-[0.15rem] opacity-0 focus-visible:opacity-100 focus-visible:outline-none"> - <MoreHorizontal className="h-4 w-4 min-w-4" /> - </button> - </DropdownMenuTrigger> - <DropdownMenuContent className="w-5"> - <DropdownMenuItem onClick={() => window.open(item.url)}> - <ArrowUpRight - className="mr-2 h-4 w-4 scale-125" - strokeWidth={1.5} - /> - Open - </DropdownMenuItem> - <DropdownMenuItem - onClick={() => { - setIsDropdownOpen(false); - setIsEditDrawerOpen(true); - }} - > - <Edit3 className="mr-2 h-4 w-4" strokeWidth={1.5} /> - Edit - </DropdownMenuItem> - <DropdownMenuItem className="focus-visible:bg-red-100 focus-visible:text-red-400 dark:focus-visible:bg-red-100/10"> - <Trash2 className="mr-2 h-4 w-4" strokeWidth={1.5} /> - Delete - </DropdownMenuItem> - </DropdownMenuContent> - </DropdownMenu> - <Drawer - shouldScaleBackground - open={isEditDrawerOpen} - onOpenChange={setIsEditDrawerOpen} - > - <DrawerContent className="pb-10 lg:px-[25vw]"> - <DrawerHeader className="relative mt-10 px-0"> - <DrawerTitle className=" flex w-full justify-between"> - Edit Page Details - </DrawerTitle> - <DrawerDescription>Change the page details</DrawerDescription> - <a - target="_blank" - href={item.url} - className="text-rgray-11/90 bg-rgray-3 text-md absolute right-0 top-0 flex w-min translate-y-1/2 items-center justify-center gap-1 rounded-full px-5 py-1" - > - <img src={item.image ?? "/brain.png"} className="h-4 w-4" /> - {cleanUrl(item.url)} - </a> - </DrawerHeader> - - <div className="mt-5"> - <Label>Title</Label> - <Input - className="" - required - value={item.title ?? ""} - placeholder={item.title ?? "Enter the title for the page"} - /> - </div> - <div className="mt-5"> - <Label>Additional Context</Label> - <Textarea - className="" - value={item.content ?? ""} - placeholder={"Enter additional context for this page"} - /> - </div> - <DrawerFooter className="flex flex-row-reverse items-center justify-end px-0 pt-5"> - <DrawerClose className="flex items-center justify-center rounded-md px-3 py-2 ring-2 ring-transparent transition hover:bg-blue-100 hover:text-blue-400 focus-visible:bg-blue-100 focus-visible:text-blue-400 focus-visible:outline-none focus-visible:ring-blue-200 dark:hover:bg-blue-100/10 dark:focus-visible:bg-blue-100/10 dark:focus-visible:ring-blue-200/30"> - <Save className="mr-2 h-4 w-4 " strokeWidth={1.5} /> - Save - </DrawerClose> - <DrawerClose className="hover:bg-rgray-3 focus-visible:bg-rgray-4 focus-visible:ring-rgray-7 flex items-center justify-center rounded-md px-3 py-2 ring-2 ring-transparent transition focus-visible:outline-none"> - Cancel - </DrawerClose> - <DrawerClose className="mr-auto flex items-center justify-center rounded-md bg-red-100 px-3 py-2 text-red-400 ring-2 ring-transparent transition focus-visible:outline-none focus-visible:ring-red-200 dark:bg-red-100/10 dark:focus-visible:ring-red-200/30"> - <Trash2 className="mr-2 h-4 w-4 " strokeWidth={1.5} /> - Delete - </DrawerClose> - </DrawerFooter> - </DrawerContent> - </Drawer> - </div> - ); -}; - -export const AddNewPagePopover: React.FC<{ - addNewUrl?: (url: string) => Promise<void>; -}> = ({ addNewUrl }) => { - const [isOpen, setIsOpen] = useState(false); - const [url, setUrl] = useState(""); - - return ( - <Popover open={isOpen} onOpenChange={setIsOpen}> - <PopoverTrigger asChild> - <button className="focus-visible:ring-rgray-7 ring-offset-rgray-3 ml-auto rounded-sm ring-2 ring-transparent ring-offset-2 focus-visible:outline-none"> - <Plus className="h-4 w-4 min-w-4" /> - </button> - </PopoverTrigger> - <PopoverContent align="start" side="top"> - <h1 className="mb-2 flex items-center justify-between "> - Add a new page - <button - onClick={() => { - setIsOpen(false); - addNewUrl?.(url); - }} - className="hover:bg-rgray-3 focus-visible:bg-rgray-4 focus-visible:ring-rgray-7 ring-offset-rgray-3 rounded-sm ring-2 ring-transparent ring-offset-2 transition focus-visible:outline-none" - > - <ChevronRight className="h-4 w-4" /> - </button> - </h1> - <Input - className="w-full" - autoFocus - onChange={(e) => setUrl(e.target.value)} - onKeyDown={(e) => { - if (e.key === "Enter") { - setIsOpen(false); - addNewUrl?.(url); - } - }} - placeholder="Enter the URL of the page" - /> - </PopoverContent> - </Popover> - ); -}; diff --git a/apps/web/src/components/Sidebar/index.tsx b/apps/web/src/components/Sidebar/index.tsx index 88b7472d..52bab0f9 100644 --- a/apps/web/src/components/Sidebar/index.tsx +++ b/apps/web/src/components/Sidebar/index.tsx @@ -1,49 +1,54 @@ -'use client'; -import { StoredContent } from '@/server/db/schema'; -import { MemoryIcon } from '../../assets/Memories'; -import { Trash2, User2 } from 'lucide-react'; -import React, { ElementType, useEffect, useState } from 'react'; -import { MemoriesBar } from './MemoriesBar'; -import { AnimatePresence, motion } from 'framer-motion'; -import { Bin } from '@/assets/Bin'; -import { CollectedSpaces } from '../../../types/memory'; +"use client"; +import { StoredContent } from "@/server/db/schema"; +import { MemoryIcon } from "../../assets/Memories"; +import { Trash2, User2 } from "lucide-react"; +import React, { ElementType, useEffect, useState } from "react"; +import { MemoriesBar } from "./MemoriesBar"; +import { AnimatePresence, motion } from "framer-motion"; +import { Bin } from "@/assets/Bin"; +import { CollectedSpaces } from "../../../types/memory"; +import { useMemory } from "@/contexts/MemoryContext"; export type MenuItem = { icon: React.ReactNode | React.ReactNode[]; label: string; - content?: React.ReactElement; + content?: React.ReactNode; }; const menuItemsBottom: Array<MenuItem> = [ { icon: <Trash2 strokeWidth={1.3} className="h-6 w-6" />, - label: 'Trash', + label: "Trash", }, { icon: <User2 strokeWidth={1.3} className="h-6 w-6" />, - label: 'Profile', + label: "Profile", }, ]; export default function Sidebar({ selectChange, - spaces, + spaces }: { - selectChange?: (selectedItem: string | null) => Promise<void>; + selectChange?: (selectedItem: string | null) => void; spaces: CollectedSpaces[]; }) { + // TODO: @yxshv, put spaces in context here + // const { spaces } = useMemory(); + const menuItemsTop: Array<MenuItem> = [ { icon: <MemoryIcon className="h-10 w-10" />, - label: 'Memories', + label: "Memories", content: <MemoriesBar spaces={spaces} />, }, ]; const menuItems = [...menuItemsTop, ...menuItemsBottom]; const [selectedItem, setSelectedItem] = useState<string | null>(null); - const Subbar = - menuItems.find((i) => i.label === selectedItem)?.content ?? (() => <></>); + const Subbar = menuItems.find((i) => i.label === selectedItem)?.content ?? ( + <></> + ); useEffect(() => { void selectChange?.(selectedItem); @@ -55,7 +60,7 @@ export default function Sidebar({ <div className="bg-rgray-2 border-r-rgray-6 relative z-[50] flex h-full w-full flex-col items-center justify-center border-r px-2 py-5 "> <MenuItem item={{ - label: 'Memories', + label: "Memories", icon: <MemoryIcon className="h-10 w-10" />, content: <MemoriesBar spaces={spaces} />, }} @@ -67,7 +72,7 @@ export default function Sidebar({ <MenuItem item={{ - label: 'Trash', + label: "Trash", icon: <Bin id="trash" className="z-[300] h-7 w-7" />, }} selectedItem={selectedItem} @@ -76,7 +81,7 @@ export default function Sidebar({ /> <MenuItem item={{ - label: 'Profile', + label: "Profile", icon: <User2 strokeWidth={1.3} className="h-7 w-7" />, }} selectedItem={selectedItem} @@ -84,10 +89,6 @@ export default function Sidebar({ /> </div> <AnimatePresence> - {/* @yxshv idk why this is giving typeerror - when used as <Subbar/> it says it's not valid element type - */} - {/* @ts-ignore */} {selectedItem && <SubSidebar>{Subbar}</SubSidebar>} </AnimatePresence> </div> @@ -119,11 +120,11 @@ const MenuItem = ({ export function SubSidebar({ children }: { children?: React.ReactNode }) { return ( <motion.div - initial={{ opacity: 0, x: '-100%' }} + initial={{ opacity: 0, x: "-100%" }} animate={{ opacity: 1, x: 0 }} exit={{ opacity: 0, - x: '-100%', + x: "-100%", transition: { delay: 0.2 }, }} transition={{ diff --git a/apps/web/src/contexts/MemoryContext.tsx b/apps/web/src/contexts/MemoryContext.tsx new file mode 100644 index 00000000..6d84f95e --- /dev/null +++ b/apps/web/src/contexts/MemoryContext.tsx @@ -0,0 +1,39 @@ +"use client"; +import React, { useCallback } from "react"; +import { CollectedSpaces } from "../../types/memory"; + +// temperory (will change) +export const MemoryContext = React.createContext<{ + spaces: CollectedSpaces[]; + addSpace: (space: CollectedSpaces) => Promise<void>; +}>({ + spaces: [], + addSpace: async (space) => {}, +}); + +export const MemoryProvider: React.FC< + { spaces: CollectedSpaces[] } & React.PropsWithChildren +> = ({ children, spaces: initalSpaces }) => { + const [spaces, setSpaces] = React.useState<CollectedSpaces[]>(initalSpaces); + + const addSpace = useCallback( + async (space: CollectedSpaces) => { + setSpaces((prev) => [...prev, space]); + }, + [spaces], + ); + + return ( + <MemoryContext.Provider value={{ spaces, addSpace }}> + {children} + </MemoryContext.Provider> + ); +}; + +export const useMemory = () => { + const context = React.useContext(MemoryContext); + if (context === undefined) { + throw new Error("useMemory must be used within a MemoryProvider"); + } + return context; +}; |