diff --git a/src/webpage/jsontypes.ts b/src/webpage/jsontypes.ts index 225a593..44f1211 100644 --- a/src/webpage/jsontypes.ts +++ b/src/webpage/jsontypes.ts @@ -488,7 +488,27 @@ type emojipjson = emojijson & { roles: string[]; groups: null; //TODO figure out what this means lol }; - +export interface guildSource { + id: string; + name: string; + icon: string | null; + description: string | null; + splash: string | null; + discovery_splash: string | null; + features: string[]; + vanity_url_code: string | null; + preferred_locale: string; + premium_subscription_count: number; + approximate_member_count: number; + approximate_presence_count: number; + premium_tier: number; + auto_removed: boolean; + primary_category_id: string | null; + keywords: string[] | null; + is_published: boolean; + //TODO later + reasons_to_join: []; +} type guildjson = { application_command_counts: {[key: string]: number}; channels: channeljson[]; diff --git a/src/webpage/message.ts b/src/webpage/message.ts index d2478d1..95bb183 100644 --- a/src/webpage/message.ts +++ b/src/webpage/message.ts @@ -1293,7 +1293,7 @@ class Message extends SnowFlake { const stickerArea = document.createElement("div"); stickerArea.classList.add("flexltr", "stickerMArea"); for (const sticker of this.stickers) { - stickerArea.append(sticker.getHTML()); + stickerArea.append(sticker.getHTML(true)); } text.append(stickerArea); diff --git a/src/webpage/sticker.ts b/src/webpage/sticker.ts index 4c8f287..f77c273 100644 --- a/src/webpage/sticker.ts +++ b/src/webpage/sticker.ts @@ -2,11 +2,11 @@ import {Contextmenu} from "./contextmenu.js"; import {Guild} from "./guild.js"; import {Hover} from "./hover.js"; import {I18n} from "./i18n.js"; -import {stickerJson} from "./jsontypes.js"; +import {guildSource, stickerJson} from "./jsontypes.js"; import {Localuser} from "./localuser.js"; import {SnowFlake} from "./snowflake.js"; import {CDNParams} from "./utils/cdnParams.js"; -import {createImg} from "./utils/utils.js"; +import {createImg, removeAni} from "./utils/utils.js"; class Sticker extends SnowFlake { name: string; @@ -33,7 +33,7 @@ class Sticker extends SnowFlake { this.tags = json.tags; this.description = json.description || ""; } - getHTML(): HTMLElement { + getHTML(noclick: boolean): HTMLElement { const img = createImg( this.owner.info.cdn + "/stickers/" + this.id + ".webp" + new CDNParams({expectedSize: 160}), undefined, @@ -44,6 +44,105 @@ class Sticker extends SnowFlake { const hover = new Hover(this.name); hover.addEvent(img); img.alt = this.description; + if (!noclick) { + img.onclick = async (e) => { + e.preventDefault(); + e.stopImmediatePropagation(); + const div = document.createElement("div"); + div.style.top = e.clientY + "px"; + div.style.left = e.clientX + "px"; + div.classList.add("flexttb", "EmojiGuildMenu"); + + const localuser = this.localuser as Localuser; + const lookup = await Sticker.lookupStickerSource(this.id, this.localuser); + + const top = document.createElement("div"); + top.classList.add("flexltr", "GuildEmojiTop"); + + const toptext = document.createElement("div"); + toptext.classList.add("flexttb"); + div.append(toptext); + + const name = document.createElement("span"); + name.textContent = this.name; + + const desc = document.createElement("span"); + + toptext.append(name, desc); + const shtml = this.getHTML(false); + shtml.classList.add("sourceSticker"); + top.append(shtml, toptext); + + if (!lookup) { + desc.textContent = I18n.sticker.found.private(); + return; + } + const guild = localuser.guilds.get(lookup.id); + if (guild) { + if (localuser.focusGuild === guild) { + desc.textContent = I18n.sticker.found.this(); + } else { + desc.textContent = I18n.sticker.found.other(); + } + } else { + desc.textContent = I18n.sticker.found.not(); + } + + const h3 = document.createElement("h3"); + h3.textContent = I18n.sticker.from(); + + const guildRow = document.createElement("div"); + guildRow.classList.add("flexltr", "guildEmojiRow"); + + const guildText = document.createElement("div"); + guildText.classList.add("flexttb", "guildEmojiText"); + + const guildName = document.createElement("span"); + guildName.classList.add("guildName"); + guildName.textContent = lookup.name; + + const guildDesc = document.createElement("span"); + guildDesc.classList.add("guildDesc"); + const discoverable = lookup.features.find((_) => _ === "DISCOVERABLE"); + if (discoverable) { + if (lookup.description) { + guildDesc.textContent = lookup.description; + } + } else { + guildDesc.textContent = I18n.emoji.privateGuild(); + } + + guildText.append(guildName, guildDesc); + if (!guild && discoverable) { + const button = document.createElement("button"); + button.textContent = I18n.emoji.join(); + button.classList.add("emojiJoin"); + guildText.append(button); + button.onclick = async () => { + const joinRes = await fetch( + localuser.info.api + "/guilds/" + lookup.id + "/members/@me", + { + method: "PUT", + headers: localuser.headers, + }, + ); + if (joinRes.ok) { + removeAni(div); + } + }; + } + guildRow.append( + Guild.generateGuildIcon({...lookup, info: localuser.info}, false) + .lastChild as HTMLElement, + guildText, + ); + div.append(top, h3, guildRow); + + document.body.append(div); + Contextmenu.keepOnScreen(div); + Contextmenu.declareMenu(div); + }; + } return img; } static searchStickers(search: string, localuser: Localuser, results = 50): [Sticker, number][] { @@ -177,7 +276,7 @@ class Sticker extends SnowFlake { const emojiElem = document.createElement("div"); emojiElem.classList.add("stickerSelect"); - emojiElem.append(sticker.getHTML()); + emojiElem.append(sticker.getHTML(false)); body.append(emojiElem); emojiElem.addEventListener("click", () => { @@ -246,7 +345,7 @@ class Sticker extends SnowFlake { for (const [, sticker] of guild.stickers) { const stickerElem = document.createElement("div"); stickerElem.classList.add("stickerSelect"); - stickerElem.append(sticker.getHTML()); + stickerElem.append(sticker.getHTML(false)); body.append(stickerElem); stickerElem.addEventListener("click", () => { res(sticker); @@ -270,5 +369,39 @@ class Sticker extends SnowFlake { search.focus(); return promise; } + static stickerMap = new WeakMap>(); + static async lookupStickerSource(id: string, localuser: Localuser): Promise { + const guild = localuser.guilds.values().find((guild) => guild.stickers.has(id)); + if (guild) { + return { + ...guild.properties, + premium_subscription_count: 0, + approximate_member_count: 0, + approximate_presence_count: 0, + auto_removed: false, + primary_category_id: null, + keywords: null, + is_published: false, + reasons_to_join: [], + vanity_url_code: "", + }; + } + + const map = this.stickerMap.get(localuser) || new Map(); + this.emojiMap.set(localuser, map); + + if (map.has(id)) return map.get(id); + + const res = await fetch(localuser.info.api + `/stickers/${id}/guild`, { + headers: localuser.headers, + }); + if (res.status === 403) { + map.set(id, undefined); + return undefined; + } + const json = (await res.json()) as guildSource; + map.set(id, json); + return json; + } } export {Sticker}; diff --git a/src/webpage/style.css b/src/webpage/style.css index 4adb8a8..b6d5a2c 100644 --- a/src/webpage/style.css +++ b/src/webpage/style.css @@ -5225,7 +5225,10 @@ img.error::after { margin-right: 10px; } } - +.sourceSticker { + width: 48px; + height: 48px; +} .guildEmojiRow { img { margin-right: 8px; diff --git a/translations/en.json b/translations/en.json index d839e34..8432bb9 100644 --- a/translations/en.json +++ b/translations/en.json @@ -937,7 +937,14 @@ "name": "Name:", "tags": "Associated emoji: ", "title": "Stickers", - "upload": "Upload stickers" + "upload": "Upload stickers", + "found": { + "not": "This sticker is not from your guilds, join the guild hosting it to use this sticker", + "other": "This sticker is from one of your other guilds.", + "private": "The guild for this sticker can't be found", + "this": "This sticker is from this guild." + }, + "from": "This sticker is from" }, "submit": "Submit", "switchAccounts": "Switch accounts ⇌",