feat(web): note editing at the reader slider and book banner; Deleted rename
Objective 8a (web half). Reader notes-drawer rows gain an inline editor (mirroring the bookmark-rename pattern) that PUTs the new content — the stored position rides along because the server's note PUT hard-sets the position column (a probe note proved an omitted position NULLs the anchor). Book-detail banner rows gain Edit/Delete for location notes and Edit-note/Add-note for the note ATTACHED to a highlight (full-replace highlight PUT; empty content clears the note; the highlight itself is never edited from the banner). The edit dialog is centered with the markdown caption. The book-detail 'Notes' button becomes 'Deleted' (badge = deleted-annotation count) and the modal title becomes 'Deleted annotations' — that modal only ever showed removed annotations. Verified live in Brave: drawer inline edit round-trips content and position; banner note edit, highlight-note edit, and delete (tombstone appears in the Deleted modal); rename renders with the count badge.
This commit is contained in:
1 parent
0b83db8fce
commit
5275f2ba38
11 files changed
+257
-30
No files matched your search
@@ -2081,6 +2081,52 @@ document.addEventListener("alpine:init", () => {
|
||||
/* ignore note errors */
|
||||
}
|
||||
},
|
||||
/** Note edit at the annotations slider (objective 8a): inline, mirrors
|
||||
* the bookmark-rename pattern. The PUT carries the stored position —
|
||||
* the server hard-sets it, so an omitted position would strip the
|
||||
* jump anchor. */
|
||||
startNoteEdit(note: any) {
|
||||
note.editText = note.content ?? "";
|
||||
note.editOpen = true;
|
||||
},
|
||||
cancelNoteEdit(note: any) {
|
||||
note.editOpen = false;
|
||||
note.editText = "";
|
||||
},
|
||||
async saveNoteEdit(note: any) {
|
||||
const token = getToken();
|
||||
if (!token || !this.mediaItemId) return;
|
||||
const content = String(note.editText ?? "").trim();
|
||||
if (!content) return;
|
||||
try {
|
||||
const resp = await fetch(
|
||||
`/api/media-items/${this.mediaItemId}/notes/${note.id}`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
// The stored position MUST ride along: the server's PUT
|
||||
// hard-sets the position column (an omitted position
|
||||
// would strip the jump anchor).
|
||||
body: JSON.stringify({
|
||||
content,
|
||||
position: note.positionLabel || note.content && note.position || "",
|
||||
}),
|
||||
},
|
||||
);
|
||||
if (!resp.ok) return;
|
||||
const row: any = await resp.json();
|
||||
note.content = row.content ?? content;
|
||||
note.contentHtml = renderNoteHtml(note.content);
|
||||
note.positionLabel = row.position ?? note.positionLabel;
|
||||
note.editOpen = false;
|
||||
note.editText = "";
|
||||
} catch (_e) {
|
||||
/* note errors are non-critical */
|
||||
}
|
||||
},
|
||||
async deleteNoteById(id: string) {
|
||||
const token = getToken();
|
||||
if (!token || !this.mediaItemId) return;
|
||||
|
||||
Reference in new issue
Block a user