aboutsummaryrefslogtreecommitdiff
path: root/apps/web/src/components/Sidebar/EditNoteDialog.tsx
blob: d3278df6f05de3ce582eaf7a73359e4dfedda2aa (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
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, onDelete }: { memory: StoredContent, closeDialog: () => any, onDelete?: () => void }) {
  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)
					onDelete?.()
				}}>
					<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>
  );
}