aboutsummaryrefslogtreecommitdiff
path: root/apps/web/src/components/Sidebar/EditNoteDialog.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'apps/web/src/components/Sidebar/EditNoteDialog.tsx')
-rw-r--r--apps/web/src/components/Sidebar/EditNoteDialog.tsx152
1 files changed, 152 insertions, 0 deletions
diff --git a/apps/web/src/components/Sidebar/EditNoteDialog.tsx b/apps/web/src/components/Sidebar/EditNoteDialog.tsx
new file mode 100644
index 00000000..b7760656
--- /dev/null
+++ b/apps/web/src/components/Sidebar/EditNoteDialog.tsx
@@ -0,0 +1,152 @@
+
+import { Editor } from "novel";
+import {
+ DialogClose,
+ DialogFooter,
+} from "../ui/dialog";
+import { Input } from "../ui/input";
+import { Markdown } from "tiptap-markdown";
+import { useEffect, useRef, useState } from "react";
+import { FilterSpaces } from "./FilterCombobox";
+import { useMemory } from "@/contexts/MemoryContext";
+import { Loader, Plus, Trash, X } from "lucide-react";
+import { motion } from "framer-motion";
+import { StoredContent } from "@/server/db/schema";
+import { fetchContent } from "@/actions/db";
+import { isArraysEqual } from "@/lib/utils";
+import DeleteConfirmation from "./DeleteConfirmation";
+
+
+export function NoteEdit({ memory, closeDialog }: { memory: StoredContent, closeDialog: () => any }) {
+ const { updateMemory, deleteMemory } = useMemory();
+
+ const [initialSpaces, setInitialSpaces] = useState<number[]>([])
+ const [selectedSpacesId, setSelectedSpacesId] = useState<number[]>([]);
+
+ const inputRef = useRef<HTMLInputElement>(null);
+ const [name, setName] = useState(memory.title ?? "");
+ const [content, setContent] = useState(memory.content);
+ const [loading, setLoading] = useState(false);
+
+ function check(): boolean {
+ const data = {
+ name: name.trim(),
+ content,
+ };
+ 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 note";
+ inputRef.current.dataset["error"] = "true";
+ setTimeout(() => {
+ inputRef.current!.placeholder = "Title of the note";
+ inputRef.current!.dataset["error"] = "false";
+ }, 500);
+ inputRef.current.focus();
+ return false;
+ }
+ return true;
+ }
+
+ useEffect(() => {
+ fetchContent(memory.id).then((data) => {
+ if (data?.spaces) {
+ setInitialSpaces(data.spaces)
+ setSelectedSpacesId(data.spaces)
+ }
+ })
+ }, [])
+
+ return (
+ <div>
+ <Input
+ ref={inputRef}
+ data-error="false"
+ className="w-full border-none p-0 text-xl ring-0 placeholder:text-white/30 placeholder:transition placeholder:duration-500 focus-visible:ring-0 data-[error=true]:placeholder:text-red-400"
+ placeholder="Title of the note"
+ value={name}
+ disabled={loading}
+ onChange={(e) => setName(e.target.value)}
+ />
+ <Editor
+ disableLocalStorage
+ defaultValue={memory.content}
+ onUpdate={(editor) => {
+ if (!editor) return;
+ setContent(editor.storage.markdown.getMarkdown());
+ }}
+ extensions={[Markdown]}
+ className="novel-editor bg-rgray-4 border-rgray-7 dark mt-5 max-h-[60vh] min-h-[40vh] w-[50vw] overflow-y-auto rounded-lg border [&>div>div]:p-5"
+ />
+ <DialogFooter>
+ <FilterSpaces
+ selectedSpaces={selectedSpacesId}
+ setSelectedSpaces={setSelectedSpacesId}
+ className="hover:bg-rgray-5 mr-auto bg-white/5"
+ name={"Spaces"}
+ />
+ <DeleteConfirmation onDelete={() => {
+ deleteMemory(memory.id)
+ }}>
+ <button
+ type={undefined}
+ disabled={loading}
+ className="focus-visible:bg-red-100 focus-visible:text-red-400 dark:focus-visible:bg-red-100/10 hover:bg-red-100 dark:hover:bg-red-100/10 hover:text-red-400 rounded-md px-3 py-2 ring-transparent transition focus-visible:outline-none focus-visible:ring-2 disabled:cursor-not-allowed disabled:opacity-70"
+ >
+ <Trash className="w-5 h-5" />
+ </button>
+ </DeleteConfirmation>
+ <button
+ onClick={() => {
+ if (check()) {
+ setLoading(true);
+ console.log(
+
+ {
+ title: name === memory.title ? undefined : name,
+ content: content === memory.content ? undefined : content,
+ spaces: isArraysEqual(initialSpaces, selectedSpacesId) ? undefined : selectedSpacesId,
+ },
+ )
+ updateMemory(
+ memory.id,
+ {
+ title: name === memory.title ? undefined : name,
+ content: content === memory.content ? undefined : content,
+ spaces: isArraysEqual(initialSpaces, selectedSpacesId) ? undefined : selectedSpacesId,
+ },
+ ).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%" }}
+ >
+ Save
+ </motion.div>
+ </button>
+ <DialogClose
+ type={undefined}
+ 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>
+ </DialogFooter>
+ </div>
+ );
+}
+