import { html, render } from "/js/lib/lit-html.js"; import { Component } from "/js/components/component.js"; import { isAllowedLiveHost, getDisplayDomain } from "/js/dataHelpers.js"; import { getLinkCardMeta } from "/js/embedHelpers.js"; import { LIVE_ALLOWED_SERVICES } from "/js/config.js"; import { normalizeUrl } from "/js/utils.js"; const ALLOWED_SERVICES_LABEL = "Allowed services: " + LIVE_ALLOWED_SERVICES.map(({ displayName }) => displayName).join(", "); function errorTemplate({ error }) { return html`
${error}
`; } function infoTemplate() { return html`
${ALLOWED_SERVICES_LABEL}
`; } function previewTemplate({ linkMeta, linkMetaFailed }) { return html` ${linkMetaFailed ? html`
We couldn't load a preview for this link.
` : null} `; } class LiveLinkForm extends Component { connectedCallback() { if (this._initialized) return; this._initialized = true; const initialUrl = this.initialUrl ?? ""; const initialMeta = this.initialLinkMeta ?? null; this._urlInput = initialUrl; this._normalizedUrl = initialUrl ? normalizeUrl(initialUrl) : null; this._debouncedUrl = this._normalizedUrl; this._debounceTimer = null; this._linkMeta = initialMeta; this._linkMetaLoading = false; this._linkMetaFailed = false; this._urlError = null; this.render(); if (this._normalizedUrl && !initialMeta) { this._loadLinkMeta(this._normalizedUrl); } } disconnectedCallback() { clearTimeout(this._debounceTimer); } focus(options) { this.querySelector("input")?.focus(options); } get isLoading() { return this._debouncedUrl !== this._normalizedUrl || this._linkMetaLoading; } get linkMeta() { return this._linkMeta; } _notifyChange() { this.dispatchEvent( new CustomEvent("change", { detail: { linkMeta: this._linkMeta, isLoading: this.isLoading }, }), ); } _handleInput(event) { const value = event.target.value; this._urlInput = value; this._urlError = null; this._linkMetaFailed = false; clearTimeout(this._debounceTimer); const normalized = normalizeUrl(value); this._normalizedUrl = normalized; if (!value.trim()) { this._linkMeta = null; this._debouncedUrl = null; this._linkMetaLoading = false; this.render(); this._notifyChange(); return; } this._debounceTimer = setTimeout(() => { this._debouncedUrl = normalized; this._loadLinkMeta(normalized); }, 500); this.render(); this._notifyChange(); } _handleBlur() { if (this._urlInput.trim() && !this._normalizedUrl) { this._urlError = "This is not a valid link"; this.render(); this._notifyChange(); } } _handleFocus() { if (this._urlError) { this._urlError = null; this.render(); this._notifyChange(); } } async _loadLinkMeta(url) { if (!url) { this._linkMeta = null; this._linkMetaLoading = false; this._linkMetaFailed = false; this.render(); this._notifyChange(); return; } if (!isAllowedLiveHost(url)) { this._linkMeta = null; this._linkMetaLoading = false; this._linkMetaFailed = false; this._urlError = "This service is not supported."; this.render(); this._notifyChange(); return; } this._linkMeta = { url, title: url, description: "", image: null }; this._linkMetaLoading = true; this._linkMetaFailed = false; this.render(); this._notifyChange(); const data = await getLinkCardMeta(url); if (this._debouncedUrl !== url) return; if (data) { this._linkMeta = { url, title: data.title || url, description: data.description || "", image: data.image || null, }; } else { this._linkMetaFailed = true; } this._linkMetaLoading = false; this.render(); this._notifyChange(); } render() { render( html` ${this._urlError ? errorTemplate({ error: this._urlError }) : this._linkMeta ? previewTemplate({ linkMeta: this._linkMeta, linkMetaFailed: this._linkMetaFailed, }) : infoTemplate()} `, this, ); } } LiveLinkForm.register();