Files
cinny/src/app/organisms/emoji-board/custom-emoji.js
2022-08-20 21:07:20 +05:30

268 lines
6.7 KiB
JavaScript

import { emojis } from './emoji';
// https://github.com/Sorunome/matrix-doc/blob/soru/emotes/proposals/2545-emotes.md
class ImagePack {
static parsePack(eventId, packContent) {
if (!eventId || typeof packContent?.images !== 'object') {
return null;
}
return new ImagePack(eventId, packContent);
}
constructor(eventId, content) {
this.id = eventId;
this.content = JSON.parse(JSON.stringify(content));
this.applyPack(content);
this.applyImages(content);
}
applyPack(content) {
const pack = content.pack ?? {};
this.displayName = pack.display_name;
this.avatarUrl = pack.avatar_url;
this.usage = pack.usage ?? ['emoticon', 'sticker'];
this.attribution = pack.attribution;
}
applyImages(content) {
this.images = new Map();
this.emoticons = [];
this.stickers = [];
Object.entries(content.images).forEach(([shortcode, data]) => {
const mxc = data.url;
const body = data.body ?? shortcode;
const usage = data.usage ?? this.usage;
const { info } = data;
if (!mxc) return;
const image = {
shortcode, mxc, body, usage, info,
};
this.images.set(shortcode, image);
if (usage.includes('emoticon')) {
this.emoticons.push(image);
}
if (usage.includes('sticker')) {
this.stickers.push(image);
}
});
}
getImages() {
return this.images;
}
getEmojis() {
return this.emoticons;
}
getStickers() {
return this.stickers;
}
getContent() {
return this.content;
}
_updatePackProperty(property, value) {
if (this.content.pack === undefined) {
this.content.pack = {};
}
this.content.pack[property] = value;
this.applyPack(this.content);
}
setAvatarUrl(avatarUrl) {
this._updatePackProperty('avatar_url', avatarUrl);
}
setDisplayName(displayName) {
this._updatePackProperty('display_name', displayName);
}
setAttribution(attribution) {
this._updatePackProperty('attribution', attribution);
}
setUsage(usage) {
this._updatePackProperty('usage', usage);
}
addImage(key, imgContent) {
this.content.images = {
[key]: imgContent,
...this.content.images,
};
this.applyImages(this.content);
}
removeImage(key) {
if (this.content.images[key] === undefined) return;
delete this.content.images[key];
this.applyImages(this.content);
}
updateImageKey(key, newKey) {
if (this.content.images[key] === undefined) return;
const copyImages = {};
Object.keys(this.content.images).forEach((imgKey) => {
copyImages[imgKey === key ? newKey : imgKey] = this.content.images[imgKey];
});
this.content.images = copyImages;
this.applyImages(this.content);
}
_updateImageProperty(key, property, value) {
if (this.content.images[key] === undefined) return;
this.content.images[key][property] = value;
this.applyImages(this.content);
}
setImageUrl(key, url) {
this._updateImageProperty(key, 'url', url);
}
setImageBody(key, body) {
this._updateImageProperty(key, 'body', body);
}
setImageInfo(key, info) {
this._updateImageProperty(key, 'info', info);
}
setImageUsage(key, usage) {
this._updateImageProperty(key, 'usage', usage);
}
}
function getGlobalImagePacks(mx) {
const globalContent = mx.getAccountData('im.ponies.emote_rooms')?.getContent();
if (typeof globalContent !== 'object') return [];
const { rooms } = globalContent;
if (typeof rooms !== 'object') return [];
const roomIds = Object.keys(rooms);
const packs = roomIds.flatMap((roomId) => {
if (typeof rooms[roomId] !== 'object') return [];
const room = mx.getRoom(roomId);
if (!room) return [];
const stateKeys = Object.keys(rooms[roomId]);
return stateKeys.map((stateKey) => {
const data = room.currentState.getStateEvents('im.ponies.room_emotes', stateKey);
const pack = ImagePack.parsePack(data?.getId(), data?.getContent());
if (pack) {
pack.displayName ??= room.name;
pack.avatarUrl ??= room.getMxcAvatarUrl();
}
return pack;
}).filter((pack) => pack !== null);
});
return packs;
}
function getUserImagePack(mx) {
const accountDataEmoji = mx.getAccountData('im.ponies.user_emotes');
if (!accountDataEmoji) {
return null;
}
const userImagePack = ImagePack.parsePack(mx.getUserId(), accountDataEmoji.event.content);
if (userImagePack) userImagePack.displayName ??= 'Personal Emoji';
return userImagePack;
}
function getRoomImagePacks(room) {
const dataEvents = room.currentState.getStateEvents('im.ponies.room_emotes');
return dataEvents
.map((data) => {
const pack = ImagePack.parsePack(data?.getId(), data?.getContent());
if (pack) {
pack.displayName ??= room.name;
pack.avatarUrl ??= room.getMxcAvatarUrl();
}
return pack;
})
.filter((pack) => pack !== null);
}
/**
* @param {MatrixClient} mx Provide if you want to include user personal/global pack
* @param {Room[]} rooms Provide rooms if you want to include rooms pack
* @returns {ImagePack[]} packs
*/
function getRelevantPacks(mx, rooms) {
const userPack = mx ? getUserImagePack(mx) : [];
const globalPacks = mx ? getGlobalImagePacks(mx) : [];
const globalPackIds = new Set(globalPacks.map((pack) => pack.id));
const roomsPack = rooms?.flatMap(getRoomImagePacks) ?? [];
return [].concat(
userPack ?? [],
globalPacks,
roomsPack.filter((pack) => !globalPackIds.has(pack.id)),
);
}
function getShortcodeToEmoji(mx, rooms) {
const allEmoji = new Map();
emojis.forEach((emoji) => {
if (Array.isArray(emoji.shortcodes)) {
emoji.shortcodes.forEach((shortcode) => {
allEmoji.set(shortcode, emoji);
});
} else {
allEmoji.set(emoji.shortcodes, emoji);
}
});
getRelevantPacks(mx, rooms)
.flatMap((pack) => pack.getEmojis())
.forEach((emoji) => {
allEmoji.set(emoji.shortcode, emoji);
});
return allEmoji;
}
function getShortcodeToCustomEmoji(room) {
const allEmoji = new Map();
getRelevantPacks(room.client, [room])
.flatMap((pack) => pack.getEmojis())
.forEach((emoji) => {
allEmoji.set(emoji.shortcode, emoji);
});
return allEmoji;
}
function getEmojiForCompletion(mx, rooms) {
const allEmoji = new Map();
getRelevantPacks(mx, rooms)
.flatMap((pack) => pack.getEmojis())
.forEach((emoji) => {
allEmoji.set(emoji.shortcode, emoji);
});
return Array.from(allEmoji.values()).concat(emojis.filter((e) => !allEmoji.has(e.shortcode)));
}
export {
ImagePack,
getUserImagePack, getGlobalImagePacks, getRoomImagePacks,
getShortcodeToEmoji, getShortcodeToCustomEmoji,
getRelevantPacks, getEmojiForCompletion,
};