diff options
author | crupest <crupest@outlook.com> | 2021-03-12 17:14:57 +0800 |
---|---|---|
committer | crupest <crupest@outlook.com> | 2021-03-12 17:14:57 +0800 |
commit | 6d35c11754b3840a1debf36ce1ab8df3ec4fc491 (patch) | |
tree | c39cf5b1ce36c7ce18e9f9cb50ed7035adb298cd | |
parent | 5742cdaf5a0285725b630a769626c7f72cf635d8 (diff) | |
download | timeline-6d35c11754b3840a1debf36ce1ab8df3ec4fc491.tar.gz timeline-6d35c11754b3840a1debf36ce1ab8df3ec4fc491.tar.bz2 timeline-6d35c11754b3840a1debf36ce1ab8df3ec4fc491.zip |
feat: Add TimelinePostBuilder.
-rw-r--r-- | FrontEnd/src/app/services/TimelinePostBuilder.ts | 120 |
1 files changed, 120 insertions, 0 deletions
diff --git a/FrontEnd/src/app/services/TimelinePostBuilder.ts b/FrontEnd/src/app/services/TimelinePostBuilder.ts new file mode 100644 index 00000000..8594fa49 --- /dev/null +++ b/FrontEnd/src/app/services/TimelinePostBuilder.ts @@ -0,0 +1,120 @@ +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<Remarkable.ImageToken, string> => + 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 "<img" + src + alt + title + suffix + ">"; + })(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<HttpTimelinePostPostRequest["dataList"]> { + 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, + })) + ) + )), + ]; + } +} |