underbbs/frontend/ts/message-element.ts

111 lines
No EOL
4.4 KiB
TypeScript

import util from "./util"
import { Message } from "./message"
import { BatchTimer } from "./batch-timer"
import { AdapterState } from "./adapter"
export class MessageElement extends HTMLElement {
static observedAttributes = [ "data-target", "data-latest", "data-adapter", "data-replyCt", "data-reactionCt", "data-boostCt" ]
private _id: string | null = null;
private _adapter: string | null = null;
private _message: Message | null = null;
private _messageTimer: BatchTimer | null = null;
constructor() {
super();
this.innerHTML = `<div class="message_metadata"></div><div class="message_content"></div><div class="message_attachments"></div>`
}
connectedCallback() {
this._id = this.getAttribute("data-target");
this._adapter = this.getAttribute("data-adapter");
const gateway = this.getAttribute("data-gateway") ?? "";
this._messageTimer = new BatchTimer(gateway, this._adapter ?? "", "message");
}
attributeChangedCallback(attr: string, prev: string, next: string) {
switch (attr) {
case "data-target":
if (!next) {
return
}
this._id = next;
this._message = null;
this.innerHTML = `<div class="message_metadata"></div><div class="message_content"></div><div class="message_attachments"></div>`;
if (this._messageTimer) {
this._messageTimer.queue(next, 100);
}
break;
case "data-latest":
let datastore = AdapterState._instance.data.get(this._adapter ?? "");
if (!datastore) {
console.log("no data yet, wait for some to come in maybe...");
return;
}
let msg = datastore.messages.get(next);
console.log("MessageElement.attributeChangedCallback: " + JSON.stringify(msg));
if (msg) {
this._message = msg;
const metadata = this.querySelector(".message_metadata");
const content = this.querySelector(".message_content");
const attachments = this.querySelector(".message_attachments");
if (metadata) {
if (this._message.renoteId) {
metadata.innerHTML = `<span class="message_renoter">${this._message.renoter}</span><span class="message_renotetime">${new Date(this._message.renoteTime ?? 0)}</span>`
} else {
metadata.innerHTML = "";
}
metadata.innerHTML += `<span class="message_author">${this._message.author}</span><span class="message_timestamp">${new Date(this._message.created)}</span><span class="message_visibility">${this._message.visibility}</span><span class="message_protocol">${this._message.protocol}</span>`
}
if (content) {
content.innerHTML = this._message.content;
}
if (attachments && this._message.attachments.length > 0) {
let html = "<ul>";
for (const a of this._message.attachments) {
// we can do it based on actual mimetype later but now let's just do an extension check
const srcUrl = new URL(a.src);
const pathParts = srcUrl.pathname.split(".");
if (pathParts.length < 2) {
html += `<li><a href="${a.src}">${a.desc}</a></li>`
continue;
}
const ext = pathParts[pathParts.length - 1];
switch (ext.toLowerCase()) {
case "jpg":
case "jpeg":
case "png":
case "gif":
case "avif":
case "svg":
case "bmp":
html += `<li><a href="${a.src}"><img src="${a.src}" alt="${a.desc}"/></a>`
break;
case "mp3":
case "wav":
case "ogg":
case "opus":
case "aac":
case "flac":
html += `<li><audio src="${a.src}" controls preload="metadata"><a href="${a.src}">${a.desc}</a></audio></li>`
break;
case "mp4":
case "mkv":
case "avi":
case "mov":
html += `<li><video src="${a.src}" controls preload="metadata"><a href="${a.src}">${a.desc}</a></video></li>`
break;
default:
html += `<li><a href="${a.src}">${a.desc}</a></li>`
}
}
html += "</ul>";
attachments.innerHTML = html;
}
}
break;
}
}
}