Fixing labels with strings

This commit is contained in:
2026-03-18 13:23:48 -04:00
parent 2c05590490
commit 820045601f

View File

@@ -1,9 +1,21 @@
import { apiInitializer } from "discourse/lib/api"; import { apiInitializer } from "discourse/lib/api";
import { ajax } from "discourse/lib/ajax"; import { ajax } from "discourse/lib/ajax";
import { i18n } from "discourse-i18n";
const URL_REGEX = /^(https?:\/\/[^\s/$.?#][^\s]*)$/i; const URL_REGEX = /^(https?:\/\/[^\s/$.?#][^\s]*)$/i;
const STRINGS = {
bar_label: "URL detected — import as article?",
fetch_button: "Import Article",
onebox_button: "Use Onebox",
dismiss: "Dismiss",
fetching: "Fetching…",
success: "Article imported!",
error_generic: "Unknown error",
error_prefix: "Error:",
source_label: "Source",
retry_button: "Retry",
};
export default apiInitializer("1.8.0", (api) => { export default apiInitializer("1.8.0", (api) => {
if (!api.container.lookup("site-settings:main").url_to_article_enabled) { if (!api.container.lookup("site-settings:main").url_to_article_enabled) {
return; return;
@@ -29,9 +41,9 @@ export default apiInitializer("1.8.0", (api) => {
}, },
// ---- Title paste interception ------------------------------------- // ---- Title paste interception -------------------------------------
// We use capture:true so our handler runs before Discourse's, then // capture:true so our handler runs before Discourse's, then
// preventDefault + stopImmediatePropagation so Discourse never sees // preventDefault + stopImmediatePropagation so Discourse never sees
// the paste event at all — no title lookup, no onebox race. // the paste event — no title lookup, no onebox race.
_attachTitlePasteListener(attempts = 0) { _attachTitlePasteListener(attempts = 0) {
const input = document.querySelector("#reply-title"); const input = document.querySelector("#reply-title");
@@ -66,22 +78,21 @@ export default apiInitializer("1.8.0", (api) => {
bar.className = "url-to-article-bar"; bar.className = "url-to-article-bar";
bar.innerHTML = ` bar.innerHTML = `
<span class="url-to-article-icon">📄</span> <span class="url-to-article-icon">📄</span>
<span class="url-to-article-label">${i18n("url_to_article.bar_label")}</span> <span class="url-to-article-label">${STRINGS.bar_label}</span>
<button class="btn btn-small btn-primary url-to-article-btn"> <button class="btn btn-small btn-primary url-to-article-btn">
${i18n("url_to_article.fetch_button")} ${STRINGS.fetch_button}
</button> </button>
<button class="btn btn-small btn-default url-to-article-onebox-btn"> <button class="btn btn-small btn-default url-to-article-onebox-btn">
${i18n("url_to_article.onebox_button")} ${STRINGS.onebox_button}
</button> </button>
<button class="btn btn-small btn-flat url-to-article-dismiss" <button class="btn btn-small btn-flat url-to-article-dismiss"
aria-label="${i18n("url_to_article.dismiss")}">✕</button> aria-label="${STRINGS.dismiss}">✕</button>
`; `;
bar.querySelector(".url-to-article-btn").addEventListener("click", () => { bar.querySelector(".url-to-article-btn").addEventListener("click", () => {
this._fetchAndPopulate(url); this._fetchAndPopulate(url);
}); });
// "Use Onebox" — commit URL into Ember model normally so Discourse takes over
bar.querySelector(".url-to-article-onebox-btn").addEventListener("click", () => { bar.querySelector(".url-to-article-onebox-btn").addEventListener("click", () => {
this._hideArticleBar(); this._hideArticleBar();
this._commitUrlToModel(); this._commitUrlToModel();
@@ -135,9 +146,9 @@ export default apiInitializer("1.8.0", (api) => {
if (btn) { if (btn) {
btn.disabled = true; btn.disabled = true;
btn.textContent = i18n("url_to_article.fetching"); btn.textContent = STRINGS.fetching;
} }
this._setStatus(i18n("url_to_article.fetching"), "info"); this._setStatus(STRINGS.fetching, "info");
try { try {
const data = await ajax("/url-to-article/extract", { const data = await ajax("/url-to-article/extract", {
@@ -148,20 +159,17 @@ export default apiInitializer("1.8.0", (api) => {
if (data.error) throw new Error(data.error); if (data.error) throw new Error(data.error);
this._populateComposer(data); this._populateComposer(data);
this._setStatus(i18n("url_to_article.success"), "success"); this._setStatus(STRINGS.success, "success");
setTimeout(() => this._hideArticleBar(), 3000); setTimeout(() => this._hideArticleBar(), 3000);
} catch (err) { } catch (err) {
const msg = const msg =
err.jqXHR?.responseJSON?.error || err.jqXHR?.responseJSON?.error ||
err.message || err.message ||
i18n("url_to_article.error_generic"); STRINGS.error_generic;
this._setStatus( this._setStatus(`${STRINGS.error_prefix} ${msg}`, "error");
`${i18n("url_to_article.error_prefix")} ${msg}`,
"error"
);
if (btn) { if (btn) {
btn.disabled = false; btn.disabled = false;
btn.textContent = i18n("url_to_article.retry_button"); btn.textContent = STRINGS.retry_button;
} }
} }
}, },
@@ -175,10 +183,10 @@ export default apiInitializer("1.8.0", (api) => {
const byline = data.byline ? ` — *${data.byline}*` : ""; const byline = data.byline ? ` — *${data.byline}*` : "";
if (siteName || byline) { if (siteName || byline) {
lines.push(`> ${siteName}${byline}`); lines.push(`> ${siteName}${byline}`);
lines.push(`> ${i18n("url_to_article.source_label")}: <${data.url}>`); lines.push(`> ${STRINGS.source_label}: <${data.url}>`);
lines.push(""); lines.push("");
} else { } else {
lines.push(`> ${i18n("url_to_article.source_label")}: <${data.url}>`); lines.push(`> ${STRINGS.source_label}: <${data.url}>`);
lines.push(""); lines.push("");
} }
@@ -191,12 +199,10 @@ export default apiInitializer("1.8.0", (api) => {
lines.push(data.markdown || ""); lines.push(data.markdown || "");
// Set title directly on the model (bypasses the title-lookup trigger)
composerModel.set("title", data.title || this._pendingUrl || ""); composerModel.set("title", data.title || this._pendingUrl || "");
composerModel.set("reply", lines.join("\n")); composerModel.set("reply", lines.join("\n"));
this._pendingUrl = null; this._pendingUrl = null;
// Sync the title input visually
if (this._titleInputEl) { if (this._titleInputEl) {
this._titleInputEl.value = composerModel.get("title"); this._titleInputEl.value = composerModel.get("title");
} }