Action Message: Refactoring and fixes
This commit is contained in:
parent
8baad38a37
commit
c6672612bf
@ -46,7 +46,7 @@ const WebLink: FC<OwnProps> = ({
|
||||
siteName: domain.replace(/^www./, ''),
|
||||
url: url.includes('://') ? url : url.includes('@') ? `mailto:${url}` : `http://${url}`,
|
||||
formattedDescription: getMessageText(message) !== url
|
||||
? renderMessageSummary(lang, message, undefined, undefined, MAX_TEXT_LENGTH, true)
|
||||
? renderMessageSummary(lang, message, undefined, undefined, MAX_TEXT_LENGTH)
|
||||
: undefined,
|
||||
} as ApiWebPageWithFormatted;
|
||||
}
|
||||
|
||||
@ -6,12 +6,12 @@ import {
|
||||
import { LangFn } from '../../../hooks/useLang';
|
||||
import {
|
||||
getChatTitle,
|
||||
getMessageContent, getMessageSummaryText,
|
||||
getMessageSummaryText,
|
||||
getUserFullName,
|
||||
} from '../../../modules/helpers';
|
||||
import trimText from '../../../util/trimText';
|
||||
import { formatCurrency } from '../../../util/formatCurrency';
|
||||
import { renderMessageSummary, TextPart } from './renderMessageText';
|
||||
import { TextPart, renderMessageSummary } from './renderMessageText';
|
||||
import renderText from './renderText';
|
||||
|
||||
import UserLink from '../UserLink';
|
||||
@ -19,12 +19,12 @@ import MessageLink from '../MessageLink';
|
||||
import ChatLink from '../ChatLink';
|
||||
import GroupCallLink from '../GroupCallLink';
|
||||
|
||||
interface ActionMessageTextOptions {
|
||||
maxTextLength?: number;
|
||||
asPlain?: boolean;
|
||||
isEmbedded?: boolean;
|
||||
interface RenderOptions {
|
||||
asPlainText?: boolean;
|
||||
asTextWithSpoilers?: boolean;
|
||||
}
|
||||
|
||||
const MAX_LENGTH = 32;
|
||||
const NBSP = '\u00A0';
|
||||
|
||||
export function renderActionMessageText(
|
||||
@ -35,21 +35,22 @@ export function renderActionMessageText(
|
||||
targetUsers?: ApiUser[],
|
||||
targetMessage?: ApiMessage,
|
||||
targetChatId?: string,
|
||||
options: ActionMessageTextOptions = {},
|
||||
options: RenderOptions = {},
|
||||
) {
|
||||
if (!message.content.action) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const {
|
||||
text, translationValues, amount, currency, call,
|
||||
} = message.content.action;
|
||||
const content: TextPart[] = [];
|
||||
const textOptions: ActionMessageTextOptions = { ...options, maxTextLength: 32 };
|
||||
const noLinks = options.asPlainText || options.asTextWithSpoilers;
|
||||
const translationKey = text === 'Chat.Service.Group.UpdatedPinnedMessage1' && !targetMessage
|
||||
? 'Message.PinnedGenericMessage'
|
||||
: text;
|
||||
|
||||
let unprocessed = lang(translationKey, translationValues && translationValues.length ? translationValues : undefined);
|
||||
let unprocessed = lang(translationKey, translationValues?.length ? translationValues : undefined);
|
||||
let processed: TextPart[];
|
||||
|
||||
if (unprocessed.includes('%payment_amount%')) {
|
||||
@ -66,10 +67,9 @@ export function renderActionMessageText(
|
||||
unprocessed,
|
||||
'%action_origin%',
|
||||
actionOriginUser ? (
|
||||
(!options.isEmbedded && renderUserContent(actionOriginUser, options.asPlain)) || NBSP
|
||||
|
||||
renderUserContent(actionOriginUser, noLinks) || NBSP
|
||||
) : actionOriginChat ? (
|
||||
(!options.isEmbedded && renderChatContent(lang, actionOriginChat, options.asPlain)) || NBSP
|
||||
renderChatContent(lang, actionOriginChat, noLinks) || NBSP
|
||||
) : 'User',
|
||||
);
|
||||
|
||||
@ -80,7 +80,7 @@ export function renderActionMessageText(
|
||||
unprocessed,
|
||||
'%target_user%',
|
||||
targetUsers
|
||||
? targetUsers.map((user) => renderUserContent(user, options.asPlain)).filter<TextPart>(Boolean as any)
|
||||
? targetUsers.map((user) => renderUserContent(user, noLinks)).filter<TextPart>(Boolean as any)
|
||||
: 'User',
|
||||
);
|
||||
|
||||
@ -91,7 +91,7 @@ export function renderActionMessageText(
|
||||
unprocessed,
|
||||
'%message%',
|
||||
targetMessage
|
||||
? renderMessageContent(lang, targetMessage, textOptions)
|
||||
? renderMessageContent(lang, targetMessage, options)
|
||||
: 'a message',
|
||||
);
|
||||
unprocessed = processed.pop() as string;
|
||||
@ -111,12 +111,12 @@ export function renderActionMessageText(
|
||||
unprocessed,
|
||||
'%target_chat%',
|
||||
targetChatId
|
||||
? renderMigratedContent(targetChatId, options.asPlain)
|
||||
? renderMigratedContent(targetChatId, noLinks)
|
||||
: 'another chat',
|
||||
);
|
||||
content.push(...processed);
|
||||
|
||||
if (options.asPlain) {
|
||||
if (options.asPlainText) {
|
||||
return content.join('').trim();
|
||||
}
|
||||
|
||||
@ -133,47 +133,23 @@ function renderProductContent(message: ApiMessage) {
|
||||
: 'a product';
|
||||
}
|
||||
|
||||
function renderMessageContent(lang: LangFn, message: ApiMessage, options: ActionMessageTextOptions = {}) {
|
||||
const { maxTextLength, isEmbedded, asPlain } = options;
|
||||
function renderMessageContent(lang: LangFn, message: ApiMessage, options: RenderOptions = {}) {
|
||||
const { asPlainText, asTextWithSpoilers } = options;
|
||||
|
||||
const text = asPlain
|
||||
? [trimText(getMessageSummaryText(lang, message), maxTextLength)]
|
||||
: renderMessageSummary(lang, message, undefined, undefined, maxTextLength, true);
|
||||
const {
|
||||
photo, video, document, sticker,
|
||||
} = getMessageContent(message);
|
||||
|
||||
const showQuotes = isEmbedded && text && !photo && !video && !document && !sticker;
|
||||
let messageText = text;
|
||||
|
||||
if (isEmbedded) {
|
||||
if (photo) {
|
||||
messageText = ['a photo'];
|
||||
} else if (video) {
|
||||
messageText = [video.isGif ? 'a GIF' : 'a video'];
|
||||
} else if (document) {
|
||||
messageText = ['a document'];
|
||||
} else if (sticker) {
|
||||
messageText = text;
|
||||
}
|
||||
if (asPlainText) {
|
||||
return getMessageSummaryText(lang, message, undefined, MAX_LENGTH);
|
||||
}
|
||||
|
||||
if (asPlain && messageText) {
|
||||
return (showQuotes ? ['«', ...messageText, '»'] : messageText).join('');
|
||||
}
|
||||
const messageSummary = renderMessageSummary(lang, message, undefined, undefined, MAX_LENGTH);
|
||||
|
||||
if (showQuotes) {
|
||||
if (asTextWithSpoilers) {
|
||||
return (
|
||||
<span>
|
||||
«
|
||||
<MessageLink className="action-link" message={message}>{messageText}</MessageLink>
|
||||
»
|
||||
</span>
|
||||
<span>{messageSummary}</span>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<MessageLink className="action-link" message={message}>{messageText}</MessageLink>
|
||||
<MessageLink className="action-link" message={message}>{messageSummary}</MessageLink>
|
||||
);
|
||||
}
|
||||
|
||||
@ -185,30 +161,30 @@ function renderGroupCallContent(groupCall: Partial<ApiGroupCall>, text: TextPart
|
||||
);
|
||||
}
|
||||
|
||||
function renderUserContent(sender: ApiUser, asPlain?: boolean): string | TextPart | undefined {
|
||||
const text = trimText(getUserFullName(sender));
|
||||
function renderUserContent(sender: ApiUser, noLinks?: boolean): string | TextPart | undefined {
|
||||
const text = trimText(getUserFullName(sender), MAX_LENGTH);
|
||||
|
||||
if (asPlain) {
|
||||
if (noLinks) {
|
||||
return text;
|
||||
}
|
||||
|
||||
return <UserLink className="action-link" sender={sender}>{sender && renderText(text!)}</UserLink>;
|
||||
}
|
||||
|
||||
function renderChatContent(lang: LangFn, chat: ApiChat, asPlain?: boolean): string | TextPart | undefined {
|
||||
const text = trimText(getChatTitle(lang, chat));
|
||||
function renderChatContent(lang: LangFn, chat: ApiChat, noLinks?: boolean): string | TextPart | undefined {
|
||||
const text = trimText(getChatTitle(lang, chat), MAX_LENGTH);
|
||||
|
||||
if (asPlain) {
|
||||
if (noLinks) {
|
||||
return text;
|
||||
}
|
||||
|
||||
return <ChatLink className="action-link" chatId={chat.id}>{chat && renderText(text!)}</ChatLink>;
|
||||
}
|
||||
|
||||
function renderMigratedContent(chatId: string, asPlain?: boolean): string | TextPart | undefined {
|
||||
function renderMigratedContent(chatId: string, noLinks?: boolean): string | TextPart | undefined {
|
||||
const text = 'another chat';
|
||||
|
||||
if (asPlain) {
|
||||
if (noLinks) {
|
||||
return text;
|
||||
}
|
||||
|
||||
|
||||
71
src/components/common/helpers/renderMessageText.ts
Normal file
71
src/components/common/helpers/renderMessageText.ts
Normal file
@ -0,0 +1,71 @@
|
||||
import { ApiMessage, ApiMessageEntityTypes } from '../../../api/types';
|
||||
import {
|
||||
getMessageSummaryDescription,
|
||||
getMessageSummaryEmoji,
|
||||
getMessageSummaryText,
|
||||
getMessageText,
|
||||
TRUNCATED_SUMMARY_LENGTH,
|
||||
} from '../../../modules/helpers';
|
||||
import { LangFn } from '../../../hooks/useLang';
|
||||
import renderText from './renderText';
|
||||
import { renderTextWithEntities, TextPart } from './renderTextWithEntities';
|
||||
import trimText from '../../../util/trimText';
|
||||
|
||||
export type { TextPart };
|
||||
|
||||
export function renderMessageText(
|
||||
message: ApiMessage,
|
||||
highlight?: string,
|
||||
shouldRenderHqEmoji?: boolean,
|
||||
isSimple?: boolean,
|
||||
truncateLength?: number,
|
||||
) {
|
||||
const { text, entities } = message.content.text || {};
|
||||
|
||||
if (!text) {
|
||||
const contentNotSupportedText = getMessageText(message);
|
||||
return contentNotSupportedText ? [trimText(contentNotSupportedText, truncateLength)] : undefined;
|
||||
}
|
||||
|
||||
return renderTextWithEntities(
|
||||
trimText(text, truncateLength),
|
||||
entities,
|
||||
highlight,
|
||||
shouldRenderHqEmoji,
|
||||
undefined,
|
||||
message.id,
|
||||
isSimple,
|
||||
);
|
||||
}
|
||||
|
||||
export function renderMessageSummary(
|
||||
lang: LangFn,
|
||||
message: ApiMessage,
|
||||
noEmoji = false,
|
||||
highlight?: string,
|
||||
truncateLength = TRUNCATED_SUMMARY_LENGTH,
|
||||
): TextPart[] {
|
||||
let { entities } = message.content.text || {};
|
||||
|
||||
const hasSpoilers = entities?.some((e) => e.type === ApiMessageEntityTypes.Spoiler);
|
||||
if (!hasSpoilers) {
|
||||
const text = trimText(getMessageSummaryText(lang, message, noEmoji), truncateLength);
|
||||
|
||||
if (highlight) {
|
||||
return renderText(text, ['emoji', 'highlight'], { highlight });
|
||||
} else {
|
||||
return renderText(text);
|
||||
}
|
||||
}
|
||||
|
||||
const emoji = !noEmoji && getMessageSummaryEmoji(message);
|
||||
const emojiWithSpace = emoji ? `${emoji} ` : '';
|
||||
|
||||
const text = renderMessageText(message, highlight, undefined, true, truncateLength);
|
||||
const description = getMessageSummaryDescription(lang, message, text);
|
||||
|
||||
return [
|
||||
emojiWithSpace,
|
||||
...(Array.isArray(description) ? description : [description]),
|
||||
].filter<TextPart>(Boolean);
|
||||
}
|
||||
@ -2,159 +2,23 @@ import { MouseEvent } from 'react';
|
||||
import React from '../../../lib/teact/teact';
|
||||
import { getDispatch } from '../../../lib/teact/teactn';
|
||||
|
||||
import { ApiMessageEntity, ApiMessageEntityTypes, ApiMessage } from '../../../api/types';
|
||||
|
||||
import {
|
||||
getMessageSummaryText,
|
||||
getMessageSummaryDescription,
|
||||
getMessageSummaryEmoji,
|
||||
getMessageText,
|
||||
TRUNCATED_SUMMARY_LENGTH,
|
||||
} from '../../../modules/helpers';
|
||||
import { ApiFormattedText, ApiMessageEntity, ApiMessageEntityTypes } from '../../../api/types';
|
||||
import renderText, { TextFilter } from './renderText';
|
||||
|
||||
import MentionLink from '../../middle/message/MentionLink';
|
||||
import SafeLink from '../SafeLink';
|
||||
import Spoiler from '../spoiler/Spoiler';
|
||||
import { LangFn } from '../../../hooks/useLang';
|
||||
|
||||
export type TextPart =
|
||||
string
|
||||
| Element;
|
||||
|
||||
export function renderMessageSummary(
|
||||
lang: LangFn,
|
||||
message: ApiMessage,
|
||||
noEmoji = false,
|
||||
highlight?: string,
|
||||
truncateLength = TRUNCATED_SUMMARY_LENGTH,
|
||||
shouldAddEllipsis?: boolean,
|
||||
): TextPart[] {
|
||||
const hasSpoilers = message.content.text?.entities?.some((l) => l.type === ApiMessageEntityTypes.Spoiler);
|
||||
if (!hasSpoilers) {
|
||||
let text = getMessageSummaryText(lang, message, noEmoji, truncateLength);
|
||||
if (shouldAddEllipsis) {
|
||||
text += '...';
|
||||
}
|
||||
|
||||
if (highlight) {
|
||||
return renderText(text, ['emoji', 'highlight'], {
|
||||
highlight,
|
||||
});
|
||||
} else {
|
||||
return renderText(text);
|
||||
}
|
||||
}
|
||||
|
||||
const text = renderMessageText(message, highlight, undefined, true, truncateLength);
|
||||
const emoji = !noEmoji && getMessageSummaryEmoji(message);
|
||||
const emojiWithSpace = emoji ? `${emoji} ` : '';
|
||||
const description = getMessageSummaryDescription(lang, message, text);
|
||||
return [
|
||||
emojiWithSpace,
|
||||
...(Array.isArray(description) ? description : [description]),
|
||||
shouldAddEllipsis && '...',
|
||||
].filter<TextPart>(Boolean);
|
||||
}
|
||||
|
||||
export function renderMessageText(
|
||||
message: ApiMessage,
|
||||
highlight?: string,
|
||||
shouldRenderHqEmoji?: boolean,
|
||||
isSimple?: boolean,
|
||||
truncateLength?: number,
|
||||
) {
|
||||
const formattedText = message.content.text;
|
||||
|
||||
if (!formattedText || !formattedText.text) {
|
||||
const rawText = getMessageText(message);
|
||||
return rawText ? [rawText] : undefined;
|
||||
}
|
||||
const { text, entities } = formattedText;
|
||||
|
||||
return renderTextWithEntities(
|
||||
truncateLength ? text.substr(0, truncateLength) : text,
|
||||
entities,
|
||||
highlight,
|
||||
shouldRenderHqEmoji,
|
||||
undefined,
|
||||
message.id,
|
||||
isSimple,
|
||||
);
|
||||
}
|
||||
|
||||
interface IOrganizedEntity {
|
||||
entity: ApiMessageEntity;
|
||||
organizedIndexes: Set<number>;
|
||||
nestedEntities: IOrganizedEntity[];
|
||||
}
|
||||
|
||||
function organizeEntity(
|
||||
entity: ApiMessageEntity,
|
||||
index: number,
|
||||
entities: ApiMessageEntity[],
|
||||
organizedEntityIndexes: Set<number>,
|
||||
): IOrganizedEntity | undefined {
|
||||
const { offset, length } = entity;
|
||||
const organizedIndexes = new Set([index]);
|
||||
|
||||
if (organizedEntityIndexes.has(index)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Determine any nested entities inside current entity
|
||||
const nestedEntities: IOrganizedEntity[] = [];
|
||||
const parsedNestedEntities = entities
|
||||
.filter((e, i) => i > index && e.offset >= offset && e.offset < offset + length)
|
||||
.map((e) => organizeEntity(e, entities.indexOf(e), entities, organizedEntityIndexes))
|
||||
.filter<IOrganizedEntity>(Boolean as any);
|
||||
|
||||
parsedNestedEntities.forEach((parsedEntity) => {
|
||||
let isChanged = false;
|
||||
|
||||
parsedEntity.organizedIndexes.forEach((organizedIndex) => {
|
||||
if (!isChanged && !organizedIndexes.has(organizedIndex)) {
|
||||
isChanged = true;
|
||||
}
|
||||
|
||||
organizedIndexes.add(organizedIndex);
|
||||
});
|
||||
|
||||
if (isChanged) {
|
||||
nestedEntities.push(parsedEntity);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
entity,
|
||||
organizedIndexes,
|
||||
nestedEntities,
|
||||
};
|
||||
}
|
||||
|
||||
// Organize entities in a tree-like structure to better represent how the text will be displayed
|
||||
function organizeEntities(entities: ApiMessageEntity[]) {
|
||||
const organizedEntityIndexes: Set<number> = new Set();
|
||||
const organizedEntities: IOrganizedEntity[] = [];
|
||||
|
||||
entities.forEach((entity, index) => {
|
||||
if (organizedEntityIndexes.has(index)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const organizedEntity = organizeEntity(entity, index, entities, organizedEntityIndexes);
|
||||
if (organizedEntity) {
|
||||
organizedEntity.organizedIndexes.forEach((organizedIndex) => {
|
||||
organizedEntityIndexes.add(organizedIndex);
|
||||
});
|
||||
|
||||
organizedEntities.push(organizedEntity);
|
||||
}
|
||||
});
|
||||
|
||||
return organizedEntities;
|
||||
}
|
||||
|
||||
export function renderTextWithEntities(
|
||||
text: string,
|
||||
entities?: ApiMessageEntity[],
|
||||
@ -283,6 +147,128 @@ export function renderTextWithEntities(
|
||||
return result;
|
||||
}
|
||||
|
||||
export function getTextWithEntitiesAsHtml(formattedText?: ApiFormattedText) {
|
||||
const { text, entities } = formattedText || {};
|
||||
if (!text) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const result = renderTextWithEntities(
|
||||
text,
|
||||
entities,
|
||||
undefined,
|
||||
undefined,
|
||||
true,
|
||||
);
|
||||
|
||||
if (Array.isArray(result)) {
|
||||
return result.join('');
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function renderMessagePart(
|
||||
content: TextPart | TextPart[],
|
||||
highlight?: string,
|
||||
shouldRenderHqEmoji?: boolean,
|
||||
shouldRenderAsHtml?: boolean,
|
||||
isSimple?: boolean,
|
||||
) {
|
||||
if (Array.isArray(content)) {
|
||||
const result: TextPart[] = [];
|
||||
|
||||
content.forEach((c) => {
|
||||
result.push(...renderMessagePart(c, highlight, shouldRenderHqEmoji, shouldRenderAsHtml, isSimple));
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
if (shouldRenderAsHtml) {
|
||||
return renderText(content, ['escape_html', 'emoji_html', 'br_html']);
|
||||
}
|
||||
|
||||
const emojiFilter = shouldRenderHqEmoji ? 'hq_emoji' : 'emoji';
|
||||
|
||||
const filters: TextFilter[] = [emojiFilter];
|
||||
if (!isSimple) {
|
||||
filters.push('br');
|
||||
}
|
||||
|
||||
if (highlight) {
|
||||
return renderText(content, filters.concat('highlight'), { highlight });
|
||||
} else {
|
||||
return renderText(content, filters);
|
||||
}
|
||||
}
|
||||
|
||||
// Organize entities in a tree-like structure to better represent how the text will be displayed
|
||||
function organizeEntities(entities: ApiMessageEntity[]) {
|
||||
const organizedEntityIndexes: Set<number> = new Set();
|
||||
const organizedEntities: IOrganizedEntity[] = [];
|
||||
|
||||
entities.forEach((entity, index) => {
|
||||
if (organizedEntityIndexes.has(index)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const organizedEntity = organizeEntity(entity, index, entities, organizedEntityIndexes);
|
||||
if (organizedEntity) {
|
||||
organizedEntity.organizedIndexes.forEach((organizedIndex) => {
|
||||
organizedEntityIndexes.add(organizedIndex);
|
||||
});
|
||||
|
||||
organizedEntities.push(organizedEntity);
|
||||
}
|
||||
});
|
||||
|
||||
return organizedEntities;
|
||||
}
|
||||
|
||||
function organizeEntity(
|
||||
entity: ApiMessageEntity,
|
||||
index: number,
|
||||
entities: ApiMessageEntity[],
|
||||
organizedEntityIndexes: Set<number>,
|
||||
): IOrganizedEntity | undefined {
|
||||
const { offset, length } = entity;
|
||||
const organizedIndexes = new Set([index]);
|
||||
|
||||
if (organizedEntityIndexes.has(index)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Determine any nested entities inside current entity
|
||||
const nestedEntities: IOrganizedEntity[] = [];
|
||||
const parsedNestedEntities = entities
|
||||
.filter((e, i) => i > index && e.offset >= offset && e.offset < offset + length)
|
||||
.map((e) => organizeEntity(e, entities.indexOf(e), entities, organizedEntityIndexes))
|
||||
.filter<IOrganizedEntity>(Boolean as any);
|
||||
|
||||
parsedNestedEntities.forEach((parsedEntity) => {
|
||||
let isChanged = false;
|
||||
|
||||
parsedEntity.organizedIndexes.forEach((organizedIndex) => {
|
||||
if (!isChanged && !organizedIndexes.has(organizedIndex)) {
|
||||
isChanged = true;
|
||||
}
|
||||
|
||||
organizedIndexes.add(organizedIndex);
|
||||
});
|
||||
|
||||
if (isChanged) {
|
||||
nestedEntities.push(parsedEntity);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
entity,
|
||||
organizedIndexes,
|
||||
nestedEntities,
|
||||
};
|
||||
}
|
||||
|
||||
function processEntity(
|
||||
entity: ApiMessageEntity,
|
||||
entityContent: TextPart,
|
||||
@ -408,55 +394,6 @@ function processEntity(
|
||||
}
|
||||
}
|
||||
|
||||
function renderMessagePart(
|
||||
content: TextPart | TextPart[],
|
||||
highlight?: string,
|
||||
shouldRenderHqEmoji?: boolean,
|
||||
shouldRenderAsHtml?: boolean,
|
||||
isSimple?: boolean,
|
||||
) {
|
||||
if (Array.isArray(content)) {
|
||||
const result: TextPart[] = [];
|
||||
|
||||
content.forEach((c) => {
|
||||
result.push(...renderMessagePart(c, highlight, shouldRenderHqEmoji, shouldRenderAsHtml, isSimple));
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
if (shouldRenderAsHtml) {
|
||||
return renderText(content, ['escape_html', 'emoji_html', 'br_html']);
|
||||
}
|
||||
|
||||
const emojiFilter = shouldRenderHqEmoji ? 'hq_emoji' : 'emoji';
|
||||
|
||||
const filters: TextFilter[] = [emojiFilter];
|
||||
if (!isSimple) {
|
||||
filters.push('br');
|
||||
}
|
||||
|
||||
if (highlight) {
|
||||
return renderText(content, filters.concat('highlight'), { highlight });
|
||||
} else {
|
||||
return renderText(content, filters);
|
||||
}
|
||||
}
|
||||
|
||||
function getLinkUrl(entityContent: string, entity: ApiMessageEntity) {
|
||||
const { type, url } = entity;
|
||||
return type === ApiMessageEntityTypes.TextUrl && url ? url : entityContent;
|
||||
}
|
||||
|
||||
function handleBotCommandClick(e: MouseEvent<HTMLAnchorElement>) {
|
||||
getDispatch().sendBotCommand({ command: e.currentTarget.innerText });
|
||||
}
|
||||
|
||||
function handleHashtagClick(e: MouseEvent<HTMLAnchorElement>) {
|
||||
getDispatch().setLocalTextSearchQuery({ query: e.currentTarget.innerText });
|
||||
getDispatch().searchTextMessagesLocal();
|
||||
}
|
||||
|
||||
function processEntityAsHtml(
|
||||
entity: ApiMessageEntity,
|
||||
entityContent: TextPart,
|
||||
@ -510,3 +447,17 @@ function processEntityAsHtml(
|
||||
return renderedContent;
|
||||
}
|
||||
}
|
||||
|
||||
function getLinkUrl(entityContent: string, entity: ApiMessageEntity) {
|
||||
const { type, url } = entity;
|
||||
return type === ApiMessageEntityTypes.TextUrl && url ? url : entityContent;
|
||||
}
|
||||
|
||||
function handleBotCommandClick(e: MouseEvent<HTMLAnchorElement>) {
|
||||
getDispatch().sendBotCommand({ command: e.currentTarget.innerText });
|
||||
}
|
||||
|
||||
function handleHashtagClick(e: MouseEvent<HTMLAnchorElement>) {
|
||||
getDispatch().setLocalTextSearchQuery({ query: e.currentTarget.innerText });
|
||||
getDispatch().searchTextMessagesLocal();
|
||||
}
|
||||
@ -247,6 +247,7 @@ const Chat: FC<OwnProps & StateProps> = ({
|
||||
actionTargetUsers,
|
||||
actionTargetMessage,
|
||||
actionTargetChatId,
|
||||
{ asTextWithSpoilers: true },
|
||||
)}
|
||||
</p>
|
||||
);
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import React, { FC, useEffect, useState } from '../../lib/teact/teact';
|
||||
|
||||
import { throttle } from '../../util/schedulers';
|
||||
import { TextPart } from '../common/helpers/renderMessageText';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
import { REM } from '../common/helpers/mediaDimensions';
|
||||
import { IS_SINGLE_COLUMN_LAYOUT } from '../../util/environment';
|
||||
import { throttle } from '../../util/schedulers';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
import { TextPart } from '../common/helpers/renderMessageText';
|
||||
import { REM } from '../common/helpers/mediaDimensions';
|
||||
|
||||
import './MediaViewerFooter.scss';
|
||||
|
||||
|
||||
@ -98,7 +98,7 @@ const ActionMessage: FC<OwnProps & StateProps> = ({
|
||||
targetUsers,
|
||||
targetMessage,
|
||||
targetChatId,
|
||||
isEmbedded ? { isEmbedded: true } : undefined,
|
||||
{ asTextWithSpoilers: isEmbedded },
|
||||
);
|
||||
const {
|
||||
isContextMenuOpen, contextMenuPosition,
|
||||
|
||||
@ -1,23 +0,0 @@
|
||||
import { ApiFormattedText } from '../../../../api/types';
|
||||
import { renderTextWithEntities } from '../../../common/helpers/renderMessageText';
|
||||
|
||||
export default function getMessageTextAsHtml(formattedText?: ApiFormattedText) {
|
||||
const { text, entities } = formattedText || {};
|
||||
if (!text) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const result = renderTextWithEntities(
|
||||
text,
|
||||
entities,
|
||||
undefined,
|
||||
undefined,
|
||||
true,
|
||||
);
|
||||
|
||||
if (Array.isArray(result)) {
|
||||
return result.join('');
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -8,10 +8,10 @@ import usePrevious from '../../../../hooks/usePrevious';
|
||||
import { debounce } from '../../../../util/schedulers';
|
||||
import focusEditableElement from '../../../../util/focusEditableElement';
|
||||
import parseMessageInput from '../../../../util/parseMessageInput';
|
||||
import getMessageTextAsHtml from '../helpers/getMessageTextAsHtml';
|
||||
import useBackgroundMode from '../../../../hooks/useBackgroundMode';
|
||||
import useBeforeUnload from '../../../../hooks/useBeforeUnload';
|
||||
import { IS_TOUCH_ENV } from '../../../../util/environment';
|
||||
import { getTextWithEntitiesAsHtml } from '../../../common/helpers/renderTextWithEntities';
|
||||
|
||||
// Used to avoid running debounced callbacks when chat changes.
|
||||
let currentChatId: string | undefined;
|
||||
@ -65,7 +65,7 @@ const useDraft = (
|
||||
return;
|
||||
}
|
||||
|
||||
setHtml(getMessageTextAsHtml(draft));
|
||||
setHtml(getTextWithEntitiesAsHtml(draft));
|
||||
|
||||
if (!IS_TOUCH_ENV) {
|
||||
requestAnimationFrame(() => {
|
||||
|
||||
@ -5,9 +5,9 @@ import { ApiMessage } from '../../../../api/types';
|
||||
|
||||
import { EDITABLE_INPUT_ID } from '../../../../config';
|
||||
import parseMessageInput from '../../../../util/parseMessageInput';
|
||||
import getMessageTextAsHtml from '../helpers/getMessageTextAsHtml';
|
||||
import focusEditableElement from '../../../../util/focusEditableElement';
|
||||
import { hasMessageMedia } from '../../../../modules/helpers';
|
||||
import { getTextWithEntitiesAsHtml } from '../../../common/helpers/renderTextWithEntities';
|
||||
|
||||
const useEditing = (
|
||||
htmlRef: { current: string },
|
||||
@ -26,7 +26,7 @@ const useEditing = (
|
||||
return;
|
||||
}
|
||||
|
||||
setHtml(getMessageTextAsHtml(editedMessage.content.text));
|
||||
setHtml(getTextWithEntitiesAsHtml(editedMessage.content.text));
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
const messageInput = document.getElementById(EDITABLE_INPUT_ID)!;
|
||||
|
||||
@ -14,7 +14,7 @@ import {
|
||||
} from '../../../api/types';
|
||||
|
||||
import renderText from '../../common/helpers/renderText';
|
||||
import { renderTextWithEntities } from '../../common/helpers/renderMessageText';
|
||||
import { renderTextWithEntities } from '../../common/helpers/renderTextWithEntities';
|
||||
import { formatMediaDuration } from '../../../util/dateFormat';
|
||||
import useLang, { LangFn } from '../../../hooks/useLang';
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@ import { getDispatch, withGlobal } from '../../../lib/teact/teactn';
|
||||
|
||||
import { ApiChat, ApiSponsoredMessage, ApiUser } from '../../../api/types';
|
||||
|
||||
import { renderTextWithEntities } from '../../common/helpers/renderMessageText';
|
||||
import { renderTextWithEntities } from '../../common/helpers/renderTextWithEntities';
|
||||
import { selectChat, selectSponsoredMessage, selectUser } from '../../../modules/selectors';
|
||||
import { getChatTitle, getUserFullName } from '../../../modules/helpers';
|
||||
import renderText from '../../common/helpers/renderText';
|
||||
|
||||
@ -8,9 +8,9 @@ import React, {
|
||||
|
||||
import { ANIMATION_END_DELAY } from '../../config';
|
||||
import useShowTransition from '../../hooks/useShowTransition';
|
||||
import { TextPart } from '../common/helpers/renderMessageText';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
import captureEscKeyListener from '../../util/captureEscKeyListener';
|
||||
import { TextPart } from '../common/helpers/renderMessageText';
|
||||
|
||||
import Portal from './Portal';
|
||||
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
import type { TextPart } from '../../components/common/helpers/renderTextWithEntities';
|
||||
|
||||
import { LangFn } from '../../hooks/useLang';
|
||||
import { ApiMessage, ApiMessageEntityTypes } from '../../api/types';
|
||||
import type { TextPart } from '../../components/common/helpers/renderMessageText';
|
||||
import { CONTENT_NOT_SUPPORTED } from '../../config';
|
||||
import { getMessageText } from './messages';
|
||||
import trimText from '../../util/trimText';
|
||||
|
||||
const SPOILER_CHARS = ['⠺', '⠵', '⠞', '⠟'];
|
||||
export const TRUNCATED_SUMMARY_LENGTH = 80;
|
||||
@ -15,7 +17,7 @@ export function getMessageSummaryText(
|
||||
) {
|
||||
const emoji = !noEmoji && getMessageSummaryEmoji(message);
|
||||
const emojiWithSpace = emoji ? `${emoji} ` : '';
|
||||
const text = getMessageTextWithSpoilers(message)?.substr(0, truncateLength);
|
||||
const text = trimText(getMessageTextWithSpoilers(message), truncateLength);
|
||||
const description = getMessageSummaryDescription(lang, message, text);
|
||||
|
||||
return `${emojiWithSpace}${description}`;
|
||||
@ -39,6 +41,7 @@ export function getMessageTextWithSpoilers(message: ApiMessage) {
|
||||
|
||||
const spoiler = generateBrailleSpoiler(length);
|
||||
|
||||
|
||||
return `${accText.substr(0, offset)}${spoiler}${accText.substr(offset + length, accText.length)}`;
|
||||
}, text);
|
||||
}
|
||||
|
||||
@ -287,7 +287,7 @@ function getNotificationContent(chat: ApiChat, message: ApiMessage) {
|
||||
actionTargetUsers,
|
||||
actionTargetMessage,
|
||||
actionTargetChatId,
|
||||
{ asPlain: true },
|
||||
{ asPlainText: true },
|
||||
) as string;
|
||||
} else {
|
||||
const senderName = getMessageSenderName(getTranslation, chat.id, messageSender);
|
||||
|
||||
@ -1,9 +1,7 @@
|
||||
const DEFAULT_MAX_TEXT_LENGTH = 30;
|
||||
|
||||
export default function trimText(text: string | undefined, length = DEFAULT_MAX_TEXT_LENGTH) {
|
||||
if (!text || text.length <= length) {
|
||||
export default function trimText<T extends string | undefined>(text: T, length?: number) {
|
||||
if (!text || !length || text.length <= length) {
|
||||
return text;
|
||||
}
|
||||
|
||||
return `${text.substr(0, length)}...`;
|
||||
return `${text.substring(0, length)}...`;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user