AT protocol bookmarking platforms in obsidian
at client-cache 142 lines 4.2 kB view raw
1import { Modal, Notice, setIcon } from "obsidian"; 2import type ATmarkPlugin from "../main"; 3import { createNoteCard, deleteRecord } from "../lib"; 4import type { ATmarkItem } from "../sources/types"; 5 6export class CardDetailModal extends Modal { 7 plugin: ATmarkPlugin; 8 item: ATmarkItem; 9 onSuccess?: () => void; 10 noteInput: HTMLTextAreaElement | null = null; 11 12 constructor(plugin: ATmarkPlugin, item: ATmarkItem, onSuccess?: () => void) { 13 super(plugin.app); 14 this.plugin = plugin; 15 this.item = item; 16 this.onSuccess = onSuccess; 17 } 18 19 onOpen() { 20 const { contentEl } = this; 21 contentEl.empty(); 22 contentEl.addClass("atmark-detail-modal"); 23 24 const header = contentEl.createEl("div", { cls: "atmark-detail-header" }); 25 const source = this.item.getSource(); 26 header.createEl("span", { 27 text: source, 28 cls: `atmark-badge atmark-badge-source atmark-badge-${source}`, 29 }); 30 31 this.item.renderDetail(contentEl); 32 33 // semble 34 if (this.item.canAddNotes() && this.item.getAttachedNotes) { 35 this.renderNotesSection(contentEl); 36 } 37 38 if (this.item.canAddNotes()) { 39 this.renderAddNoteForm(contentEl); 40 } 41 42 const footer = contentEl.createEl("div", { cls: "atmark-detail-footer" }); 43 footer.createEl("span", { 44 text: `Created ${new Date(this.item.getCreatedAt()).toLocaleDateString()}`, 45 cls: "atmark-detail-date", 46 }); 47 } 48 49 private renderNotesSection(contentEl: HTMLElement) { 50 const notes = this.item.getAttachedNotes?.(); 51 if (!notes || notes.length === 0) return; 52 53 const notesSection = contentEl.createEl("div", { cls: "atmark-semble-detail-notes-section" }); 54 notesSection.createEl("h3", { text: "Notes", cls: "atmark-detail-section-title" }); 55 56 for (const note of notes) { 57 const noteEl = notesSection.createEl("div", { cls: "atmark-semble-detail-note" }); 58 59 const noteContent = noteEl.createEl("div", { cls: "atmark-semble-detail-note-content" }); 60 const noteIcon = noteContent.createEl("span", { cls: "atmark-semble-detail-note-icon" }); 61 setIcon(noteIcon, "message-square"); 62 noteContent.createEl("p", { text: note.text, cls: "atmark-semble-detail-note-text" }); 63 64 const deleteBtn = noteEl.createEl("button", { cls: "atmark-semble-note-delete-btn" }); 65 setIcon(deleteBtn, "trash-2"); 66 deleteBtn.addEventListener("click", () => { 67 void this.handleDeleteNote(note.uri); 68 }); 69 } 70 } 71 72 private renderAddNoteForm(contentEl: HTMLElement) { 73 const formSection = contentEl.createEl("div", { cls: "atmark-semble-detail-add-note" }); 74 formSection.createEl("h3", { text: "Add a note", cls: "atmark-detail-section-title" }); 75 76 const form = formSection.createEl("div", { cls: "atmark-semble-add-note-form" }); 77 78 this.noteInput = form.createEl("textarea", { 79 cls: "atmark-textarea atmark-semble-note-input", 80 attr: { placeholder: "Write a note about this item..." }, 81 }); 82 83 const addBtn = form.createEl("button", { text: "Add note", cls: "atmark-btn atmark-btn-primary" }); 84 addBtn.addEventListener("click", () => { void this.handleAddNote(); }); 85 } 86 87 private async handleAddNote() { 88 if (!this.plugin.client || !this.noteInput) return; 89 90 const text = this.noteInput.value.trim(); 91 if (!text) { 92 new Notice("Please enter a note"); 93 return; 94 } 95 96 try { 97 await createNoteCard( 98 this.plugin.client, 99 this.plugin.settings.identifier, 100 text, 101 { uri: this.item.getUri(), cid: this.item.getCid() } 102 ); 103 104 new Notice("Note added"); 105 this.close(); 106 this.onSuccess?.(); 107 } catch (err) { 108 const message = err instanceof Error ? err.message : String(err); 109 new Notice(`Failed to add note: ${message}`); 110 } 111 } 112 113 private async handleDeleteNote(noteUri: string) { 114 if (!this.plugin.client) return; 115 116 const rkey = noteUri.split("/").pop(); 117 if (!rkey) { 118 new Notice("Invalid note uri"); 119 return; 120 } 121 122 try { 123 await deleteRecord( 124 this.plugin.client, 125 this.plugin.settings.identifier, 126 "network.cosmik.card", 127 rkey 128 ); 129 130 new Notice("Note deleted"); 131 this.close(); 132 this.onSuccess?.(); 133 } catch (err) { 134 const message = err instanceof Error ? err.message : String(err); 135 new Notice(`Failed to delete note: ${message}`); 136 } 137 } 138 139 onClose() { 140 this.contentEl.empty(); 141 } 142}