import {
escapeHtml,
replaceEntities,
unescapeMd,
} from "remarkable/lib/common/utils";
import { Remarkable } from "remarkable";
import { UiLogicError } from "@/common";
import { base64 } from "@/http/common";
import { HttpTimelinePostPostRequest } from "@/http/timeline";
export class TimelinePostBuilder {
private _onChange: () => void;
private _text = "";
private _images: { file: File; url: string }[] = [];
private _md: Remarkable = new Remarkable();
constructor(onChange: () => void) {
this._onChange = onChange;
this._md.renderer.rules.image = ((
_t: TimelinePostBuilder
): Remarkable.Rule =>
function (tokens, idx, options /*, env */) {
const i = parseInt(tokens[idx].src);
const src =
' src="' +
(isNaN(i) && i > 0 && i <= _t._images.length
? escapeHtml(tokens[idx].src)
: _t._images[i - 1].url) +
'"';
const title = tokens[idx].title
? ' title="' + escapeHtml(replaceEntities(tokens[idx].title)) + '"'
: "";
const alt =
' alt="' +
(tokens[idx].alt
? escapeHtml(replaceEntities(unescapeMd(tokens[idx].alt)))
: "") +
'"';
const suffix = options?.xhtmlOut ? " /" : "";
return "
";
})(this);
}
setMarkdownText(text: string): void {
this._text = text;
this._onChange();
}
appendImage(file: File): void {
this._images.push({
file,
url: URL.createObjectURL(file),
});
this._onChange();
}
moveImage(oldIndex: number, newIndex: number): void {
if (oldIndex < 0 || oldIndex >= this._images.length) {
throw new UiLogicError("Old index out of range.");
}
if (newIndex < 0) {
newIndex = 0;
}
if (newIndex >= this._images.length) {
newIndex = this._images.length - 1;
}
const [old] = this._images.splice(oldIndex, 1);
this._images.splice(newIndex, 0, old);
this._onChange();
}
deleteImage(index: number): void {
if (index < 0 || index >= this._images.length) {
throw new UiLogicError("Old index out of range.");
}
URL.revokeObjectURL(this._images[index].url);
this._images.splice(index, 1);
this._onChange();
}
get images(): { file: File; url: string }[] {
return this._images;
}
renderHtml(): string {
return this._md.render(this._text);
}
dispose(): void {
for (const image of this._images) {
URL.revokeObjectURL(image.url);
}
this._images = [];
}
async build(): Promise {
return [
{
contentType: "text/markdown",
data: await base64(this._text),
},
...(await Promise.all(
this._images.map((image) =>
base64(image.file).then((data) => ({
contentType: image.file.type,
data,
}))
)
)),
];
}
}