mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06: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 { getRegister } from "discourse-common/lib/get-owner";
|
||||||
import { isEmpty } from "@ember/utils";
|
import { isEmpty } from "@ember/utils";
|
||||||
import { isTesting } from "discourse-common/config/environment";
|
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 { search as searchCategoryTag } from "discourse/lib/category-tag-search";
|
||||||
import { inject as service } from "@ember/service";
|
import { inject as service } from "@ember/service";
|
||||||
import showModal from "discourse/lib/show-modal";
|
import showModal from "discourse/lib/show-modal";
|
||||||
@ -389,6 +394,32 @@ export default Component.extend({
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.set("preview", cooked);
|
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", () => {
|
schedule("afterRender", () => {
|
||||||
if (this._state !== "inDOM") {
|
if (this._state !== "inDOM") {
|
||||||
return;
|
return;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { TAG_HASHTAG_POSTFIX } from "discourse/lib/tag-hashtags";
|
import { TAG_HASHTAG_POSTFIX } from "discourse/lib/tag-hashtags";
|
||||||
import { ajax } from "discourse/lib/ajax";
|
import { ajax } from "discourse/lib/ajax";
|
||||||
import { replaceSpan } from "discourse/lib/category-hashtags";
|
import { replaceSpan } from "discourse/lib/category-hashtags";
|
||||||
import { schedule } from "@ember/runloop";
|
|
||||||
|
|
||||||
const categoryHashtags = {};
|
const categoryHashtags = {};
|
||||||
const tagHashtags = {};
|
const tagHashtags = {};
|
||||||
@ -15,21 +14,19 @@ export function linkSeenHashtags($elem) {
|
|||||||
|
|
||||||
const slugs = [...$hashtags.map((_, hashtag) => hashtag.innerText.substr(1))];
|
const slugs = [...$hashtags.map((_, hashtag) => hashtag.innerText.substr(1))];
|
||||||
|
|
||||||
schedule("afterRender", () => {
|
$hashtags.each((index, hashtag) => {
|
||||||
$hashtags.each((index, hashtag) => {
|
let slug = slugs[index];
|
||||||
let slug = slugs[index];
|
const hasTagSuffix = slug.endsWith(TAG_HASHTAG_POSTFIX);
|
||||||
const hasTagSuffix = slug.endsWith(TAG_HASHTAG_POSTFIX);
|
if (hasTagSuffix) {
|
||||||
if (hasTagSuffix) {
|
slug = slug.substr(0, slug.length - TAG_HASHTAG_POSTFIX.length);
|
||||||
slug = slug.substr(0, slug.length - TAG_HASHTAG_POSTFIX.length);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const lowerSlug = slug.toLowerCase();
|
const lowerSlug = slug.toLowerCase();
|
||||||
if (categoryHashtags[lowerSlug] && !hasTagSuffix) {
|
if (categoryHashtags[lowerSlug] && !hasTagSuffix) {
|
||||||
replaceSpan($(hashtag), slug, categoryHashtags[lowerSlug]);
|
replaceSpan($(hashtag), slug, categoryHashtags[lowerSlug]);
|
||||||
} else if (tagHashtags[lowerSlug]) {
|
} else if (tagHashtags[lowerSlug]) {
|
||||||
replaceSpan($(hashtag), slug, tagHashtags[lowerSlug]);
|
replaceSpan($(hashtag), slug, tagHashtags[lowerSlug]);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return slugs
|
return slugs
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { ajax } from "discourse/lib/ajax";
|
import { ajax } from "discourse/lib/ajax";
|
||||||
import { formatUsername } from "discourse/lib/utilities";
|
import { formatUsername } from "discourse/lib/utilities";
|
||||||
import getURL from "discourse-common/lib/get-url";
|
import getURL from "discourse-common/lib/get-url";
|
||||||
import { schedule } from "@ember/runloop";
|
|
||||||
import { userPath } from "discourse/lib/url";
|
import { userPath } from "discourse/lib/url";
|
||||||
|
|
||||||
let maxGroupMention;
|
let maxGroupMention;
|
||||||
@ -42,23 +41,21 @@ const checked = {};
|
|||||||
const cannotSee = [];
|
const cannotSee = [];
|
||||||
|
|
||||||
function updateFound($mentions, usernames) {
|
function updateFound($mentions, usernames) {
|
||||||
schedule("afterRender", function () {
|
$mentions.each((i, e) => {
|
||||||
$mentions.each((i, e) => {
|
const $e = $(e);
|
||||||
const $e = $(e);
|
const username = usernames[i];
|
||||||
const username = usernames[i];
|
if (found[username.toLowerCase()]) {
|
||||||
if (found[username.toLowerCase()]) {
|
replaceSpan($e, username, { cannot_see: cannotSee[username] });
|
||||||
replaceSpan($e, username, { cannot_see: cannotSee[username] });
|
} else if (mentionableGroups[username]) {
|
||||||
} else if (mentionableGroups[username]) {
|
replaceSpan($e, username, {
|
||||||
replaceSpan($e, username, {
|
group: true,
|
||||||
group: true,
|
mentionable: mentionableGroups[username],
|
||||||
mentionable: mentionableGroups[username],
|
});
|
||||||
});
|
} else if (foundGroups[username]) {
|
||||||
} else if (foundGroups[username]) {
|
replaceSpan($e, username, { group: true });
|
||||||
replaceSpan($e, username, { group: true });
|
} else if (checked[username]) {
|
||||||
} else if (checked[username]) {
|
$e.addClass("mention-tested");
|
||||||
$e.addClass("mention-tested");
|
}
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,8 @@ export function loadOneboxes(
|
|||||||
topicId,
|
topicId,
|
||||||
categoryId,
|
categoryId,
|
||||||
maxOneboxes,
|
maxOneboxes,
|
||||||
refresh
|
refresh,
|
||||||
|
offline
|
||||||
) {
|
) {
|
||||||
const oneboxes = {};
|
const oneboxes = {};
|
||||||
const inlineOneboxes = {};
|
const inlineOneboxes = {};
|
||||||
@ -41,17 +42,22 @@ export function loadOneboxes(
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let newBoxes = 0;
|
|
||||||
|
|
||||||
if (Object.keys(oneboxes).length > 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) {
|
if (Object.keys(inlineOneboxes).length > 0) {
|
||||||
_loadInlineOneboxes(inlineOneboxes, ajax, topicId, categoryId);
|
_loadInlineOneboxes(inlineOneboxes, ajax, topicId, categoryId);
|
||||||
}
|
}
|
||||||
|
|
||||||
return newBoxes;
|
return Object.keys(oneboxes).length + Object.keys(inlineOneboxes).length;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _loadInlineOneboxes(inline, ajax, topicId, categoryId) {
|
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) => {
|
Object.values(oneboxes).forEach((onebox) => {
|
||||||
onebox.forEach((o) => {
|
onebox.forEach((elem) => {
|
||||||
load({
|
load({
|
||||||
elem: o,
|
elem,
|
||||||
refresh,
|
|
||||||
ajax,
|
ajax,
|
||||||
categoryId: categoryId,
|
categoryId,
|
||||||
topicId: topicId,
|
topicId,
|
||||||
|
refresh,
|
||||||
|
offline,
|
||||||
});
|
});
|
||||||
|
|
||||||
count++;
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ export const PUBLIC_JS_VERSIONS = {
|
|||||||
"Chart.min.js": "chart.js/2.9.3/Chart.min.js",
|
"Chart.min.js": "chart.js/2.9.3/Chart.min.js",
|
||||||
"chartjs-plugin-datalabels.min.js":
|
"chartjs-plugin-datalabels.min.js":
|
||||||
"chartjs-plugin-datalabels/0.7.0/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":
|
"jquery.magnific-popup.min.js":
|
||||||
"magnific-popup/1.1.0/jquery.magnific-popup.min.js",
|
"magnific-popup/1.1.0/jquery.magnific-popup.min.js",
|
||||||
"pikaday.js": "pikaday/1.8.0/pikaday.js",
|
"pikaday.js": "pikaday/1.8.0/pikaday.js",
|
||||||
|
@ -49,7 +49,11 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="d-editor-preview-wrapper {{if forcePreview "force-preview"}}">
|
<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}}
|
{{plugin-outlet name="editor-preview" classNames="d-editor-plugin" args=outletArgs}}
|
||||||
</div>
|
</div>
|
||||||
</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.
|
// It will insert a loading indicator and remove it when the loading is complete or fails.
|
||||||
export function load({
|
export function load({
|
||||||
elem,
|
elem,
|
||||||
refresh = true,
|
|
||||||
ajax,
|
ajax,
|
||||||
synchronous = false,
|
|
||||||
categoryId,
|
|
||||||
topicId,
|
topicId,
|
||||||
|
categoryId,
|
||||||
|
refresh = true,
|
||||||
|
offline = false,
|
||||||
|
synchronous = false,
|
||||||
}) {
|
}) {
|
||||||
const $elem = $(elem);
|
const $elem = $(elem);
|
||||||
|
|
||||||
@ -134,6 +135,10 @@ export function load({
|
|||||||
if (failed) {
|
if (failed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (offline) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the loading CSS class
|
// 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) {
|
export function resolveAllShortUrls(ajax, siteSettings, scope, opts) {
|
||||||
const attributes =
|
let shortUploadElements = scope.querySelectorAll(SHORT_URL_ATTRIBUTES);
|
||||||
"img[data-orig-src], a[data-orig-href], source[data-orig-src]";
|
|
||||||
let shortUploadElements = scope.querySelectorAll(attributes);
|
|
||||||
|
|
||||||
if (shortUploadElements.length > 0) {
|
if (shortUploadElements.length > 0) {
|
||||||
_loadCachedShortUrls(shortUploadElements, siteSettings, opts);
|
_loadCachedShortUrls(shortUploadElements, siteSettings, opts);
|
||||||
|
|
||||||
shortUploadElements = scope.querySelectorAll(attributes);
|
shortUploadElements = scope.querySelectorAll(SHORT_URL_ATTRIBUTES);
|
||||||
if (shortUploadElements.length > 0) {
|
if (shortUploadElements.length > 0) {
|
||||||
// this is carefully batched so we can do a leading debounce (trigger right away)
|
// this is carefully batched so we can do a leading debounce (trigger right away)
|
||||||
return discourseDebounce(
|
return discourseDebounce(
|
||||||
|
@ -2184,6 +2184,8 @@ en:
|
|||||||
max_allowed_message_recipients: "Maximum recipients allowed in a message."
|
max_allowed_message_recipients: "Maximum recipients allowed in a message."
|
||||||
watched_words_regular_expressions: "Watched words are regular expressions."
|
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"
|
old_post_notice_days: "Days before post notice becomes old"
|
||||||
new_user_notice_tl: "Minimum trust level required to see new user post notices."
|
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."
|
returning_user_notice_tl: "Minimum trust level required to see returning user post notices."
|
||||||
|
@ -976,6 +976,10 @@ posting:
|
|||||||
hidden: true
|
hidden: true
|
||||||
default: false
|
default: false
|
||||||
client: true
|
client: true
|
||||||
|
enable_diffhtml_preview:
|
||||||
|
hidden: true
|
||||||
|
default: false
|
||||||
|
client: true
|
||||||
old_post_notice_days:
|
old_post_notice_days:
|
||||||
default: 14
|
default: 14
|
||||||
max: 36500
|
max: 36500
|
||||||
|
@ -76,6 +76,9 @@ def dependencies
|
|||||||
}, {
|
}, {
|
||||||
source: 'chartjs-plugin-datalabels/dist/chartjs-plugin-datalabels.min.js',
|
source: 'chartjs-plugin-datalabels/dist/chartjs-plugin-datalabels.min.js',
|
||||||
public: true
|
public: true
|
||||||
|
}, {
|
||||||
|
source: 'diffhtml/dist/diffhtml.min.js',
|
||||||
|
public: true
|
||||||
}, {
|
}, {
|
||||||
source: 'magnific-popup/dist/jquery.magnific-popup.min.js',
|
source: 'magnific-popup/dist/jquery.magnific-popup.min.js',
|
||||||
public: true
|
public: true
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
"bootstrap": "v3.4.1",
|
"bootstrap": "v3.4.1",
|
||||||
"chart.js": "2.9.3",
|
"chart.js": "2.9.3",
|
||||||
"chartjs-plugin-datalabels": "^0.7.0",
|
"chartjs-plugin-datalabels": "^0.7.0",
|
||||||
|
"diffhtml": "^1.0.0-beta.18",
|
||||||
"eslint-config-discourse": "^1.1.8",
|
"eslint-config-discourse": "^1.1.8",
|
||||||
"handlebars": "^4.7.0",
|
"handlebars": "^4.7.0",
|
||||||
"highlight.js": "https://github.com/highlightjs/highlight.js",
|
"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"
|
resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
|
||||||
integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
|
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:
|
dir-glob@^3.0.1:
|
||||||
version "3.0.1"
|
version "3.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f"
|
resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f"
|
||||||
|
Loading…
Reference in New Issue
Block a user