Files
jank-client-fork/src/webpage/embed.ts
2025-03-27 21:20:19 -05:00

407 lines
12 KiB
TypeScript

import {Message} from "./message.js";
import {MarkDown} from "./markdown.js";
import {embedjson, invitejson} from "./jsontypes.js";
import {getapiurls, getBulkUsers, getInstances, Specialuser} from "./utils/utils.js";
import {Guild} from "./guild.js";
import {I18n} from "./i18n.js";
import {ImagesDisplay} from "./disimg.js";
import {File} from "./file.js";
class Embed {
type: string;
owner: Message;
json: embedjson;
constructor(json: embedjson, owner: Message) {
this.type = this.getType(json);
this.owner = owner;
this.json = json;
}
getType(json: embedjson) {
const users = Object.values(getBulkUsers().users) as Specialuser[];
const instances = getInstances()
?.map((_) => _.url)
.filter((_) => undefined !== _)
.concat(users.map((_) => _.serverurls.wellknown));
if (instances && json.type === "link" && json.url && URL.canParse(json.url)) {
const Url = new URL(json.url);
for (const instance of instances) {
if (instance && URL.canParse(instance)) {
const IUrl = new URL(instance);
const params = new URLSearchParams(Url.search);
let host: string;
if (params.has("instance")) {
const url = params.get("instance") as string;
if (URL.canParse(url)) {
host = new URL(url).host;
} else {
host = Url.host;
}
} else {
host = Url.host;
}
if (IUrl.host === host) {
const code = Url.pathname.split("/")[Url.pathname.split("/").length - 1];
json.invite = {
url: instance,
code,
};
return "invite";
}
}
}
}
return json.type || "rich";
}
generateHTML() {
switch (this.type) {
case "rich":
return this.generateRich();
case "image":
return this.generateImage();
case "invite":
return this.generateInvite();
case "link":
return this.generateLink();
case "video":
case "article":
return this.generateArticle();
default:
console.warn(`unsupported embed type ${this.type}, please add support dev :3`, this.json);
return document.createElement("div"); //prevent errors by giving blank div
}
}
get message() {
return this.owner;
}
get channel() {
return this.message.channel;
}
get guild() {
return this.channel.guild;
}
get localuser() {
return this.guild.localuser;
}
generateRich() {
const div = document.createElement("div");
if (this.json.color) {
div.style.backgroundColor = "#" + this.json.color.toString(16);
}
div.classList.add("embed-color");
const embed = document.createElement("div");
embed.classList.add("embed");
div.append(embed);
if (this.json.author) {
const authorline = document.createElement("div");
if (this.json.author.icon_url) {
const img = document.createElement("img");
img.classList.add("authorEmbedImg");
img.src = this.json.author.icon_url;
authorline.append(img);
}
const a = document.createElement("a");
a.textContent = this.json.author.name as string;
if (this.json.author.url) {
MarkDown.safeLink(a, this.json.author.url);
}
a.classList.add("username");
authorline.append(a);
embed.append(authorline);
}
if (this.json.title) {
const title = document.createElement("a");
title.append(new MarkDown(this.json.title, this.channel).makeHTML());
if (this.json.url) {
MarkDown.safeLink(title, this.json.url);
}
title.classList.add("embedtitle");
embed.append(title);
}
if (this.json.description) {
const p = document.createElement("p");
p.append(new MarkDown(this.json.description, this.channel).makeHTML());
embed.append(p);
}
embed.append(document.createElement("br"));
if (this.json.fields) {
for (const thing of this.json.fields) {
const div = document.createElement("div");
const b = document.createElement("b");
b.textContent = thing.name;
div.append(b);
const p = document.createElement("p");
p.append(new MarkDown(thing.value, this.channel).makeHTML());
p.classList.add("embedp");
div.append(p);
if (thing.inline) {
div.classList.add("inline");
}
embed.append(div);
}
}
if (this.json.footer || this.json.timestamp) {
const footer = document.createElement("div");
if (this.json?.footer?.icon_url) {
const img = document.createElement("img");
img.src = this.json.footer.icon_url;
img.classList.add("embedicon");
footer.append(img);
}
if (this.json?.footer?.text) {
const span = document.createElement("span");
span.textContent = this.json.footer.text;
footer.append(span);
}
if (this.json?.footer && this.json?.timestamp) {
const span = document.createElement("span");
span.textContent = " • ";
footer.append(span);
}
if (this.json?.timestamp) {
const span = document.createElement("span");
span.textContent = new Date(this.json.timestamp).toLocaleString();
footer.append(span);
}
embed.append(footer);
}
return div;
}
generateImage() {
const img = document.createElement("img");
img.classList.add("messageimg");
img.onclick = function () {
const full = new ImagesDisplay([
new File({id: "", filename: "", url: img.src, size: -1, content_type: "image"}, null),
]);
full.show();
};
img.src = this.json.thumbnail.proxy_url;
if (this.json.thumbnail.width) {
let scale = 1;
const max = 96 * 3;
scale = Math.max(scale, this.json.thumbnail.width / max);
scale = Math.max(scale, this.json.thumbnail.height / max);
this.json.thumbnail.width /= scale;
this.json.thumbnail.height /= scale;
}
img.style.width = this.json.thumbnail.width + "px";
img.style.height = this.json.thumbnail.height + "px";
console.log(this.json, "Image fix");
return img;
}
generateLink() {
const table = document.createElement("table");
table.classList.add("embed", "linkembed");
const trtop = document.createElement("tr");
table.append(trtop);
if (this.json.url && this.json.title) {
const td = document.createElement("td");
const a = document.createElement("a");
MarkDown.safeLink(a, this.json.url);
a.textContent = this.json.title;
td.append(a);
trtop.append(td);
}
{
const td = document.createElement("td");
const img = document.createElement("img");
if (this.json.thumbnail) {
img.classList.add("embedimg");
img.onclick = function () {
const full = new ImagesDisplay([
new File({id: "", filename: "", url: img.src, size: -1, content_type: "image"}, null),
]);
full.show();
};
img.src = this.json.thumbnail.proxy_url;
td.append(img);
}
trtop.append(td);
}
const bottomtr = document.createElement("tr");
const td = document.createElement("td");
if (this.json.description) {
const span = document.createElement("span");
span.textContent = this.json.description;
td.append(span);
}
bottomtr.append(td);
table.append(bottomtr);
return table;
}
invcache: [invitejson, {cdn: string; api: string}] | undefined;
generateInvite() {
if (this.invcache && (!this.json.invite || !this.localuser)) {
return this.generateLink();
}
const div = document.createElement("div");
div.classList.add("embed", "inviteEmbed", "flexttb");
const json1 = this.json.invite;
(async () => {
let json: invitejson;
let info: {cdn: string; api: string};
if (!this.invcache) {
if (!json1) {
div.classList.remove("embed", "inviteEmbed", "flexttb");
div.append(this.generateLink());
return;
}
const tempinfo = await getapiurls(json1.url);
if (!tempinfo) {
div.classList.remove("embed", "inviteEmbed", "flexttb");
div.append(this.generateLink());
return;
}
info = tempinfo;
const res = await fetch(info.api + "/invites/" + json1.code);
if (!res.ok) {
div.classList.remove("embed", "inviteEmbed", "flexttb");
div.append(this.generateLink());
return;
}
json = (await res.json()) as invitejson;
this.invcache = [json, info];
} else {
[json, info] = this.invcache;
}
if (!json) {
div.append(this.generateLink());
div.classList.remove("embed", "inviteEmbed", "flexttb");
return;
}
if (json.guild.banner) {
const banner = document.createElement("img");
banner.src =
info.cdn + "/icons/" + json.guild.id + "/" + json.guild.banner + ".png?size=256";
banner.classList.add("banner");
div.append(banner);
}
const guild: invitejson["guild"] & {info?: {cdn: string}} = json.guild;
guild.info = info;
const icon = Guild.generateGuildIcon(guild as invitejson["guild"] & {info: {cdn: string}});
const iconrow = document.createElement("div");
iconrow.classList.add("flexltr");
iconrow.append(icon);
{
const guildinfo = document.createElement("div");
guildinfo.classList.add("flexttb", "invguildinfo");
const name = document.createElement("b");
name.textContent = guild.name;
guildinfo.append(name);
const members = document.createElement("span");
members.innerText = "#" + json.channel.name + " • Members: " + guild.member_count;
guildinfo.append(members);
members.classList.add("subtext");
iconrow.append(guildinfo);
}
div.append(iconrow);
const h2 = document.createElement("h2");
h2.textContent = I18n.getTranslation("invite.invitedBy", json.inviter.username);
div.append(h2);
const button = document.createElement("button");
button.textContent = I18n.getTranslation("invite.accept");
if (this.localuser.info.api.startsWith(info.api) && this.localuser.guildids.has(guild.id)) {
button.textContent = I18n.getTranslation("invite.alreadyJoined");
button.disabled = true;
}
button.classList.add("acceptinvbutton");
div.append(button);
button.onclick = (_) => {
if (this.localuser.info.api.startsWith(info.api)) {
fetch(this.localuser.info.api + "/invites/" + json.code, {
method: "POST",
headers: this.localuser.headers,
})
.then((r) => r.json())
.then((_) => {
if (_.message) {
alert(_.message);
}
});
} else {
if (this.json.invite) {
const params = new URLSearchParams("");
params.set("instance", this.json.invite.url);
const encoded = params.toString();
const url = `${location.origin}/invite/${this.json.invite.code}?${encoded}`;
window.open(url, "_blank");
}
}
};
})();
return div;
}
generateArticle() {
const colordiv = document.createElement("div");
colordiv.style.backgroundColor = "#000000";
colordiv.classList.add("embed-color");
const div = document.createElement("div");
div.classList.add("embed");
if (this.json.provider) {
const provider = document.createElement("p");
provider.classList.add("provider");
provider.textContent = this.json.provider.name;
div.append(provider);
}
const a = document.createElement("a");
if (this.json.url && this.json.url) {
MarkDown.safeLink(a, this.json.url);
a.textContent = this.json.url;
div.append(a);
}
if (this.json.description) {
const description = document.createElement("p");
description.textContent = this.json.description;
div.append(description);
}
if (this.json.thumbnail) {
const img = document.createElement("img");
if (this.json.thumbnail.width && this.json.thumbnail.width) {
let scale = 1;
const inch = 96;
scale = Math.max(scale, this.json.thumbnail.width / inch / 4);
scale = Math.max(scale, this.json.thumbnail.height / inch / 3);
this.json.thumbnail.width /= scale;
this.json.thumbnail.height /= scale;
img.style.width = this.json.thumbnail.width + "px";
img.style.height = this.json.thumbnail.height + "px";
}
img.classList.add("bigembedimg");
if (this.json.video) {
img.onclick = async () => {
if (this.json.video) {
img.remove();
const iframe = document.createElement("iframe");
iframe.src = this.json.video.url + "?autoplay=1";
if (this.json.thumbnail.width && this.json.thumbnail.height) {
iframe.style.width = this.json.thumbnail.width + "px";
iframe.style.height = this.json.thumbnail.height + "px";
}
div.append(iframe);
}
};
} else {
img.onclick = async () => {
const full = new ImagesDisplay([
new File({id: "", filename: "", url: img.src, size: -1, content_type: "image"}, null),
]);
full.show();
};
}
img.src = this.json.thumbnail.proxy_url || this.json.thumbnail.url;
div.append(img);
}
colordiv.append(div);
return colordiv;
}
}
export {Embed};