DEV: Refactor composer preview rendering (#31308)

- Switch to use new `<DecoratedHtml` component (including
`renderGlimmer` support)

- Updates click handling to use `{{on` modifier instead of manual event
listener setup/teardown
This commit is contained in:
David Taylor 2025-02-14 09:50:02 +00:00 committed by GitHub
parent 35084d3089
commit 54b1e3195c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 31 additions and 42 deletions

View File

@ -519,8 +519,12 @@ export default class ComposerEditor extends Component {
resolveAllShortUrls(ajax, this.siteSettings, preview); resolveAllShortUrls(ajax, this.siteSettings, preview);
} }
_decorateCookedElement(preview) { _decorateCookedElement(preview, helper) {
this.appEvents.trigger("decorate-non-stream-cooked-element", preview); this.appEvents.trigger(
"decorate-non-stream-cooked-element",
preview,
helper
);
} }
@debounce(DEBOUNCE_JIT_MS) @debounce(DEBOUNCE_JIT_MS)
@ -896,13 +900,13 @@ export default class ComposerEditor extends Component {
} }
@action @action
previewUpdated(preview) { previewUpdated(preview, helper) {
this._renderMentions(preview); this._renderMentions(preview);
this._renderHashtags(preview); this._renderHashtags(preview);
this._refreshOneboxes(preview); this._refreshOneboxes(preview);
this._expandShortUrls(preview); this._expandShortUrls(preview);
this._decorateCookedElement(preview); this._decorateCookedElement(preview, helper);
this.composer.afterRefresh(preview); this.composer.afterRefresh(preview);
} }

View File

@ -2,6 +2,7 @@ import { tracked } from "@glimmer/tracking";
import Component from "@ember/component"; import Component from "@ember/component";
import EmberObject, { action } from "@ember/object"; import EmberObject, { action } from "@ember/object";
import { not } from "@ember/object/computed"; import { not } from "@ember/object/computed";
import { schedule } from "@ember/runloop";
import { service } from "@ember/service"; import { service } from "@ember/service";
import { classNameBindings } from "@ember-decorators/component"; import { classNameBindings } from "@ember-decorators/component";
import { ajax } from "discourse/lib/ajax"; import { ajax } from "discourse/lib/ajax";
@ -70,8 +71,10 @@ export default class ComposerMessages extends Component {
return; return;
} }
this.reset(); schedule("actions", () => {
this.popup(EmberObject.create(info)); this.reset();
this.popup(EmberObject.create(info));
});
} }
// Resets all active messages. // Resets all active messages.

View File

@ -71,12 +71,16 @@
</div> </div>
</div> </div>
{{! template-lint-disable no-invalid-interactive }}
<div <div
class="d-editor-preview-wrapper {{if this.forcePreview 'force-preview'}}" class="d-editor-preview-wrapper {{if this.forcePreview 'force-preview'}}"
{{on "click" this.handlePreviewClick}}
> >
<div class="d-editor-preview"> <DecoratedHtml
{{html-safe this.preview}} @className="d-editor-preview"
</div> @html={{html-safe this.preview}}
@decorate={{this.previewUpdated}}
/>
<span class="d-editor-plugin"> <span class="d-editor-plugin">
<PluginOutlet <PluginOutlet
@name="editor-preview" @name="editor-preview"

View File

@ -15,7 +15,7 @@ import InsertHyperlink from "discourse/components/modal/insert-hyperlink";
import { SKIP } from "discourse/lib/autocomplete"; import { SKIP } from "discourse/lib/autocomplete";
import Toolbar from "discourse/lib/composer/toolbar"; import Toolbar from "discourse/lib/composer/toolbar";
import discourseDebounce from "discourse/lib/debounce"; import discourseDebounce from "discourse/lib/debounce";
import discourseComputed, { bind } from "discourse/lib/decorators"; import discourseComputed from "discourse/lib/decorators";
import deprecated from "discourse/lib/deprecated"; import deprecated from "discourse/lib/deprecated";
import { isTesting } from "discourse/lib/environment"; import { isTesting } from "discourse/lib/environment";
import { getRegister } from "discourse/lib/get-owner"; import { getRegister } from "discourse/lib/get-owner";
@ -60,6 +60,8 @@ export default class DEditor extends Component {
/** @type {TextManipulation} */ /** @type {TextManipulation} */
@tracked textManipulation; @tracked textManipulation;
@tracked preview;
ready = false; ready = false;
lastSel = null; lastSel = null;
showLink = true; showLink = true;
@ -105,14 +107,7 @@ export default class DEditor extends Component {
didInsertElement() { didInsertElement() {
super.didInsertElement(...arguments); super.didInsertElement(...arguments);
this._previewMutationObserver = this._disablePreviewTabIndex(); this._previewMutationObserver = this._disablePreviewTabIndex();
// disable clicking on links in the preview
this.element
.querySelector(".d-editor-preview")
.addEventListener("click", this._handlePreviewLinkClick);
``;
} }
get keymap() { get keymap() {
@ -154,8 +149,12 @@ export default class DEditor extends Component {
return keymap; return keymap;
} }
@bind @action
_handlePreviewLinkClick(event) { handlePreviewClick(event) {
if (!event.target.closest(".d-editor-preview")) {
return;
}
if (wantsNewWindow(event)) { if (wantsNewWindow(event)) {
return; return;
} }
@ -184,10 +183,6 @@ export default class DEditor extends Component {
@on("willDestroyElement") @on("willDestroyElement")
_shutDown() { _shutDown() {
this.element
.querySelector(".d-editor-preview")
?.removeEventListener("click", this._handlePreviewLinkClick);
this._previewMutationObserver?.disconnect(); this._previewMutationObserver?.disconnect();
this._cachedCookFunction = null; this._cachedCookFunction = null;
@ -235,24 +230,7 @@ export default class DEditor extends Component {
return; return;
} }
this.set("preview", cooked); this.preview = cooked;
schedule("afterRender", () => {
if (
this._state !== "inDOM" ||
!this.element ||
this.isDestroying ||
this.isDestroyed
) {
return;
}
const previewElement = this.element.querySelector(".d-editor-preview");
if (previewElement && this.previewUpdated) {
this.previewUpdated(previewElement);
}
});
} }
@observes("ready", "value", "processPreview") @observes("ready", "value", "processPreview")
@ -716,7 +694,7 @@ export default class DEditor extends Component {
}); });
}); });
observer.observe(document.querySelector(".d-editor-preview"), { observer.observe(document.querySelector(".d-editor-preview-wrapper"), {
childList: true, childList: true,
subtree: true, subtree: true,
attributes: false, attributes: false,