123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192 |
- // @ts-expect-error
- import aliases from 'react-emoji-render/data/aliases';
- // eslint-disable-next-line lines-around-comment
- // @ts-expect-error
- import emojiAsciiAliases from 'react-emoji-render/data/asciiAliases';
-
- import { IReduxState } from '../app/types';
- import { getLocalizedDateFormatter } from '../base/i18n/dateUtil';
- import i18next from '../base/i18n/i18next';
- import { getParticipantById } from '../base/participants/functions';
- import { escapeRegexp } from '../base/util/helpers';
-
- import { MESSAGE_TYPE_ERROR, MESSAGE_TYPE_LOCAL, TIMESTAMP_FORMAT } from './constants';
- import { IMessage } from './types';
-
- /**
- * An ASCII emoticon regexp array to find and replace old-style ASCII
- * emoticons (such as :O) with the new Unicode representation, so that
- * devices and browsers that support them can render these natively
- * without a 3rd party component.
- *
- * NOTE: this is currently only used on mobile, but it can be used
- * on web too once we drop support for browsers that don't support
- * unicode emoji rendering.
- */
- const ASCII_EMOTICON_REGEXP_ARRAY: Array<[RegExp, string]> = [];
-
- /**
- * An emoji regexp array to find and replace alias emoticons
- * (such as :smiley:) with the new Unicode representation, so that
- * devices and browsers that support them can render these natively
- * without a 3rd party component.
- *
- * NOTE: this is currently only used on mobile, but it can be used
- * on web too once we drop support for browsers that don't support
- * unicode emoji rendering.
- */
- const SLACK_EMOJI_REGEXP_ARRAY: Array<[RegExp, string]> = [];
-
- (function() {
- for (const [ key, value ] of Object.entries(aliases)) {
-
- // Add ASCII emoticons
- const asciiEmoticons = emojiAsciiAliases[key];
-
- if (asciiEmoticons) {
- const asciiEscapedValues = asciiEmoticons.map((v: string) => escapeRegexp(v));
-
- const asciiRegexp = `(${asciiEscapedValues.join('|')})`;
-
- // Escape urls
- const formattedAsciiRegexp = key === 'confused'
- ? `(?=(${asciiRegexp}))(:(?!//).)`
- : asciiRegexp;
-
- ASCII_EMOTICON_REGEXP_ARRAY.push([ new RegExp(formattedAsciiRegexp, 'g'), value as string ]);
- }
-
- // Add slack-type emojis
- const emojiRegexp = `\\B(${escapeRegexp(`:${key}:`)})\\B`;
-
- SLACK_EMOJI_REGEXP_ARRAY.push([ new RegExp(emojiRegexp, 'g'), value as string ]);
- }
- })();
-
- /**
- * Replaces ASCII and other non-unicode emoticons with unicode emojis to let the emojis be rendered
- * by the platform native renderer.
- *
- * @param {string} message - The message to parse and replace.
- * @returns {string}
- */
- export function replaceNonUnicodeEmojis(message: string) {
- let replacedMessage = message;
-
- for (const [ regexp, replaceValue ] of SLACK_EMOJI_REGEXP_ARRAY) {
- replacedMessage = replacedMessage.replace(regexp, replaceValue);
- }
-
- for (const [ regexp, replaceValue ] of ASCII_EMOTICON_REGEXP_ARRAY) {
- replacedMessage = replacedMessage.replace(regexp, replaceValue);
- }
-
- return replacedMessage;
- }
-
- /**
- * Selector for calculating the number of unread chat messages.
- *
- * @param {IReduxState} state - The redux state.
- * @returns {number} The number of unread messages.
- */
- export function getUnreadCount(state: IReduxState) {
- const { lastReadMessage, messages } = state['features/chat'];
- const messagesCount = messages.length;
-
- if (!messagesCount) {
- return 0;
- }
-
- let reactionMessages = 0;
- let lastReadIndex;
-
- if (navigator.product === 'ReactNative') {
- // React native stores the messages in a reversed order.
- lastReadIndex = messages.indexOf(<IMessage>lastReadMessage);
-
- for (let i = 0; i < lastReadIndex; i++) {
- if (messages[i].isReaction) {
- reactionMessages++;
- }
- }
-
- return lastReadIndex - reactionMessages;
- }
-
- lastReadIndex = messages.lastIndexOf(<IMessage>lastReadMessage);
-
- for (let i = lastReadIndex + 1; i < messagesCount; i++) {
- if (messages[i].isReaction) {
- reactionMessages++;
- }
- }
-
- return messagesCount - (lastReadIndex + 1) - reactionMessages;
- }
-
- /**
- * Get whether the chat smileys are disabled or not.
- *
- * @param {IReduxState} state - The redux state.
- * @returns {boolean} The disabled flag.
- */
- export function areSmileysDisabled(state: IReduxState) {
- const disableChatSmileys = state['features/base/config']?.disableChatSmileys === true;
-
- return disableChatSmileys;
- }
-
- /**
- * Returns the timestamp to display for the message.
- *
- * @param {IMessage} message - The message from which to get the timestamp.
- * @returns {string}
- */
- export function getFormattedTimestamp(message: IMessage) {
- return getLocalizedDateFormatter(new Date(message.timestamp))
- .format(TIMESTAMP_FORMAT);
- }
-
- /**
- * Generates the message text to be rendered in the component.
- *
- * @param {IMessage} message - The message from which to get the text.
- * @returns {string}
- */
- export function getMessageText(message: IMessage) {
- return message.messageType === MESSAGE_TYPE_ERROR
- ? i18next.t('chat.error', {
- error: message.message
- })
- : message.message;
- }
-
-
- /**
- * Returns whether a message can be replied to.
- *
- * @param {IReduxState} state - The redux state.
- * @param {IMessage} message - The message to be checked.
- * @returns {boolean}
- */
- export function getCanReplyToMessage(state: IReduxState, message: IMessage) {
- const { knocking } = state['features/lobby'];
- const participant = getParticipantById(state, message.participantId);
-
- return Boolean(participant)
- && (message.privateMessage || (message.lobbyChat && !knocking))
- && message.messageType !== MESSAGE_TYPE_LOCAL;
- }
-
- /**
- * Returns the message that is displayed as a notice for private messages.
- *
- * @param {IMessage} message - The message to be checked.
- * @returns {string}
- */
- export function getPrivateNoticeMessage(message: IMessage) {
- return i18next.t('chat.privateNotice', {
- recipient: message.messageType === MESSAGE_TYPE_LOCAL ? message.recipient : i18next.t('chat.you')
- });
- }
|