diff options
Diffstat (limited to 'FrontEnd')
-rw-r--r-- | FrontEnd/package.json | 2 | ||||
-rw-r--r-- | FrontEnd/src/app/services/TimelinePostBuilder.ts | 120 | ||||
-rw-r--r-- | FrontEnd/src/sw/sw.ts | 8 |
3 files changed, 125 insertions, 5 deletions
diff --git a/FrontEnd/package.json b/FrontEnd/package.json index 7120daf7..f1a1ea18 100644 --- a/FrontEnd/package.json +++ b/FrontEnd/package.json @@ -104,7 +104,7 @@ "sass": "^1.32.8",
"sass-loader": "^11.0.1",
"style-loader": "^2.0.0",
- "ts-loader": "^8.0.17",
+ "ts-loader": "^8.0.18",
"type-fest": "^0.21.3",
"typescript": "^4.2.3",
"url-loader": "^4.1.1",
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, + })) + ) + )), + ]; + } +} diff --git a/FrontEnd/src/sw/sw.ts b/FrontEnd/src/sw/sw.ts index dadddfdf..724804dd 100644 --- a/FrontEnd/src/sw/sw.ts +++ b/FrontEnd/src/sw/sw.ts @@ -20,10 +20,10 @@ precacheAndRoute(self.__WB_MANIFEST); const networkOnly = new NetworkOnly(); -registerRoute(new RegExp("/swagger/.+"), new NetworkOnly()); +registerRoute(new RegExp("/swagger/?.*"), new NetworkOnly()); -registerRoute(new RegExp("/api/token/.+"), new NetworkOnly()); -registerRoute(new RegExp("/api/search/.+"), new NetworkOnly()); +registerRoute(new RegExp("/api/token/?.*"), new NetworkOnly()); +registerRoute(new RegExp("/api/search/?.*"), new NetworkOnly()); registerRoute( new RegExp("/api/users/.+/avatar"), @@ -41,7 +41,7 @@ registerRoute( ); registerRoute( - new RegExp("/api/.+"), + new RegExp("/api/?.*"), new NetworkFirst({ plugins: [ new CacheableResponsePlugin({ |