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>
);
}
|