Message: Show forward counter on view hover (#5447)

This commit is contained in:
zubiden 2025-01-21 18:20:54 +01:00 committed by Alexander Zinchuk
parent 9b57bd9e21
commit 5381c148e4
5 changed files with 82 additions and 19 deletions

View File

@ -468,6 +468,13 @@ export function updater(update: Update) {
id: update.id, id: update.id,
message: { viewsCount: update.views }, message: { viewsCount: update.views },
}); });
} else if (update instanceof GramJs.UpdateChannelMessageForwards) {
sendApiUpdate({
'@type': 'updateMessage',
chatId: buildApiPeerId(update.channelId, 'channel'),
id: update.id,
message: { forwardsCount: update.forwards },
});
// Chats // Chats
} else if (update instanceof GramJs.UpdateReadHistoryInbox) { } else if (update instanceof GramJs.UpdateReadHistoryInbox) {

View File

@ -732,6 +732,18 @@
"Reminders" = "Reminders"; "Reminders" = "Reminders";
"Messages_one" = "{count} message"; "Messages_one" = "{count} message";
"Messages_other" = "{count} messages"; "Messages_other" = "{count} messages";
"MessageTooltipForwards_one" = "Shares: {count}";
"MessageTooltipForwards_other" = "Shares: {count}";
"MessageTooltipViews_one" = "Views: {count}";
"MessageTooltipViews_other" = "Views: {count}";
"MessageTooltipReplies_one" = "Replies: {count}";
"MessageTooltipReplies_other" = "Replies: {count}";
"MessageTooltipEditedDate" = "Edited: {date}";
"MessageTooltipForwardedDate" = "Original: {date}";
"MessageMetaEdited" = "edited";
"MessageMetaApproximate" = "approx.";
"MessageMetaImported" = "imported";
"ImportedInfo" = "This message was imported from another app. We can't guarantee it's real.";
"ScheduledMessagesEmptyPlaceholder" = "No scheduled messages here yet..."; "ScheduledMessagesEmptyPlaceholder" = "No scheduled messages here yet...";
"ConversationCloudStorageInfoTitle" = "Your Cloud Storage"; "ConversationCloudStorageInfoTitle" = "Your Cloud Storage";
"ConversationClousStorageInfoDescription1" = "• Forward messages here to save them"; "ConversationClousStorageInfoDescription1" = "• Forward messages here to save them";

View File

@ -74,12 +74,14 @@
margin-left: 0.125rem; margin-left: 0.125rem;
margin-right: 0.375rem; margin-right: 0.375rem;
font-size: 1.125rem; font-size: 1.125rem;
line-height: inherit;
} }
.icon-reply-filled { .icon-reply-filled {
margin-left: 0.125rem; margin-left: 0.125rem;
margin-right: 0.375rem; margin-right: 0.375rem;
font-size: 0.75rem; font-size: 0.75rem;
line-height: inherit;
} }
.has-solid-background & { .has-solid-background & {

View File

@ -12,6 +12,7 @@ import { formatIntegerCompact } from '../../../util/textFormat';
import renderText from '../../common/helpers/renderText'; import renderText from '../../common/helpers/renderText';
import useFlag from '../../../hooks/useFlag'; import useFlag from '../../../hooks/useFlag';
import useLang from '../../../hooks/useLang';
import useOldLang from '../../../hooks/useOldLang'; import useOldLang from '../../../hooks/useOldLang';
import AnimatedCounter from '../../common/AnimatedCounter'; import AnimatedCounter from '../../common/AnimatedCounter';
@ -57,14 +58,19 @@ const MessageMeta: FC<OwnProps> = ({
onOpenThread, onOpenThread,
}) => { }) => {
const { showNotification } = getActions(); const { showNotification } = getActions();
const lang = useOldLang();
const [isActivated, markActivated] = useFlag(); const [isActivated, markActivated] = useFlag();
const oldLang = useOldLang();
const lang = useLang();
function handleImportedClick(e: React.MouseEvent) { function handleImportedClick(e: React.MouseEvent) {
e.stopPropagation(); e.stopPropagation();
showNotification({ showNotification({
message: lang('ImportedInfo'), message: {
key: 'ImportedInfo',
},
}); });
} }
@ -73,42 +79,59 @@ const MessageMeta: FC<OwnProps> = ({
onOpenThread(); onOpenThread();
} }
const title = useMemo(() => { const dateTitle = useMemo(() => {
if (!isActivated) return undefined; if (!isActivated) return undefined;
const createDateTime = formatDateTimeToString(message.date * 1000, lang.code, undefined, lang.timeFormat); const createDateTime = formatDateTimeToString(message.date * 1000, oldLang.code, undefined, oldLang.timeFormat);
const editDateTime = message.isEdited const editDateTime = message.isEdited
&& formatDateTimeToString(message.editDate! * 1000, lang.code, undefined, lang.timeFormat); && formatDateTimeToString(message.editDate! * 1000, oldLang.code, undefined, oldLang.timeFormat);
const forwardedDateTime = message.forwardInfo const forwardedDateTime = message.forwardInfo
&& formatDateTimeToString( && formatDateTimeToString(
(message.forwardInfo.savedDate || message.forwardInfo.date) * 1000, (message.forwardInfo.savedDate || message.forwardInfo.date) * 1000,
lang.code, oldLang.code,
undefined, undefined,
lang.timeFormat, oldLang.timeFormat,
); );
let text = createDateTime; let text = createDateTime;
if (editDateTime) { if (editDateTime) {
text += '\n'; text += '\n';
text += lang('lng_edited_date').replace('{date}', editDateTime); text += lang('MessageTooltipEditedDate', { date: editDateTime });
} }
if (forwardedDateTime) { if (forwardedDateTime) {
text += '\n'; text += '\n';
text += lang('lng_forwarded_date').replace('{date}', forwardedDateTime); text += lang('MessageTooltipForwardedDate', { date: forwardedDateTime });
} }
return text; return text;
// We need to listen to timeformat change // We need to listen to timeformat change
// eslint-disable-next-line react-hooks-static-deps/exhaustive-deps // eslint-disable-next-line react-hooks-static-deps/exhaustive-deps
}, [isActivated, lang, message, lang.timeFormat]); }, [isActivated, oldLang, message, oldLang.timeFormat]);
const viewsTitle = useMemo(() => {
if (!message.viewsCount) return undefined;
let text = lang('MessageTooltipViews', { count: message.viewsCount }, { pluralValue: message.viewsCount });
if (message.forwardsCount) {
text += '\n';
text += lang('MessageTooltipForwards', { count: message.forwardsCount }, { pluralValue: message.forwardsCount });
}
return text;
}, [lang, message.forwardsCount, message.viewsCount]);
const repliesTitle = useMemo(() => {
const count = repliesThreadInfo?.messagesCount;
if (!count) return undefined;
return lang('MessageTooltipReplies', { count }, { pluralValue: count });
}, [lang, repliesThreadInfo]);
const date = useMemo(() => { const date = useMemo(() => {
const time = formatTime(lang, message.date * 1000); const time = formatTime(oldLang, message.date * 1000);
if (!withFullDate) { if (!withFullDate) {
return time; return time;
} }
return formatPastTimeShort(lang, (message.forwardInfo?.date || message.date) * 1000, true); return formatPastTimeShort(oldLang, (message.forwardInfo?.date || message.date) * 1000, true);
}, [lang, message.date, message.forwardInfo?.date, withFullDate]); }, [oldLang, message.date, message.forwardInfo?.date, withFullDate]);
const fullClassName = buildClassName( const fullClassName = buildClassName(
'MessageMeta', 'MessageMeta',
@ -133,14 +156,14 @@ const MessageMeta: FC<OwnProps> = ({
)} )}
{Boolean(message.viewsCount) && ( {Boolean(message.viewsCount) && (
<> <>
<span className="message-views"> <span className="message-views" title={viewsTitle}>
{formatIntegerCompact(message.viewsCount!)} {formatIntegerCompact(message.viewsCount!)}
</span> </span>
<Icon name="channelviews" /> <Icon name="channelviews" />
</> </>
)} )}
{!noReplies && Boolean(repliesThreadInfo?.messagesCount) && ( {!noReplies && Boolean(repliesThreadInfo?.messagesCount) && (
<span onClick={handleOpenThread} className="message-replies-wrapper"> <span onClick={handleOpenThread} className="message-replies-wrapper" title={repliesTitle}>
<span className="message-replies"> <span className="message-replies">
<AnimatedCounter text={formatIntegerCompact(repliesThreadInfo!.messagesCount!)} /> <AnimatedCounter text={formatIntegerCompact(repliesThreadInfo!.messagesCount!)} />
</span> </span>
@ -153,17 +176,17 @@ const MessageMeta: FC<OwnProps> = ({
{signature && ( {signature && (
<span className="message-signature">{renderText(signature)}</span> <span className="message-signature">{renderText(signature)}</span>
)} )}
<span className="message-time" title={title} onMouseEnter={markActivated}> <span className="message-time" title={dateTitle} onMouseEnter={markActivated}>
{message.forwardInfo?.isImported && ( {message.forwardInfo?.isImported && (
<> <>
<span className="message-imported" onClick={handleImportedClick}> <span className="message-imported" onClick={handleImportedClick}>
{formatDateTimeToString(message.forwardInfo.date * 1000, lang.code, true)} {formatDateTimeToString(message.forwardInfo.date * 1000, lang.code, true)}
</span> </span>
<span className="message-imported" onClick={handleImportedClick}>{lang('ImportedMessage')}</span> <span className="message-imported" onClick={handleImportedClick}>{lang('MessageMetaImported')}</span>
</> </>
)} )}
{message.isEdited && `${lang('EditedMessage')} `} {message.isEdited && `${lang('MessageMetaEdited')} `}
{message.isVideoProcessingPending && `${lang('lng_approximate')} `} {message.isVideoProcessingPending && `${lang('MessageMetaApproximate')} `}
{date} {date}
</span> </span>
{outgoingStatus && ( {outgoingStatus && (

View File

@ -627,6 +627,10 @@ export interface LangPair {
'ContextCopySelectedItems': undefined; 'ContextCopySelectedItems': undefined;
'EditAdminGroupDeleteMessages': undefined; 'EditAdminGroupDeleteMessages': undefined;
'Reminders': undefined; 'Reminders': undefined;
'MessageMetaEdited': undefined;
'MessageMetaApproximate': undefined;
'MessageMetaImported': undefined;
'ImportedInfo': undefined;
'ScheduledMessagesEmptyPlaceholder': undefined; 'ScheduledMessagesEmptyPlaceholder': undefined;
'ConversationCloudStorageInfoTitle': undefined; 'ConversationCloudStorageInfoTitle': undefined;
'ConversationClousStorageInfoDescription1': undefined; 'ConversationClousStorageInfoDescription1': undefined;
@ -1396,6 +1400,12 @@ export interface LangPairWithVariables<V extends unknown = LangVariable> {
'MessageScheduledOn': { 'MessageScheduledOn': {
'date': V; 'date': V;
}; };
'MessageTooltipEditedDate': {
'date': V;
};
'MessageTooltipForwardedDate': {
'date': V;
};
'EmptyGroupInfoLine1': { 'EmptyGroupInfoLine1': {
'count': V; 'count': V;
}; };
@ -1761,6 +1771,15 @@ export interface LangPairPluralWithVariables<V extends unknown = LangVariable> {
'Messages': { 'Messages': {
'count': V; 'count': V;
}; };
'MessageTooltipForwards': {
'count': V;
};
'MessageTooltipViews': {
'count': V;
};
'MessageTooltipReplies': {
'count': V;
};
'Hours': { 'Hours': {
'count': V; 'count': V;
}; };