Removing id:discourse.resolver-resolutions notice.
This commit is contained in:
@@ -17,62 +17,18 @@ const STRINGS = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default apiInitializer("1.8.0", (api) => {
|
export default apiInitializer("1.8.0", (api) => {
|
||||||
if (!api.container.lookup("site-settings:main").bookmark_url_enabled) {
|
if (!api.container.lookup("service:site-settings").bookmark_url_enabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
api.modifyClass("component:composer-editor", {
|
let titleInputEl = null;
|
||||||
pluginId: "bookmark-url",
|
let titlePasteHandler = null;
|
||||||
|
let pendingUrl = null;
|
||||||
didInsertElement() {
|
|
||||||
this._super(...arguments);
|
|
||||||
this._attachTitlePasteListener();
|
|
||||||
},
|
|
||||||
|
|
||||||
willDestroyElement() {
|
|
||||||
this._super(...arguments);
|
|
||||||
if (this._titleInputEl && this._titlePasteHandler) {
|
|
||||||
this._titleInputEl.removeEventListener(
|
|
||||||
"paste",
|
|
||||||
this._titlePasteHandler,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// ---- Title paste interception -------------------------------------
|
|
||||||
// capture:true so our handler runs before Discourse's, then
|
|
||||||
// preventDefault + stopImmediatePropagation so Discourse never sees
|
|
||||||
// the paste event — no title lookup, no onebox race.
|
|
||||||
|
|
||||||
_attachTitlePasteListener(attempts = 0) {
|
|
||||||
const input = document.querySelector("#reply-title");
|
|
||||||
if (input) {
|
|
||||||
this._titleInputEl = input;
|
|
||||||
this._titlePasteHandler = (e) => {
|
|
||||||
const text = (e.clipboardData || window.clipboardData)
|
|
||||||
.getData("text/plain")
|
|
||||||
.trim();
|
|
||||||
if (!URL_REGEX.test(text)) return;
|
|
||||||
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopImmediatePropagation();
|
|
||||||
|
|
||||||
// Show URL in the field visually without going through Ember binding
|
|
||||||
input.value = text;
|
|
||||||
this._pendingUrl = text;
|
|
||||||
this._showArticleBar(text);
|
|
||||||
};
|
|
||||||
input.addEventListener("paste", this._titlePasteHandler, true);
|
|
||||||
} else if (attempts < 15) {
|
|
||||||
setTimeout(() => this._attachTitlePasteListener(attempts + 1), 200);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// ---- Bar UI -------------------------------------------------------
|
// ---- Bar UI -------------------------------------------------------
|
||||||
|
|
||||||
_showArticleBar(url) {
|
function showArticleBar(url) {
|
||||||
this._hideArticleBar();
|
hideArticleBar();
|
||||||
|
|
||||||
const bar = document.createElement("div");
|
const bar = document.createElement("div");
|
||||||
bar.className = "bookmark-url-bar";
|
bar.className = "bookmark-url-bar";
|
||||||
@@ -90,35 +46,37 @@ export default apiInitializer("1.8.0", (api) => {
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
bar.querySelector(".bookmark-url-btn").addEventListener("click", () => {
|
bar.querySelector(".bookmark-url-btn").addEventListener("click", () => {
|
||||||
this._fetchAndPopulate(url);
|
fetchAndPopulate(url);
|
||||||
});
|
});
|
||||||
|
|
||||||
bar.querySelector(".bookmark-url-onebox-btn").addEventListener("click", () => {
|
bar.querySelector(".bookmark-url-onebox-btn").addEventListener("click", () => {
|
||||||
this._hideArticleBar();
|
hideArticleBar();
|
||||||
this._commitUrlToModel();
|
commitUrlToModel();
|
||||||
});
|
});
|
||||||
|
|
||||||
bar.querySelector(".bookmark-url-dismiss").addEventListener("click", () => {
|
bar.querySelector(".bookmark-url-dismiss").addEventListener("click", () => {
|
||||||
this._hideArticleBar();
|
hideArticleBar();
|
||||||
this._pendingUrl = null;
|
pendingUrl = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
const container = this.element.querySelector(".d-editor-container");
|
const container = document.querySelector(".d-editor-container");
|
||||||
if (container) {
|
if (container) {
|
||||||
container.parentElement.insertBefore(bar, container);
|
container.parentElement.insertBefore(bar, container);
|
||||||
} else {
|
} else {
|
||||||
this.element.insertAdjacentElement("afterbegin", bar);
|
document
|
||||||
|
.querySelector(".composer-fields")
|
||||||
|
?.insertAdjacentElement("afterbegin", bar);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
_hideArticleBar() {
|
function hideArticleBar() {
|
||||||
this.element
|
document
|
||||||
?.querySelectorAll(".bookmark-url-bar")
|
.querySelectorAll(".bookmark-url-bar")
|
||||||
.forEach((el) => el.remove());
|
.forEach((el) => el.remove());
|
||||||
},
|
}
|
||||||
|
|
||||||
_setStatus(message, type = "info") {
|
function setStatus(message, type = "info") {
|
||||||
const bar = this.element?.querySelector(".bookmark-url-bar");
|
const bar = document.querySelector(".bookmark-url-bar");
|
||||||
if (!bar) return;
|
if (!bar) return;
|
||||||
let status = bar.querySelector(".bookmark-url-status");
|
let status = bar.querySelector(".bookmark-url-status");
|
||||||
if (!status) {
|
if (!status) {
|
||||||
@@ -128,29 +86,29 @@ export default apiInitializer("1.8.0", (api) => {
|
|||||||
}
|
}
|
||||||
status.textContent = message;
|
status.textContent = message;
|
||||||
status.className = `bookmark-url-status bookmark-url-status--${type}`;
|
status.className = `bookmark-url-status bookmark-url-status--${type}`;
|
||||||
},
|
}
|
||||||
|
|
||||||
// Release the URL into Ember's data-binding so Discourse handles it normally
|
// Release the URL into Ember's data-binding so Discourse handles it normally
|
||||||
_commitUrlToModel() {
|
function commitUrlToModel() {
|
||||||
const input = this._titleInputEl;
|
const input = titleInputEl;
|
||||||
const url = this._pendingUrl;
|
const url = pendingUrl;
|
||||||
if (!input || !url) return;
|
if (!input || !url) return;
|
||||||
input.value = url;
|
input.value = url;
|
||||||
input.dispatchEvent(new Event("input", { bubbles: true }));
|
input.dispatchEvent(new Event("input", { bubbles: true }));
|
||||||
this._pendingUrl = null;
|
pendingUrl = null;
|
||||||
},
|
}
|
||||||
|
|
||||||
// ---- Fetch & populate ---------------------------------------------
|
// ---- Fetch & populate ---------------------------------------------
|
||||||
|
|
||||||
async _fetchAndPopulate(url) {
|
async function fetchAndPopulate(url) {
|
||||||
const bar = this.element?.querySelector(".bookmark-url-bar");
|
const bar = document.querySelector(".bookmark-url-bar");
|
||||||
const btn = bar?.querySelector(".bookmark-url-btn");
|
const btn = bar?.querySelector(".bookmark-url-btn");
|
||||||
|
|
||||||
if (btn) {
|
if (btn) {
|
||||||
btn.disabled = true;
|
btn.disabled = true;
|
||||||
btn.textContent = STRINGS.fetching;
|
btn.textContent = STRINGS.fetching;
|
||||||
}
|
}
|
||||||
this._setStatus(STRINGS.fetching, "info");
|
setStatus(STRINGS.fetching, "info");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = await ajax("/bookmark-url/extract", {
|
const data = await ajax("/bookmark-url/extract", {
|
||||||
@@ -160,24 +118,25 @@ 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);
|
populateComposer(data);
|
||||||
this._setStatus(STRINGS.success, "success");
|
setStatus(STRINGS.success, "success");
|
||||||
setTimeout(() => this._hideArticleBar(), 3000);
|
setTimeout(() => hideArticleBar(), 3000);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const msg =
|
const msg =
|
||||||
err.jqXHR?.responseJSON?.error ||
|
err.jqXHR?.responseJSON?.error ||
|
||||||
err.message ||
|
err.message ||
|
||||||
STRINGS.error_generic;
|
STRINGS.error_generic;
|
||||||
this._setStatus(`${STRINGS.error_prefix} ${msg}`, "error");
|
setStatus(`${STRINGS.error_prefix} ${msg}`, "error");
|
||||||
if (btn) {
|
if (btn) {
|
||||||
btn.disabled = false;
|
btn.disabled = false;
|
||||||
btn.textContent = STRINGS.retry_button;
|
btn.textContent = STRINGS.retry_button;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
_populateComposer(data) {
|
function populateComposer(data) {
|
||||||
const composerModel = this.get("composer.model");
|
const composerService = api.container.lookup("service:composer");
|
||||||
|
const composerModel = composerService?.model;
|
||||||
if (!composerModel) return;
|
if (!composerModel) return;
|
||||||
|
|
||||||
const lines = [];
|
const lines = [];
|
||||||
@@ -201,13 +160,61 @@ export default apiInitializer("1.8.0", (api) => {
|
|||||||
|
|
||||||
lines.push(data.markdown || "");
|
lines.push(data.markdown || "");
|
||||||
|
|
||||||
composerModel.set("title", data.title || this._pendingUrl || "");
|
composerModel.set("title", data.title || pendingUrl || "");
|
||||||
composerModel.set("reply", lines.join("\n"));
|
composerModel.set("reply", lines.join("\n"));
|
||||||
this._pendingUrl = null;
|
pendingUrl = null;
|
||||||
|
|
||||||
if (this._titleInputEl) {
|
if (titleInputEl) {
|
||||||
this._titleInputEl.value = composerModel.get("title");
|
titleInputEl.value = composerModel.get("title");
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
|
// ---- Title paste interception -------------------------------------
|
||||||
|
// capture:true so our handler runs before Discourse's, then
|
||||||
|
// preventDefault + stopImmediatePropagation so Discourse never sees
|
||||||
|
// the paste event — no title lookup, no onebox race.
|
||||||
|
|
||||||
|
function attachTitlePasteListener(attempts = 0) {
|
||||||
|
const input = document.querySelector("#reply-title");
|
||||||
|
if (input) {
|
||||||
|
titleInputEl = input;
|
||||||
|
titlePasteHandler = (e) => {
|
||||||
|
const text = (e.clipboardData || window.clipboardData)
|
||||||
|
.getData("text/plain")
|
||||||
|
.trim();
|
||||||
|
if (!URL_REGEX.test(text)) return;
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
|
||||||
|
// Show URL in the field visually without going through Ember binding
|
||||||
|
input.value = text;
|
||||||
|
pendingUrl = text;
|
||||||
|
showArticleBar(text);
|
||||||
|
};
|
||||||
|
input.addEventListener("paste", titlePasteHandler, true);
|
||||||
|
} else if (attempts < 15) {
|
||||||
|
setTimeout(() => attachTitlePasteListener(attempts + 1), 200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function detachTitlePasteListener() {
|
||||||
|
if (titleInputEl && titlePasteHandler) {
|
||||||
|
titleInputEl.removeEventListener("paste", titlePasteHandler, true);
|
||||||
|
}
|
||||||
|
titleInputEl = null;
|
||||||
|
titlePasteHandler = null;
|
||||||
|
pendingUrl = null;
|
||||||
|
hideArticleBar();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Composer lifecycle -------------------------------------------
|
||||||
|
|
||||||
|
api.onAppEvent("composer:opened", () => {
|
||||||
|
attachTitlePasteListener();
|
||||||
|
});
|
||||||
|
|
||||||
|
api.onAppEvent("composer:closed", () => {
|
||||||
|
detachTitlePasteListener();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user