import { searchBooks } from "@data-sources/goodreads/scraper"; import { SearchResult } from "@data-sources/goodreads/types"; import { App, Modal, Notice, TextComponent } from "obsidian"; export class GoodreadsSearchModal extends Modal { private query: string; constructor( app: App, private readonly onSearch: (error: any, results: SearchResult[]) => void ) { super(app); } async doSearch(): Promise { if (!this.query || this.query.trim() === "") { this.onSearch(new Error("Search query cannot be empty."), []); return; } try { const results = await searchBooks(this.query); if (results.length === 0) { this.onSearch( new Error("No results found for the given query."), [] ); return; } this.onSearch(null, results); } catch (error) { this.onSearch(error, []); } } onOpen(): void { const { contentEl } = this; contentEl.createEl("h2", { text: "Goodreads Search" }); contentEl.createDiv({ cls: "obt-goodreads-search__input" }, (el) => { new TextComponent(el) .setValue(this.query || "") .setPlaceholder("Search for books on Goodreads") .onChange((value) => { this.query = value; }) .inputEl.addEventListener("keydown", (event) => { if (event.key === "Enter" && !event.isComposing) { event.preventDefault(); this.doSearch(); } }); }); } static createAndOpen(app: App): Promise { return new Promise((resolve, reject) => { const modal = new GoodreadsSearchModal(app, (error, results) => { if (error) { new Notice(`Error: ${error.message}`); reject(error); } else { resolve(results); } modal.close(); }); modal.open(); }); } }