diff options
| author | Dhravya <[email protected]> | 2024-04-13 09:55:29 -0700 |
|---|---|---|
| committer | Dhravya <[email protected]> | 2024-04-13 09:55:29 -0700 |
| commit | 57e699a6ee35b161cf69aa82bec6c50f114b1055 (patch) | |
| tree | a40d3c983c4d7661797e1c18591019cc09c3f083 /apps/web/src/components/Sidebar/AddMemoryDialog.tsx | |
| parent | merge (diff) | |
| parent | fix edge case for getting metadata (diff) | |
| download | supermemory-57e699a6ee35b161cf69aa82bec6c50f114b1055.tar.xz supermemory-57e699a6ee35b161cf69aa82bec6c50f114b1055.zip | |
conflicts
Diffstat (limited to 'apps/web/src/components/Sidebar/AddMemoryDialog.tsx')
| -rw-r--r-- | apps/web/src/components/Sidebar/AddMemoryDialog.tsx | 163 |
1 files changed, 151 insertions, 12 deletions
diff --git a/apps/web/src/components/Sidebar/AddMemoryDialog.tsx b/apps/web/src/components/Sidebar/AddMemoryDialog.tsx index 08b9a750..d0523581 100644 --- a/apps/web/src/components/Sidebar/AddMemoryDialog.tsx +++ b/apps/web/src/components/Sidebar/AddMemoryDialog.tsx @@ -10,8 +10,12 @@ import { Input } from "../ui/input"; import { Label } from "../ui/label"; import { Markdown } from "tiptap-markdown"; import { useEffect, useRef, useState } from "react"; -import { FilterSpaces } from "./FilterCombobox"; +import { FilterMemories, FilterSpaces } from "./FilterCombobox"; import { useMemory } from "@/contexts/MemoryContext"; +import { Loader, Plus, X } from "lucide-react"; +import { StoredContent } from "@/server/db/schema"; +import { cleanUrl } from "@/lib/utils"; +import { motion } from "framer-motion"; export function AddMemoryPage() { const { addMemory } = useMemory(); @@ -73,6 +77,8 @@ export function AddMemoryPage() { } export function NoteAddPage({ closeDialog }: { closeDialog: () => void }) { + const { addMemory } = useMemory(); + const [selectedSpacesId, setSelectedSpacesId] = useState<number[]>([]); const inputRef = useRef<HTMLInputElement>(null); @@ -112,6 +118,7 @@ export function NoteAddPage({ closeDialog }: { closeDialog: () => void }) { placeholder="Title of the note" data-modal-autofocus value={name} + disabled={loading} onChange={(e) => setName(e.target.value)} /> <Editor @@ -134,16 +141,41 @@ export function NoteAddPage({ closeDialog }: { closeDialog: () => void }) { <button onClick={() => { if (check()) { - closeDialog(); + setLoading(true); + addMemory( + { + content, + title: name, + type: "note", + url: "https://notes.supermemory.dhr.wtf/", + image: "", + savedAt: new Date(), + }, + selectedSpacesId, + ).then(closeDialog); } }} - className="bg-rgray-4 hover:bg-rgray-5 focus-visible:bg-rgray-5 focus-visible:ring-rgray-7 rounded-md px-4 py-2 ring-transparent transition focus-visible:outline-none focus-visible:ring-2" + disabled={loading} + className="bg-rgray-4 hover:bg-rgray-5 focus-visible:bg-rgray-5 focus-visible:ring-rgray-7 relative rounded-md px-4 py-2 ring-transparent transition focus-visible:outline-none focus-visible:ring-2 disabled:cursor-not-allowed disabled:opacity-70" > - Add + <motion.div + initial={{ x: "-50%", y: "-100%" }} + animate={loading && { y: "-50%", x: "-50%", opacity: 1 }} + className="absolute left-1/2 top-1/2 -translate-x-1/2 translate-y-[-100%] opacity-0" + > + <Loader className="text-rgray-11 h-5 w-5 animate-spin" /> + </motion.div> + <motion.div + initial={{ y: "0%" }} + animate={loading && { opacity: 0, y: "30%" }} + > + Add + </motion.div> </button> <DialogClose type={undefined} - className="hover:bg-rgray-4 focus-visible:bg-rgray-4 focus-visible:ring-rgray-7 rounded-md px-3 py-2 ring-transparent transition focus-visible:outline-none focus-visible:ring-2" + disabled={loading} + className="hover:bg-rgray-4 focus-visible:bg-rgray-4 focus-visible:ring-rgray-7 rounded-md px-3 py-2 ring-transparent transition focus-visible:outline-none focus-visible:ring-2 disabled:cursor-not-allowed disabled:opacity-70" > Cancel </DialogClose> @@ -153,6 +185,37 @@ export function NoteAddPage({ closeDialog }: { closeDialog: () => void }) { } export function SpaceAddPage({ closeDialog }: { closeDialog: () => void }) { + const { addSpace } = useMemory(); + + const inputRef = useRef<HTMLInputElement>(null); + const [name, setName] = useState(""); + + const [loading, setLoading] = useState(false); + + const [selected, setSelected] = useState<StoredContent[]>([]); + + function check(): boolean { + const data = { + name: name.trim(), + }; + if (!data.name || data.name.length < 1) { + if (!inputRef.current) { + alert("Please enter a name for the note"); + return false; + } + inputRef.current.value = ""; + inputRef.current.placeholder = "Please enter a title for the space"; + inputRef.current.dataset["error"] = "true"; + setTimeout(() => { + inputRef.current!.placeholder = "Enter the name of the space"; + inputRef.current!.dataset["error"] = "false"; + }, 500); + inputRef.current.focus(); + return false; + } + return true; + } + return ( <div className="md:w-[40vw]"> <DialogHeader> @@ -160,23 +223,99 @@ export function SpaceAddPage({ closeDialog }: { closeDialog: () => void }) { </DialogHeader> <Label className="mt-5 block">Name</Label> <Input + ref={inputRef} placeholder="Enter the name of the space" type="url" data-modal-autofocus - className="bg-rgray-4 mt-2 w-full" + value={name} + disabled={loading} + onChange={(e) => setName(e.target.value)} + className="bg-rgray-4 mt-2 w-full placeholder:transition placeholder:duration-500 data-[error=true]:placeholder:text-red-400 focus-visible:data-[error=true]:ring-red-500/10" /> - <Label className="mt-5 block">Memories</Label> + {selected.length > 0 && ( + <> + <Label className="mt-5 block">Add Memories</Label> + <div className="flex min-h-5 flex-col items-center justify-center py-2"> + {selected.map((i) => ( + <MemorySelectedItem + key={i.id} + onRemove={() => + setSelected((prev) => prev.filter((p) => p.id !== i.id)) + } + {...i} + /> + ))} + </div> + </> + )} <DialogFooter> - <DialogClose + <FilterMemories + selected={selected} + setSelected={setSelected} + disabled={loading} + className="hover:bg-rgray-4 focus-visible:bg-rgray-4 mr-auto bg-white/5 disabled:cursor-not-allowed disabled:opacity-70" + > + <Plus className="h-5 w-5" /> + Memory + </FilterMemories> + <button type={undefined} - className="bg-rgray-4 hover:bg-rgray-5 focus-visible:bg-rgray-5 focus-visible:ring-rgray-7 rounded-md px-4 py-2 ring-transparent transition focus-visible:outline-none focus-visible:ring-2" + onClick={() => { + if (check()) { + setLoading(true); + addSpace( + name, + selected.map((s) => s.id), + ).then(() => closeDialog()); + } + }} + disabled={loading} + className="bg-rgray-4 hover:bg-rgray-5 focus-visible:bg-rgray-5 focus-visible:ring-rgray-7 relative rounded-md px-4 py-2 ring-transparent transition focus-visible:outline-none focus-visible:ring-2 disabled:cursor-not-allowed disabled:opacity-70" + > + <motion.div + initial={{ x: "-50%", y: "-100%" }} + animate={loading && { y: "-50%", x: "-50%", opacity: 1 }} + className="absolute left-1/2 top-1/2 -translate-x-1/2 translate-y-[-100%] opacity-0" + > + <Loader className="text-rgray-11 h-5 w-5 animate-spin" /> + </motion.div> + <motion.div + initial={{ y: "0%" }} + animate={loading && { opacity: 0, y: "30%" }} + > + Add + </motion.div> + </button> + <DialogClose + disabled={loading} + className="hover:bg-rgray-4 focus-visible:bg-rgray-4 focus-visible:ring-rgray-7 rounded-md px-3 py-2 ring-transparent transition focus-visible:outline-none focus-visible:ring-2 disabled:cursor-not-allowed disabled:opacity-70" > - Add - </DialogClose> - <DialogClose className="hover:bg-rgray-4 focus-visible:bg-rgray-4 focus-visible:ring-rgray-7 rounded-md px-3 py-2 ring-transparent transition focus-visible:outline-none focus-visible:ring-2"> Cancel </DialogClose> </DialogFooter> </div> ); } + +export function MemorySelectedItem({ + id, + title, + url, + image, + onRemove, +}: StoredContent & { onRemove: () => void }) { + return ( + <div className="hover:bg-rgray-4 focus-within-bg-rgray-4 flex w-full items-center justify-start gap-2 rounded-md p-1 px-2 text-sm [&:hover>[data-icon]]:block [&:hover>img]:hidden"> + <img src={image ?? "/icons/logo_without_bg.png"} className="h-5 w-5" /> + <button + onClick={onRemove} + data-icon + className="m-0 hidden h-5 w-5 p-0 focus-visible:outline-none" + > + <X className="h-5 w-5 scale-90" /> + </button> + <span>{title}</span> + <span className="ml-auto block opacity-50">{cleanUrl(url)}</span> + </div> + ); +} |