UNPKG

nostr-web-components

Version:

collection of web components that provide quick access to basic nostr things

116 lines (115 loc) 4.01 kB
import { getOutboxRelaysFor, getWindowNostr, inputToPubkey, pool } from "./nostr.js"; class NostrFollow extends HTMLElement { static observedAttributes = ["pubkey"]; button; slotDefault; slotLoading; slotSuccess; slotFailure; constructor() { super(); this.attachShadow({ mode: "open" }); const { shadowRoot } = this; this.button = document.createElement("button"); this.button.setAttribute("part", "button"); this.button.onclick = this.handleClick.bind(this); this.slotDefault = document.createElement("slot"); this.slotDefault.textContent = "Follow me on Nostr!"; this.button.appendChild(this.slotDefault); this.slotLoading = document.createElement("slot"); this.slotLoading.name = "loading"; this.slotLoading.style.display = "none"; this.slotLoading.textContent = "Following..."; this.button.appendChild(this.slotLoading); this.slotSuccess = document.createElement("slot"); this.slotSuccess.name = "success"; this.slotSuccess.style.display = "none"; this.slotSuccess.textContent = "Followed."; this.button.appendChild(this.slotSuccess); this.slotFailure = document.createElement("slot"); this.slotFailure.name = "failure"; this.slotFailure.style.display = "none"; this.slotFailure.textContent = "Error: "; this.button.appendChild(this.slotFailure); const errorMessage = document.createElement("span"); errorMessage.setAttribute("part", "error-message"); this.slotFailure.appendChild(errorMessage); shadowRoot.appendChild(this.button); } *queryPart(name) { let slotted = this.button.querySelectorAll(`[part="${name}"]`); for (let i = 0; i < slotted.length; i++) { yield slotted[i]; } let slotted2 = this.querySelectorAll(`[part="${name}"]`); for (let i = 0; i < slotted2.length; i++) { yield slotted2[i]; } } async handleClick() { const [target, hints] = await inputToPubkey(this.getAttribute("pubkey")); if (!target) return; this.button.onclick = null; this.button.disabled = true; this.slotLoading.style.display = ""; this.slotDefault.style.display = "none"; try { const publicKey = await (await getWindowNostr()).getPublicKey(); const relays = await getOutboxRelaysFor(publicKey); relays.push(...hints); const res = await pool.querySync(relays, { kinds: [3], authors: [publicKey] }); if (res.length === 0) { throw new Error("couldn't find your contact list"); } const event = res[0]; if (event.tags.find((tag) => tag[1] === target)) { this.slotLoading.style.display = "none"; this.slotSuccess.style.display = ""; this.dispatchEvent( new CustomEvent("success", { bubbles: true, detail: { already: true } }) ); return; } event.tags.push(["p", target, relays[0]]); event.created_at = Math.round(Date.now() / 1e3); const signedEvent = await (await getWindowNostr()).signEvent(event); await Promise.any(pool.publish(relays, signedEvent)); this.slotLoading.style.display = "none"; this.slotSuccess.style.display = ""; this.dispatchEvent( new CustomEvent("success", { bubbles: true, detail: { already: false } }) ); } catch (error) { console.warn("failed to follow:", error); this.slotLoading.style.display = "none"; this.slotFailure.style.display = ""; this.dispatchEvent( new CustomEvent("failure", { bubbles: true, detail: { message: String(error) } }) ); for (let el of this.queryPart("error-message")) { let msg = String(error); if (msg.startsWith("Error:")) { msg = msg.substring(6); } el.textContent = msg.trim(); } } } } window.customElements.define("nostr-follow", NostrFollow); export { NostrFollow as default };