import { later } from "@ember/runloop"; import loadMorphlex from "discourse/lib/load-morphlex"; import { cook } from "discourse/lib/text"; const PROGRESS_INTERVAL = 40; const GIVE_UP_INTERVAL = 60000; export const MIN_LETTERS_PER_INTERVAL = 6; const MAX_FLUSH_TIME = 800; let progressTimer = null; function lastNonEmptyChild(element) { let lastChild = element.lastChild; while ( lastChild && lastChild.nodeType === Node.TEXT_NODE && !/\S/.test(lastChild.textContent) ) { lastChild = lastChild.previousSibling; } return lastChild; } export function addProgressDot(element) { let lastBlock = element; while (true) { let lastChild = lastNonEmptyChild(lastBlock); if (!lastChild) { break; } if (lastChild.nodeType === Node.ELEMENT_NODE) { lastBlock = lastChild; } else { break; } } const dotElement = document.createElement("span"); dotElement.classList.add("progress-dot"); lastBlock.appendChild(dotElement); } // this is the interface we need to implement // for a streaming updater class StreamUpdater { set streaming(value) { throw "not implemented"; } async setCooked() { throw "not implemented"; } async setRaw() { throw "not implemented"; } get element() { throw "not implemented"; } get raw() { throw "not implemented"; } } class PostUpdater extends StreamUpdater { morphingOptions = { beforeAttributeUpdated: (element, attributeName) => { return !(element.tagName === "DETAILS" && attributeName === "open"); }, }; constructor(postStream, postId) { super(); this.postStream = postStream; this.postId = postId; this.post = postStream.findLoadedPost(postId); if (this.post) { this.postElement = document.querySelector( `#post_${this.post.post_number}` ); } } get element() { return this.postElement; } set streaming(value) { if (this.postElement) { if (value) { this.postElement.classList.add("streaming"); } else { this.postElement.classList.remove("streaming"); } } } async setRaw(value, done) { this.post.set("raw", value); const cooked = await cook(value); // resets animation this.element.classList.remove("streaming"); void this.element.offsetWidth; this.element.classList.add("streaming"); const cookedElement = document.createElement("div"); cookedElement.innerHTML = cooked; if (!done) { addProgressDot(cookedElement); } await this.setCooked(cookedElement.innerHTML); } async setCooked(value) { this.post.set("cooked", value); (await loadMorphlex()).morphInner( this.postElement.querySelector(".cooked"), `