discourse/app/assets/javascripts/pretty-text/oneboxer.js.es6
Martin Brennan d0246104ee
FIX: oneboxer.js infinitely retrying failed requests (#8414)
* setFailedCache was used like a variable object, when it was in fact a function
2019-11-26 15:49:58 +10:00

144 lines
3.3 KiB
JavaScript

import { later } from "@ember/runloop";
import {
localCache,
failedCache,
setLocalCache,
setFailedCache,
resetLocalCache,
resetFailedCache,
normalize
} from "pretty-text/oneboxer-cache";
let timeout;
const loadingQueue = [];
export const LOADING_ONEBOX_CSS_CLASS = "loading-onebox";
export function resetCache() {
loadingQueue.clear();
resetLocalCache();
resetFailedCache();
}
function resolveSize(img) {
$(img).addClass("size-resolved");
if (img.width > 0 && img.width === img.height) {
$(img).addClass("onebox-avatar");
}
}
// Detect square images and apply smaller onebox-avatar class
function applySquareGenericOnebox($elem, normalizedUrl) {
if (!$elem.hasClass("whitelistedgeneric")) {
return;
}
let $img = $elem.find(".onebox-body img.thumbnail");
let img = $img[0];
// already resolved... skip
if ($img.length !== 1 || $img.hasClass("size-resolved")) {
return;
}
if (img.complete) {
resolveSize(img, $elem, normalizedUrl);
} else {
$img.on("load.onebox", () => {
resolveSize(img, $elem, normalizedUrl);
$img.off("load.onebox");
});
}
}
function loadNext(ajax) {
if (loadingQueue.length === 0) {
timeout = null;
return;
}
let timeoutMs = 150;
let removeLoading = true;
const { url, refresh, $elem, categoryId, topicId } = loadingQueue.shift();
// Retrieve the onebox
return ajax("/onebox", {
dataType: "html",
data: {
url,
refresh,
category_id: categoryId,
topic_id: topicId
},
cache: true
})
.then(
html => {
let $html = $(html);
setLocalCache(normalize(url), $html);
$elem.replaceWith($html);
applySquareGenericOnebox($html, normalize(url));
},
result => {
if (result && result.jqXHR && result.jqXHR.status === 429) {
timeoutMs = 2000;
removeLoading = false;
loadingQueue.unshift({ url, refresh, $elem, categoryId, topicId });
} else {
setFailedCache(normalize(url), true);
}
}
)
.finally(() => {
timeout = later(() => loadNext(ajax), timeoutMs);
if (removeLoading) {
$elem.removeClass(LOADING_ONEBOX_CSS_CLASS);
$elem.data("onebox-loaded");
}
});
}
// Perform a lookup of a onebox based an anchor $element.
// 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
}) {
const $elem = $(elem);
// If the onebox has loaded or is loading, return
if ($elem.data("onebox-loaded")) return;
if ($elem.hasClass(LOADING_ONEBOX_CSS_CLASS)) return;
const url = elem.href;
// Unless we're forcing a refresh...
if (!refresh) {
// If we have it in our cache, return it.
const cached = localCache[normalize(url)];
if (cached) return cached.prop("outerHTML");
// If the request failed, don't do anything
const failed = failedCache[normalize(url)];
if (failed) return;
}
// Add the loading CSS class
$elem.addClass(LOADING_ONEBOX_CSS_CLASS);
// Add to the loading queue
loadingQueue.push({ url, refresh, $elem, categoryId, topicId });
// Load next url in queue
if (synchronous) {
return loadNext(ajax);
} else {
timeout = timeout || later(() => loadNext(ajax), 150);
}
}