2025-03-01 18:02:01 +01:00

131 lines
4.8 KiB
TypeScript

import React, { type TeactNode } from '../../../../lib/teact/teact';
import { getActions } from '../../../../global';
import type { ApiMessage } from '../../../../api/types';
import type { ApiMessageActionPhoneCall } from '../../../../api/types/messageActions';
import type {
LangKey,
LangPairPluralWithVariables,
LangPairWithVariables,
PluralLangKeyWithVariables,
RegularLangKey,
RegularLangKeyWithVariables,
} from '../../../../types/language';
import type { LangFn } from '../../../../util/localization';
import { getMessageContent } from '../../../../global/helpers';
import buildClassName from '../../../../util/buildClassName';
import { IS_SAFARI } from '../../../../util/windowEnvironment';
import renderText from '../../../common/helpers/renderText';
import Link from '../../../ui/Link';
import styles from '../ActionMessage.module.scss';
type SuffixKey<T, K extends keyof T> = `${K & string}You` extends keyof T ? T[`${K & string}You`] : never;
type VariablesForKey<K extends LangKey> =
K extends RegularLangKeyWithVariables
? LangPairWithVariables<TeactNode | undefined>[K] | SuffixKey<LangPairWithVariables, K>
: K extends PluralLangKeyWithVariables
? LangPairPluralWithVariables<TeactNode | undefined>[K] | SuffixKey<LangPairPluralWithVariables, K>
: undefined;
export function translateWithYou<K extends LangKey>(
lang: LangFn,
key: K,
isYou: boolean,
variables: VariablesForKey<K>,
options?: { pluralValue?: number; asText?: boolean; isMarkdown?: boolean },
): TeactNode {
const { pluralValue, asText, isMarkdown } = options || {};
const translationKey = isYou ? (`${key}You` as LangKey) : key;
return lang(
// @ts-ignore -- I have no idea if this even possible to type correctly
translationKey,
variables,
{ withNodes: !asText, isMarkdown, pluralValue },
);
}
export function getPinnedMediaValue(lang: LangFn, message: ApiMessage) {
const {
audio, contact, document, game, giveaway, giveawayResults, paidMedia, storyData,
invoice, location, photo, pollId, sticker, video, voice,
} = getMessageContent(message);
if (message.groupedId || paidMedia) return lang('ActionPinnedMediaAlbum');
if (photo) return lang('ActionPinnedMediaPhoto');
if (audio) return lang('ActionPinnedMediaAudio');
if (voice) return lang('ActionPinnedMediaVoice');
if (video?.isRound) return lang('ActionPinnedMediaVideoMessage');
if (video?.isGif) return lang('ActionPinnedMediaGif');
if (video) return lang('ActionPinnedMediaVideo');
if (sticker) return lang('ActionPinnedMediaSticker');
if (document) return lang('ActionPinnedMediaFile');
if (contact) return lang('ActionPinnedMediaContact');
if (location) return lang('ActionPinnedMediaLocation');
if (storyData) return lang('ActionPinnedMediaStory');
if (invoice) return lang('ActionPinnedMediaInvoice');
if (game) return lang('ActionPinnedMediaGame', { game: game.title });
if (pollId) return lang('ActionPinnedMediaPoll');
if (giveaway) return lang('ActionPinnedMediaGiveaway');
if (giveawayResults) return lang('ActionPinnedMediaGiveawayResults');
return undefined;
}
export function renderPeerLink(peerId: string | undefined, text: string, asPreview?: boolean) {
if (!peerId || asPreview) {
return renderText(text);
}
return (
<Link
className={buildClassName(styles.peerLink, styles.strong)}
// eslint-disable-next-line react/jsx-no-bind
onClick={(e) => {
e.stopPropagation();
getActions().openChat({ id: peerId });
}}
// box-decoration-break: clone; is broken when child has `dir` attribute
withMultilineFix={IS_SAFARI}
>
{renderText(text)}
</Link>
);
}
export function renderMessageLink(targetMessage: ApiMessage, text: TeactNode, asPreview?: boolean) {
if (asPreview) return text;
return (
<Link
className={styles.messageLink}
// eslint-disable-next-line react/jsx-no-bind
onClick={(e) => {
e.stopPropagation();
getActions().focusMessage({ chatId: targetMessage.chatId, messageId: targetMessage.id });
}}
withMultilineFix={IS_SAFARI}
>
{text}
</Link>
);
}
export function getCallMessageKey(action: ApiMessageActionPhoneCall, isOutgoing: boolean): RegularLangKey {
const isMissed = action.reason === 'missed';
const isCancelled = action.reason === 'busy' || action.duration === undefined;
if (action.isVideo) {
if (isMissed) return isOutgoing ? 'CallMessageVideoOutgoingMissed' : 'CallMessageVideoIncomingMissed';
if (isCancelled) return 'CallMessageVideoIncomingDeclined';
return isOutgoing ? 'CallMessageVideoOutgoing' : 'CallMessageVideoIncoming';
} else {
if (isMissed) return isOutgoing ? 'CallMessageOutgoingMissed' : 'CallMessageIncomingMissed';
if (isCancelled) return 'CallMessageIncomingDeclined';
return isOutgoing ? 'CallMessageOutgoing' : 'CallMessageIncoming';
}
}