mirror of
https://github.com/discourse/discourse.git
synced 2024-11-22 11:44:49 +08:00
FEATURE: Use diffhtml to update composer preview (#11237)
Displaying videos, animated GIFs or any kind of rich content in preview used to refresh on every keystroke, which could cause performance problems.
This commit is contained in:
parent
bddf94c0ab
commit
08acf51be0
|
@ -24,6 +24,11 @@ import { findRawTemplate } from "discourse-common/lib/raw-templates";
|
|||
import { getRegister } from "discourse-common/lib/get-owner";
|
||||
import { isEmpty } from "@ember/utils";
|
||||
import { isTesting } from "discourse-common/config/environment";
|
||||
import { linkSeenHashtags } from "discourse/lib/link-hashtags";
|
||||
import { linkSeenMentions } from "discourse/lib/link-mentions";
|
||||
import { loadOneboxes } from "discourse/lib/load-oneboxes";
|
||||
import loadScript from "discourse/lib/load-script";
|
||||
import { resolveCachedShortUrls } from "pretty-text/upload-short-url";
|
||||
import { search as searchCategoryTag } from "discourse/lib/category-tag-search";
|
||||
import { inject as service } from "@ember/service";
|
||||
import showModal from "discourse/lib/show-modal";
|
||||
|
@ -389,6 +394,32 @@ export default Component.extend({
|
|||
}
|
||||
|
||||
this.set("preview", cooked);
|
||||
|
||||
if (this.siteSettings.enable_diffhtml_preview) {
|
||||
const cookedElement = document.createElement("div");
|
||||
cookedElement.innerHTML = cooked;
|
||||
|
||||
linkSeenHashtags($(cookedElement));
|
||||
linkSeenMentions($(cookedElement), this.siteSettings);
|
||||
resolveCachedShortUrls(this.siteSettings, cookedElement);
|
||||
loadOneboxes(
|
||||
cookedElement,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
this.siteSettings.max_oneboxes_per_post,
|
||||
false,
|
||||
true
|
||||
);
|
||||
|
||||
loadScript("/javascripts/diffhtml.min.js").then(() => {
|
||||
window.diff.innerHTML(
|
||||
this.element.querySelector(".d-editor-preview"),
|
||||
cookedElement.innerHTML
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
schedule("afterRender", () => {
|
||||
if (this._state !== "inDOM") {
|
||||
return;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { TAG_HASHTAG_POSTFIX } from "discourse/lib/tag-hashtags";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import { replaceSpan } from "discourse/lib/category-hashtags";
|
||||
import { schedule } from "@ember/runloop";
|
||||
|
||||
const categoryHashtags = {};
|
||||
const tagHashtags = {};
|
||||
|
@ -15,21 +14,19 @@ export function linkSeenHashtags($elem) {
|
|||
|
||||
const slugs = [...$hashtags.map((_, hashtag) => hashtag.innerText.substr(1))];
|
||||
|
||||
schedule("afterRender", () => {
|
||||
$hashtags.each((index, hashtag) => {
|
||||
let slug = slugs[index];
|
||||
const hasTagSuffix = slug.endsWith(TAG_HASHTAG_POSTFIX);
|
||||
if (hasTagSuffix) {
|
||||
slug = slug.substr(0, slug.length - TAG_HASHTAG_POSTFIX.length);
|
||||
}
|
||||
$hashtags.each((index, hashtag) => {
|
||||
let slug = slugs[index];
|
||||
const hasTagSuffix = slug.endsWith(TAG_HASHTAG_POSTFIX);
|
||||
if (hasTagSuffix) {
|
||||
slug = slug.substr(0, slug.length - TAG_HASHTAG_POSTFIX.length);
|
||||
}
|
||||
|
||||
const lowerSlug = slug.toLowerCase();
|
||||
if (categoryHashtags[lowerSlug] && !hasTagSuffix) {
|
||||
replaceSpan($(hashtag), slug, categoryHashtags[lowerSlug]);
|
||||
} else if (tagHashtags[lowerSlug]) {
|
||||
replaceSpan($(hashtag), slug, tagHashtags[lowerSlug]);
|
||||
}
|
||||
});
|
||||
const lowerSlug = slug.toLowerCase();
|
||||
if (categoryHashtags[lowerSlug] && !hasTagSuffix) {
|
||||
replaceSpan($(hashtag), slug, categoryHashtags[lowerSlug]);
|
||||
} else if (tagHashtags[lowerSlug]) {
|
||||
replaceSpan($(hashtag), slug, tagHashtags[lowerSlug]);
|
||||
}
|
||||
});
|
||||
|
||||
return slugs
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { ajax } from "discourse/lib/ajax";
|
||||
import { formatUsername } from "discourse/lib/utilities";
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import { schedule } from "@ember/runloop";
|
||||
import { userPath } from "discourse/lib/url";
|
||||
|
||||
let maxGroupMention;
|
||||
|
@ -42,23 +41,21 @@ const checked = {};
|
|||
const cannotSee = [];
|
||||
|
||||
function updateFound($mentions, usernames) {
|
||||
schedule("afterRender", function () {
|
||||
$mentions.each((i, e) => {
|
||||
const $e = $(e);
|
||||
const username = usernames[i];
|
||||
if (found[username.toLowerCase()]) {
|
||||
replaceSpan($e, username, { cannot_see: cannotSee[username] });
|
||||
} else if (mentionableGroups[username]) {
|
||||
replaceSpan($e, username, {
|
||||
group: true,
|
||||
mentionable: mentionableGroups[username],
|
||||
});
|
||||
} else if (foundGroups[username]) {
|
||||
replaceSpan($e, username, { group: true });
|
||||
} else if (checked[username]) {
|
||||
$e.addClass("mention-tested");
|
||||
}
|
||||
});
|
||||
$mentions.each((i, e) => {
|
||||
const $e = $(e);
|
||||
const username = usernames[i];
|
||||
if (found[username.toLowerCase()]) {
|
||||
replaceSpan($e, username, { cannot_see: cannotSee[username] });
|
||||
} else if (mentionableGroups[username]) {
|
||||
replaceSpan($e, username, {
|
||||
group: true,
|
||||
mentionable: mentionableGroups[username],
|
||||
});
|
||||
} else if (foundGroups[username]) {
|
||||
replaceSpan($e, username, { group: true });
|
||||
} else if (checked[username]) {
|
||||
$e.addClass("mention-tested");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,8 @@ export function loadOneboxes(
|
|||
topicId,
|
||||
categoryId,
|
||||
maxOneboxes,
|
||||
refresh
|
||||
refresh,
|
||||
offline
|
||||
) {
|
||||
const oneboxes = {};
|
||||
const inlineOneboxes = {};
|
||||
|
@ -41,17 +42,22 @@ export function loadOneboxes(
|
|||
}
|
||||
});
|
||||
|
||||
let newBoxes = 0;
|
||||
|
||||
if (Object.keys(oneboxes).length > 0) {
|
||||
_loadOneboxes(oneboxes, ajax, newBoxes, topicId, categoryId, refresh);
|
||||
_loadOneboxes({
|
||||
oneboxes,
|
||||
ajax,
|
||||
topicId,
|
||||
categoryId,
|
||||
refresh,
|
||||
offline,
|
||||
});
|
||||
}
|
||||
|
||||
if (Object.keys(inlineOneboxes).length > 0) {
|
||||
_loadInlineOneboxes(inlineOneboxes, ajax, topicId, categoryId);
|
||||
}
|
||||
|
||||
return newBoxes;
|
||||
return Object.keys(oneboxes).length + Object.keys(inlineOneboxes).length;
|
||||
}
|
||||
|
||||
function _loadInlineOneboxes(inline, ajax, topicId, categoryId) {
|
||||
|
@ -61,18 +67,24 @@ function _loadInlineOneboxes(inline, ajax, topicId, categoryId) {
|
|||
});
|
||||
}
|
||||
|
||||
function _loadOneboxes(oneboxes, ajax, count, topicId, categoryId, refresh) {
|
||||
function _loadOneboxes({
|
||||
oneboxes,
|
||||
ajax,
|
||||
topicId,
|
||||
categoryId,
|
||||
refresh,
|
||||
offline,
|
||||
}) {
|
||||
Object.values(oneboxes).forEach((onebox) => {
|
||||
onebox.forEach((o) => {
|
||||
onebox.forEach((elem) => {
|
||||
load({
|
||||
elem: o,
|
||||
refresh,
|
||||
elem,
|
||||
ajax,
|
||||
categoryId: categoryId,
|
||||
topicId: topicId,
|
||||
categoryId,
|
||||
topicId,
|
||||
refresh,
|
||||
offline,
|
||||
});
|
||||
|
||||
count++;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ export const PUBLIC_JS_VERSIONS = {
|
|||
"Chart.min.js": "chart.js/2.9.3/Chart.min.js",
|
||||
"chartjs-plugin-datalabels.min.js":
|
||||
"chartjs-plugin-datalabels/0.7.0/chartjs-plugin-datalabels.min.js",
|
||||
"diffhtml.min.js": "diffhtml/1.0.0-beta.18/diffhtml.min.js",
|
||||
"jquery.magnific-popup.min.js":
|
||||
"magnific-popup/1.1.0/jquery.magnific-popup.min.js",
|
||||
"pikaday.js": "pikaday/1.8.0/pikaday.js",
|
||||
|
|
|
@ -49,7 +49,11 @@
|
|||
</div>
|
||||
|
||||
<div class="d-editor-preview-wrapper {{if forcePreview "force-preview"}}">
|
||||
<div class="d-editor-preview">{{html-safe preview}}</div>
|
||||
<div class="d-editor-preview">
|
||||
{{#unless siteSettings.enable_diffhtml_preview}}
|
||||
{{html-safe preview}}
|
||||
{{/unless}}
|
||||
</div>
|
||||
{{plugin-outlet name="editor-preview" classNames="d-editor-plugin" args=outletArgs}}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -103,11 +103,12 @@ function loadNext(ajax) {
|
|||
// It will insert a loading indicator and remove it when the loading is complete or fails.
|
||||
export function load({
|
||||
elem,
|
||||
refresh = true,
|
||||
ajax,
|
||||
synchronous = false,
|
||||
categoryId,
|
||||
topicId,
|
||||
categoryId,
|
||||
refresh = true,
|
||||
offline = false,
|
||||
synchronous = false,
|
||||
}) {
|
||||
const $elem = $(elem);
|
||||
|
||||
|
@ -134,6 +135,10 @@ export function load({
|
|||
if (failed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (offline) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the loading CSS class
|
||||
|
|
|
@ -173,15 +173,24 @@ function _loadShortUrls(uploads, ajax, siteSettings, opts) {
|
|||
);
|
||||
}
|
||||
|
||||
const SHORT_URL_ATTRIBUTES =
|
||||
"img[data-orig-src], a[data-orig-href], source[data-orig-src]";
|
||||
|
||||
export function resolveCachedShortUrls(siteSettings, scope, opts) {
|
||||
let shortUploadElements = scope.querySelectorAll(SHORT_URL_ATTRIBUTES);
|
||||
|
||||
if (shortUploadElements.length > 0) {
|
||||
_loadCachedShortUrls(shortUploadElements, siteSettings, opts);
|
||||
}
|
||||
}
|
||||
|
||||
export function resolveAllShortUrls(ajax, siteSettings, scope, opts) {
|
||||
const attributes =
|
||||
"img[data-orig-src], a[data-orig-href], source[data-orig-src]";
|
||||
let shortUploadElements = scope.querySelectorAll(attributes);
|
||||
let shortUploadElements = scope.querySelectorAll(SHORT_URL_ATTRIBUTES);
|
||||
|
||||
if (shortUploadElements.length > 0) {
|
||||
_loadCachedShortUrls(shortUploadElements, siteSettings, opts);
|
||||
|
||||
shortUploadElements = scope.querySelectorAll(attributes);
|
||||
shortUploadElements = scope.querySelectorAll(SHORT_URL_ATTRIBUTES);
|
||||
if (shortUploadElements.length > 0) {
|
||||
// this is carefully batched so we can do a leading debounce (trigger right away)
|
||||
return discourseDebounce(
|
||||
|
|
|
@ -2184,6 +2184,8 @@ en:
|
|||
max_allowed_message_recipients: "Maximum recipients allowed in a message."
|
||||
watched_words_regular_expressions: "Watched words are regular expressions."
|
||||
|
||||
enable_diffhtml_preview: "Experimental feature which uses diffHTML to sync preview instead of full re-render"
|
||||
|
||||
old_post_notice_days: "Days before post notice becomes old"
|
||||
new_user_notice_tl: "Minimum trust level required to see new user post notices."
|
||||
returning_user_notice_tl: "Minimum trust level required to see returning user post notices."
|
||||
|
|
|
@ -976,6 +976,10 @@ posting:
|
|||
hidden: true
|
||||
default: false
|
||||
client: true
|
||||
enable_diffhtml_preview:
|
||||
hidden: true
|
||||
default: false
|
||||
client: true
|
||||
old_post_notice_days:
|
||||
default: 14
|
||||
max: 36500
|
||||
|
|
|
@ -76,6 +76,9 @@ def dependencies
|
|||
}, {
|
||||
source: 'chartjs-plugin-datalabels/dist/chartjs-plugin-datalabels.min.js',
|
||||
public: true
|
||||
}, {
|
||||
source: 'diffhtml/dist/diffhtml.min.js',
|
||||
public: true
|
||||
}, {
|
||||
source: 'magnific-popup/dist/jquery.magnific-popup.min.js',
|
||||
public: true
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
"bootstrap": "v3.4.1",
|
||||
"chart.js": "2.9.3",
|
||||
"chartjs-plugin-datalabels": "^0.7.0",
|
||||
"diffhtml": "^1.0.0-beta.18",
|
||||
"eslint-config-discourse": "^1.1.8",
|
||||
"handlebars": "^4.7.0",
|
||||
"highlight.js": "https://github.com/highlightjs/highlight.js",
|
||||
|
|
1
public/javascripts/diffhtml.min.js
vendored
Normal file
1
public/javascripts/diffhtml.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
public/javascripts/diffhtml/1.0.0-beta.18/diffhtml.min.js
vendored
Normal file
1
public/javascripts/diffhtml/1.0.0-beta.18/diffhtml.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -881,6 +881,13 @@ diff@^4.0.2:
|
|||
resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
|
||||
integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
|
||||
|
||||
diffhtml@^1.0.0-beta.18:
|
||||
version "1.0.0-beta.18"
|
||||
resolved "https://registry.yarnpkg.com/diffhtml/-/diffhtml-1.0.0-beta.18.tgz#b5255f6eb9e358fa279b423ea7d168b061a9146d"
|
||||
integrity sha512-DEsiAiSNxTE7vTpfRtT4SPm1cxQRahIbzvLXNKquOh95+BQOS24IGX6s7sJihYgk7XN6cYy2xqMwO+pEDWGtpg==
|
||||
dependencies:
|
||||
"@babel/plugin-proposal-class-properties" "^7.10.4"
|
||||
|
||||
dir-glob@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f"
|
||||
|
|
Loading…
Reference in New Issue
Block a user