140 lines
4.8 KiB
TypeScript
140 lines
4.8 KiB
TypeScript
/*
|
|
* Copyright (C) 2026 Fluxer Contributors
|
|
*
|
|
* This file is part of Fluxer.
|
|
*
|
|
* Fluxer is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Fluxer is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
import {msg} from '@lingui/core/macro';
|
|
import i18n from '~/i18n';
|
|
import type {UnicodeEmoji} from '~/lib/UnicodeEmojis';
|
|
import UnicodeEmojis from '~/lib/UnicodeEmojis';
|
|
import type {MessageRecord} from '~/records/MessageRecord';
|
|
import ChannelStore from '~/stores/ChannelStore';
|
|
import MessageReactionsStore from '~/stores/MessageReactionsStore';
|
|
import UserSettingsStore from '~/stores/UserSettingsStore';
|
|
import * as AvatarUtils from '~/utils/AvatarUtils';
|
|
import * as EmojiUtils from '~/utils/EmojiUtils';
|
|
import {shouldUseNativeEmoji} from '~/utils/EmojiUtils';
|
|
import * as NicknameUtils from '~/utils/NicknameUtils';
|
|
|
|
export type {UnicodeEmoji} from '~/lib/UnicodeEmojis';
|
|
|
|
export interface ReactionEmoji {
|
|
id?: string;
|
|
name: string;
|
|
animated?: boolean;
|
|
}
|
|
|
|
export const getReactionTooltip = (message: MessageRecord, emoji: ReactionEmoji) => {
|
|
const channel = ChannelStore.getChannel(message.channelId)!;
|
|
const guildId = channel.guildId;
|
|
const users = MessageReactionsStore.getReactions(message.id, emoji)
|
|
.slice(0, 3)
|
|
.map((user) => NicknameUtils.getNickname(user, guildId));
|
|
|
|
if (users.length === 0) {
|
|
return '';
|
|
}
|
|
|
|
const reaction = message.getReaction(emoji);
|
|
const othersCount = Math.max(0, (reaction?.count || 0) - users.length);
|
|
const emojiName = getEmojiNameWithColons(emoji);
|
|
|
|
if (users.length === 1) {
|
|
if (othersCount > 0) {
|
|
return othersCount === 1
|
|
? i18n._(msg`${emojiName} reacted by ${users[0]} and ${othersCount} other`)
|
|
: i18n._(msg`${emojiName} reacted by ${users[0]} and ${othersCount} others`);
|
|
}
|
|
return i18n._(msg`${emojiName} reacted by ${users[0]}`);
|
|
}
|
|
|
|
if (users.length === 2) {
|
|
if (othersCount > 0) {
|
|
return othersCount === 1
|
|
? i18n._(msg`${emojiName} reacted by ${users[0]}, ${users[1]} and ${othersCount} other`)
|
|
: i18n._(msg`${emojiName} reacted by ${users[0]}, ${users[1]} and ${othersCount} others`);
|
|
}
|
|
return i18n._(msg`${emojiName} reacted by ${users[0]} and ${users[1]}`);
|
|
}
|
|
|
|
if (users.length === 3) {
|
|
if (othersCount > 0) {
|
|
return othersCount === 1
|
|
? i18n._(msg`${emojiName} reacted by ${users[0]}, ${users[1]}, ${users[2]} and ${othersCount} other`)
|
|
: i18n._(msg`${emojiName} reacted by ${users[0]}, ${users[1]}, ${users[2]} and ${othersCount} others`);
|
|
}
|
|
return i18n._(msg`${emojiName} reacted by ${users[0]}, ${users[1]} and ${users[2]}`);
|
|
}
|
|
|
|
return othersCount === 1
|
|
? i18n._(msg`${emojiName} reacted by ${othersCount} other`)
|
|
: i18n._(msg`${emojiName} reacted by ${othersCount} others`);
|
|
};
|
|
|
|
const isCustomEmoji = (emoji: UnicodeEmoji | ReactionEmoji): emoji is ReactionEmoji =>
|
|
'id' in emoji && emoji.id != null;
|
|
|
|
export const toReactionEmoji = (emoji: UnicodeEmoji | ReactionEmoji) =>
|
|
isCustomEmoji(emoji) ? emoji : {name: emoji.surrogates};
|
|
|
|
export const emojiEquals = (reactionEmoji: ReactionEmoji, emoji: UnicodeEmoji | ReactionEmoji) =>
|
|
isCustomEmoji(emoji) ? emoji.id === reactionEmoji.id : reactionEmoji.id == null && emoji.name === reactionEmoji.name;
|
|
|
|
export const getReactionKey = (messageId: string, emoji: ReactionEmoji) =>
|
|
`${messageId}:${emoji.name}:${emoji.id || ''}`;
|
|
|
|
export const getEmojiName = (emoji: ReactionEmoji): string =>
|
|
emoji.id == null ? UnicodeEmojis.getSurrogateName(emoji.name) || emoji.name : `:${emoji.name}:`;
|
|
|
|
export const getEmojiNameWithColons = (emoji: ReactionEmoji): string => {
|
|
const name = emoji.id == null ? UnicodeEmojis.getSurrogateName(emoji.name) || emoji.name : emoji.name;
|
|
return `:${name}:`;
|
|
};
|
|
|
|
export const useEmojiURL = ({
|
|
emoji,
|
|
isHovering = false,
|
|
size = 128,
|
|
forceAnimate = false,
|
|
}: {
|
|
emoji: ReactionEmoji;
|
|
isHovering?: boolean;
|
|
size?: number;
|
|
forceAnimate?: boolean;
|
|
}): string | null => {
|
|
const {animateEmoji} = UserSettingsStore;
|
|
|
|
let shouldAnimate = false;
|
|
if (forceAnimate) {
|
|
shouldAnimate = emoji.animated ?? false;
|
|
} else if (animateEmoji) {
|
|
shouldAnimate = emoji.animated ?? false;
|
|
} else if (emoji.animated) {
|
|
shouldAnimate = isHovering;
|
|
}
|
|
|
|
if (emoji.id == null) {
|
|
if (shouldUseNativeEmoji) {
|
|
return null;
|
|
}
|
|
return EmojiUtils.getEmojiURL(emoji.name);
|
|
}
|
|
|
|
const url = AvatarUtils.getEmojiURL({id: emoji.id, animated: shouldAnimate});
|
|
return `${url}?size=${size}&quality=lossless`;
|
|
};
|