aboutsummaryrefslogtreecommitdiff
path: root/apps/web/src/components/Sidebar/AddMemoryDialog.tsx
diff options
context:
space:
mode:
authorDhravya <[email protected]>2024-04-13 09:55:29 -0700
committerDhravya <[email protected]>2024-04-13 09:55:29 -0700
commit57e699a6ee35b161cf69aa82bec6c50f114b1055 (patch)
treea40d3c983c4d7661797e1c18591019cc09c3f083 /apps/web/src/components/Sidebar/AddMemoryDialog.tsx
parentmerge (diff)
parentfix edge case for getting metadata (diff)
downloadsupermemory-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.tsx163
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>
+ );
+}