DEV: Use async/await in checklist (#22943)

This commit is contained in:
Jarek Radosz 2023-08-02 23:24:20 +02:00 committed by GitHub
parent 7405aae85a
commit f9b4cfe67e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 79 additions and 80 deletions

View File

@ -39,13 +39,16 @@ function addUlClasses(boxes) {
) { ) {
parent = parent.parentElement; parent = parent.parentElement;
} }
if (parent.nodeName === "LI" && parent.parentElement.nodeName === "UL") {
if (!hasPrecedingContent(val)) { if (
parent.classList.add("has-checkbox"); parent.nodeName === "LI" &&
val.classList.add("list-item-checkbox"); parent.parentElement.nodeName === "UL" &&
if (!val.nextSibling) { !hasPrecedingContent(val)
val.insertAdjacentHTML("afterend", "&#8203;"); // Ensure otherwise empty <li> does not collapse height ) {
} parent.classList.add("has-checkbox");
val.classList.add("list-item-checkbox");
if (!val.nextSibling) {
val.insertAdjacentHTML("afterend", "&#8203;"); // Ensure otherwise empty <li> does not collapse height
} }
} }
}); });
@ -67,8 +70,8 @@ export function checklistSyntax(elem, postDecorator) {
} }
boxes.forEach((val, idx) => { boxes.forEach((val, idx) => {
val.onclick = function (ev) { val.onclick = async (event) => {
const box = ev.currentTarget; const box = event.currentTarget;
const classList = box.classList; const classList = box.classList;
if (classList.contains("permanent") || classList.contains("readonly")) { if (classList.contains("permanent") || classList.contains("readonly")) {
@ -83,88 +86,82 @@ export function checklistSyntax(elem, postDecorator) {
box.classList.add("hidden"); box.classList.add("hidden");
boxes.forEach((e) => e.classList.add("readonly")); boxes.forEach((e) => e.classList.add("readonly"));
ajax(`/posts/${postModel.id}`, { type: "GET", cache: false }) try {
.then((result) => { const post = await ajax(`/posts/${postModel.id}`);
const blocks = []; const blocks = [];
// Computing offsets where checkbox are not evaluated (i.e. inside // Computing offsets where checkbox are not evaluated (i.e. inside
// code blocks). // code blocks).
[ [
// inline code // inline code
/`[^`\n]*\n?[^`\n]*`/gm, /`[^`\n]*\n?[^`\n]*`/gm,
// multi-line code // multi-line code
/^```[^]*?^```/gm, /^```[^]*?^```/gm,
// bbcode // bbcode
/\[code\][^]*?\[\/code\]/gm, /\[code\][^]*?\[\/code\]/gm,
// italic/bold // italic/bold
/_(?=\S).*?\S_/gm, /_(?=\S).*?\S_/gm,
// strikethrough // strikethrough
/~~(?=\S).*?\S~~/gm, /~~(?=\S).*?\S~~/gm,
].forEach((regex) => { ].forEach((regex) => {
let match; let match;
while ((match = regex.exec(result.raw)) != null) { while ((match = regex.exec(post.raw)) != null) {
blocks.push([match.index, match.index + match[0].length]); blocks.push([match.index, match.index + match[0].length]);
} }
}); });
[ [
// italic/bold // italic/bold
/([^\[\n]|^)\*\S.+?\S\*(?=[^\]\n]|$)/gm, /([^\[\n]|^)\*\S.+?\S\*(?=[^\]\n]|$)/gm,
].forEach((regex) => { ].forEach((regex) => {
let match; let match;
while ((match = regex.exec(result.raw)) != null) { while ((match = regex.exec(post.raw)) != null) {
// Simulate lookbehind - skip the first character // Simulate lookbehind - skip the first character
blocks.push([match.index + 1, match.index + match[0].length]); blocks.push([match.index + 1, match.index + match[0].length]);
} }
}); });
// make the first run go to index = 0
let nth = -1;
let found = false;
const newRaw = result.raw.replace(
/\[(\s|\_|\-|\x|\\?\*)?\]/gi,
(match, ignored, off) => {
if (found) {
return match;
}
nth += blocks.every(
(b) => b[0] >= off + match.length || off > b[1]
);
if (nth === idx) {
found = true; // Do not replace any further matches
return newValue;
}
// make the first run go to index = 0
let nth = -1;
let found = false;
const newRaw = post.raw.replace(
/\[(\s|\_|\-|\x|\\?\*)?\]/gi,
(match, ignored, off) => {
if (found) {
return match; return match;
} }
);
const save = postModel.save({ nth += blocks.every(
raw: newRaw, (b) => b[0] >= off + match.length || off > b[1]
edit_reason: I18n.t("checklist.edit_reason"), );
});
if (save && save.then) { if (nth === idx) {
save found = true; // Do not replace any further matches
.then(() => { return newValue;
postWidget.attrs.isSaving = false; }
postWidget.scheduleRerender();
}) return match;
.finally(() => removeReadonlyClass(boxes));
} else {
removeReadonlyClass(boxes);
} }
}) );
.catch(() => removeReadonlyClass(boxes));
await postModel.save({
raw: newRaw,
edit_reason: I18n.t("checklist.edit_reason"),
});
postWidget.attrs.isSaving = false;
postWidget.scheduleRerender();
} finally {
removeReadonlyClass(boxes);
}
}; };
}); });
} }
export default { export default {
name: "checklist", name: "checklist",
initialize: function () {
initialize() {
withPluginApi("0.1", (api) => initializePlugin(api)); withPluginApi("0.1", (api) => initializePlugin(api));
}, },
}; };

View File

@ -1,3 +1,3 @@
en: en:
site_settings: site_settings:
checklist_enabled: 'Enable checklist plugin?' checklist_enabled: "Enable checklist plugin?"

View File

@ -11,8 +11,10 @@ async function prepare(raw) {
const cooked = await cookAsync(raw, { const cooked = await cookAsync(raw, {
siteSettings: { checklist_enabled: true }, siteSettings: { checklist_enabled: true },
}); });
const widget = { attrs: {}, scheduleRerender() {} };
const model = Post.create({ id: 42, can_edit: true }); const model = Post.create({ id: 42, can_edit: true });
const decoratorHelper = { getModel: () => model }; const decoratorHelper = { widget, getModel: () => model };
const $elem = $(`<div>${cooked.string}</div>`); const $elem = $(`<div>${cooked.string}</div>`);
checklistSyntax($elem[0], decoratorHelper); checklistSyntax($elem[0], decoratorHelper);
@ -20,7 +22,7 @@ async function prepare(raw) {
currentRaw = raw; currentRaw = raw;
const updated = new Promise((resolve) => { const updated = new Promise((resolve) => {
model.save = (fields) => resolve(fields.raw); model.save = async (fields) => resolve(fields.raw);
}); });
return [$elem, updated]; return [$elem, updated];