From 7385d8da2f851ef96892e6abc78e8ab9b03a331e Mon Sep 17 00:00:00 2001
From: Alexander Zinchuk
Date: Tue, 18 Jun 2024 16:30:39 +0200
Subject: [PATCH] Show inverted media (#4677)
---
src/api/gramjs/apiBuilders/messages.ts | 3 +
src/api/types/messages.ts | 1 +
src/components/middle/message/Message.scss | 10 +-
src/components/middle/message/Message.tsx | 243 ++++++++++++------
.../middle/message/MessageMeta.scss | 10 +-
src/components/middle/message/WebPage.scss | 2 +-
.../middle/message/_message-content.scss | 20 +-
.../message/helpers/buildContentClassName.ts | 33 +++
8 files changed, 230 insertions(+), 92 deletions(-)
diff --git a/src/api/gramjs/apiBuilders/messages.ts b/src/api/gramjs/apiBuilders/messages.ts
index 161e19feb..7909de94c 100644
--- a/src/api/gramjs/apiBuilders/messages.ts
+++ b/src/api/gramjs/apiBuilders/messages.ts
@@ -191,6 +191,8 @@ export function buildApiMessageWithChatId(
const senderBoosts = mtpMessage.fromBoostsApplied;
const factCheck = mtpMessage.factcheck && buildApiFactCheck(mtpMessage.factcheck);
+ const isInvertedMedia = mtpMessage.invertMedia;
+
const savedPeerId = mtpMessage.savedPeerId && getApiChatIdFromMtpPeer(mtpMessage.savedPeerId);
return omitUndefined({
@@ -233,6 +235,7 @@ export function buildApiMessageWithChatId(
senderBoosts,
viaBusinessBotId: mtpMessage.viaBusinessBotId?.toString(),
factCheck,
+ isInvertedMedia,
});
}
diff --git a/src/api/types/messages.ts b/src/api/types/messages.ts
index 7687f7a7e..75bc279c7 100644
--- a/src/api/types/messages.ts
+++ b/src/api/types/messages.ts
@@ -606,6 +606,7 @@ export interface ApiMessage {
savedPeerId?: string;
senderBoosts?: number;
factCheck?: ApiFactCheck;
+ isInvertedMedia?: true;
}
export interface ApiReactions {
diff --git a/src/components/middle/message/Message.scss b/src/components/middle/message/Message.scss
index fb7470c92..b006cecf3 100644
--- a/src/components/middle/message/Message.scss
+++ b/src/components/middle/message/Message.scss
@@ -644,7 +644,7 @@
.message-content {
&.has-replies:not(.custom-shape),
- &.text:not(.web-page) {
+ &.has-footer:not(.web-page) {
.media-inner,
.Album {
--border-bottom-left-radius: 0;
@@ -652,6 +652,14 @@
}
}
+ &.text.is-inverted-media {
+ .Album,
+ .media-inner {
+ --border-top-left-radius: 0;
+ --border-top-right-radius: 0;
+ }
+ }
+
&.has-subheader .EmbeddedMessage {
& + .Album,
& + .Audio,
diff --git a/src/components/middle/message/Message.tsx b/src/components/middle/message/Message.tsx
index d1a29d1f3..764cebb5c 100644
--- a/src/components/middle/message/Message.tsx
+++ b/src/components/middle/message/Message.tsx
@@ -671,6 +671,7 @@ const Message: FC = ({
giveawayResults,
} = getMessageContent(message);
const text = textMessage && getMessageContent(textMessage).text;
+ const isInvertedMedia = Boolean(message.isInvertedMedia);
const { replyToMsgId, replyToPeerId, isQuote } = messageReplyInfo || {};
const { peerId: storyReplyPeerId, storyId: storyReplyId } = storyReplyInfo || {};
@@ -703,6 +704,9 @@ const Message: FC = ({
const withQuickReactionButton = !isTouchScreen && !phoneCall && !isInSelectMode && defaultReaction
&& !isInDocumentGroupNotLast && !isStoryMention && !hasTtl;
+ const hasOutsideReactions = hasReactions
+ && (isCustomShape || ((photo || video || storyData || (location?.type === 'geo')) && !hasText));
+
const contentClassName = buildContentClassName(message, album, {
hasSubheader,
isCustomShape,
@@ -716,6 +720,7 @@ const Message: FC = ({
isGeoLiveActive: location?.type === 'geoLive' && !isGeoLiveExpired(message),
withVoiceTranscription,
peerColorClass: getPeerColorClass(messageColorPeer, noUserColors),
+ hasOutsideReactions,
});
const withAppendix = contentClassName.includes('has-appendix');
@@ -726,7 +731,9 @@ const Message: FC = ({
metaPosition = 'none';
} else if (isInDocumentGroupNotLast) {
metaPosition = 'none';
- } else if (hasText && !webPage && !emojiSize) {
+ } else if (hasText && !webPage && !emojiSize && !isInvertedMedia) {
+ metaPosition = 'in-text';
+ } else if (isInvertedMedia && !emojiSize && (hasFactCheck || webPage)) {
metaPosition = 'in-text';
} else {
metaPosition = 'standalone';
@@ -734,7 +741,7 @@ const Message: FC = ({
let reactionsPosition!: ReactionsPosition;
if (hasReactions) {
- if (isCustomShape || ((photo || video || storyData || (location?.type === 'geo')) && !hasText)) {
+ if (hasOutsideReactions) {
reactionsPosition = 'outside';
} else if (asForwarded) {
metaPosition = 'standalone';
@@ -992,7 +999,8 @@ const Message: FC = ({
hasSubheader && 'with-subheader',
noMediaCorners && 'no-media-corners',
);
- const hasCustomAppendix = isLastInGroup && !hasText && !asForwarded && !withCommentButton;
+ const hasCustomAppendix = isLastInGroup
+ && (!hasText || (isInvertedMedia && !hasFactCheck && !hasReactions)) && !asForwarded && !withCommentButton;
const textContentClass = buildClassName(
'text-content',
'clearfix',
@@ -1081,17 +1089,6 @@ const Message: FC = ({
activeEmojiInteractions={activeEmojiInteractions}
/>
)}
- {isAlbum && (
-
- )}
{phoneCall && (
= ({
chatId={chatId}
/>
)}
- {!isAlbum && photo && (
-
- )}
{!isAlbum && video && video.isRound && (
= ({
onReadMedia={shouldReadMedia ? handleReadMedia : undefined}
/>
)}
- {!isAlbum && video && !video.isRound && (
-
- )}
{(audio || voice) && (
)}
- {!hasAnimatedEmoji && hasText && (
-
- {renderMessageText()}
- {isTranslationPending && (
-
-
- {renderMessageText(true)}
-
+ {isInvertedMedia && renderInvertedMediaContent(hasCustomAppendix)}
+
+ {!isInvertedMedia && (
+ <>
+ {renderInvertibleMediaContent(hasCustomAppendix)}
+ {hasText && !hasAnimatedEmoji && (
+
+ {renderMessageText()}
+ {isTranslationPending && (
+
+
+ {renderMessageText(true)}
+
+
+ )}
+ {hasFactCheck && (
+
+ )}
+ {metaPosition === 'in-text' && renderReactionsAndMeta()}
)}
- {hasFactCheck && (
-
- )}
- {metaPosition === 'in-text' && renderReactionsAndMeta()}
-
+ {renderWebPage()}
+ >
)}
- {webPage && (
-
- )}
{invoice && !invoice.extendedMedia && (
= ({
);
}
+ function renderInvertedMediaContent(hasCustomAppendix: boolean) {
+ const textContentClass = buildClassName(
+ 'text-content',
+ 'clearfix',
+ );
+ const footerClass = buildClassName(
+ 'text-content',
+ 'clearfix',
+ metaPosition === 'in-text' && 'with-meta',
+ outgoingStatus && 'with-outgoing-icon',
+ );
+
+ const hasMediaAfterText = isAlbum || (!isAlbum && photo) || (!isAlbum && video && !video.isRound);
+ const hasContentAfterText = hasMediaAfterText || (!hasAnimatedEmoji && hasFactCheck);
+ const isMetaInText = metaPosition === 'in-text';
+
+ return (
+ <>
+ {renderWebPage()}
+ {hasText && !hasAnimatedEmoji && (
+
+ {renderMessageText()}
+ {isTranslationPending && (
+
+
+ {renderMessageText(true)}
+
+
+ )}
+ {!hasContentAfterText && isMetaInText && renderReactionsAndMeta()}
+
+ )}
+
+ {hasContentAfterText && (
+ <>
+ {renderInvertibleMediaContent(hasCustomAppendix)}
+ {!hasAnimatedEmoji && (
+
+ {hasFactCheck && (
+
+ )}
+ {isMetaInText && renderReactionsAndMeta()}
+
+ )}
+ >
+ )}
+
+ >
+ );
+ }
+
+ function renderWebPage() {
+ return webPage && (
+
+ );
+ }
+
+ function renderInvertibleMediaContent(hasCustomAppendix : boolean) {
+ return (
+ <>
+ {isAlbum && (
+
+ )}
+ {!isAlbum && photo && (
+
+ )}
+ {!isAlbum && video && !video.isRound && (
+
+ )}
+ >
+ );
+ }
+
function renderSenderName() {
const media = photo || video || location;
const shouldRender = !(isCustomShape && !viaBotId) && (
diff --git a/src/components/middle/message/MessageMeta.scss b/src/components/middle/message/MessageMeta.scss
index 31b05e2ff..e3a9ad527 100644
--- a/src/components/middle/message/MessageMeta.scss
+++ b/src/components/middle/message/MessageMeta.scss
@@ -80,7 +80,7 @@
background: none;
}
- .media:not(.text) &,
+ .media.no-footer &,
.Message .custom-shape &,
.Message .invoice:not(.has-reactions) & {
--color-accent-own: white;
@@ -98,7 +98,7 @@
}
}
- .media:not(.text) &,
+ .media.no-footer &,
.Message .invoice:not(.has-reactions) & {
background: rgba(0, 0, 0, 0.2);
}
@@ -122,12 +122,12 @@
padding: 0 0.375rem 0 0.3125rem;
}
- .is-forwarded.media:not(.text):dir(rtl) &,
+ .is-forwarded.media.no-footer:dir(rtl) &,
.Message .is-forwarded.custom-shape:dir(rtl) & {
left: 0.8125rem;
}
- .is-forwarded.media:not(.text) & {
+ .is-forwarded.media.no-footer & {
bottom: 0.9375rem;
right: 0.8125rem;
max-width: calc(100% - 2.25rem);
@@ -152,7 +152,7 @@
bottom: 3.375rem;
}
- .message-content.has-replies.text:not(.custom-shape) & {
+ .message-content.has-replies.text:not(.custom-shape):not(.is-inverted-media) & {
bottom: 3.1875rem;
}
diff --git a/src/components/middle/message/WebPage.scss b/src/components/middle/message/WebPage.scss
index b1dc1320e..262b7538d 100644
--- a/src/components/middle/message/WebPage.scss
+++ b/src/components/middle/message/WebPage.scss
@@ -126,7 +126,7 @@
}
}
- .message-content:not(.has-reactions) & {
+ .message-content:not(.has-reactions):not(.is-inverted-media) & {
margin-bottom: calc(var(--message-meta-height)) !important;
}
diff --git a/src/components/middle/message/_message-content.scss b/src/components/middle/message/_message-content.scss
index 3558b6d4d..18b5dae44 100644
--- a/src/components/middle/message/_message-content.scss
+++ b/src/components/middle/message/_message-content.scss
@@ -582,6 +582,20 @@
margin-top: -0.3125rem;
}
+ &.text.is-inverted-media .media-inner,
+ &.text.is-inverted-media .Album {
+ margin-top: 0.3125rem;
+ }
+
+ &.has-solid-background.is-inverted-media.no-footer .media-inner,
+ &.has-solid-background.is-inverted-media.no-footer .Album {
+ margin-bottom: -0.375rem;
+
+ body.is-ios .Message.own & {
+ margin-bottom: -0.4375rem;
+ }
+ }
+
&.has-subheader .media-inner,
&.force-sender-name .Album,
&.has-subheader .Album,
@@ -599,8 +613,10 @@
}
// Moved below `.has-subheader` to overwrite its styles
- &.text .media-inner,
- &.text .Album {
+ &.is-inverted-media.has-fact-check .media-inner,
+ &.is-inverted-media.has-fact-check .Album,
+ &.text:not(.is-inverted-media) .media-inner,
+ &.text:not(.is-inverted-media) .Album {
margin-bottom: 0.375rem !important;
body.is-ios .Message.own & {
diff --git a/src/components/middle/message/helpers/buildContentClassName.ts b/src/components/middle/message/helpers/buildContentClassName.ts
index 87213d6f8..652e83362 100644
--- a/src/components/middle/message/helpers/buildContentClassName.ts
+++ b/src/components/middle/message/helpers/buildContentClassName.ts
@@ -20,6 +20,7 @@ export function buildContentClassName(
isGeoLiveActive,
withVoiceTranscription,
peerColorClass,
+ hasOutsideReactions,
}: {
hasSubheader?: boolean;
isCustomShape?: boolean | number;
@@ -33,6 +34,7 @@ export function buildContentClassName(
isGeoLiveActive?: boolean;
withVoiceTranscription?: boolean;
peerColorClass?: string;
+ hasOutsideReactions?: boolean;
} = {},
) {
const {
@@ -42,12 +44,25 @@ export function buildContentClassName(
const text = album?.hasMultipleCaptions ? undefined : getMessageContent(album?.captionMessage || message).text;
const hasFactCheck = Boolean(message.factCheck?.text);
+ const isInvertedMedia = message.isInvertedMedia;
+ const isInvertibleMedia = photo || (video && !video?.isRound) || album || webPage;
+
const classNames = [MESSAGE_CONTENT_CLASS_NAME];
const isMedia = storyData || photo || video || location || invoice?.extendedMedia;
const hasText = text || location?.type === 'venue' || isGeoLiveActive || hasFactCheck;
const isMediaWithNoText = isMedia && !hasText;
const isViaBot = Boolean(message.viaBotId);
+ const hasFooter = (() => {
+ if (isInvertedMedia && isInvertibleMedia) {
+ if (hasReactions && !hasOutsideReactions) return true;
+ if (hasFactCheck) return true;
+ if (webPage && hasText) return true;
+ return false;
+ }
+ return hasText;
+ })();
+
if (peerColorClass) {
classNames.push(peerColorClass);
}
@@ -130,6 +145,10 @@ export function buildContentClassName(
classNames.push('has-reactions');
}
+ if (hasOutsideReactions) {
+ classNames.push('has-outside-reactions');
+ }
+
if (isViaBot) {
classNames.push('is-via-bot');
}
@@ -149,10 +168,24 @@ export function buildContentClassName(
classNames.push('has-solid-background');
}
+ if (hasFactCheck) {
+ classNames.push('has-fact-check');
+ }
+
if (isLastInGroup && (photo || !isMediaWithNoText || (location && asForwarded))) {
classNames.push('has-appendix');
}
}
+ if (isInvertibleMedia && isInvertedMedia) {
+ classNames.push('is-inverted-media');
+ }
+
+ if (hasFooter) {
+ classNames.push('has-footer');
+ } else {
+ classNames.push('no-footer');
+ }
+
return classNames.join(' ');
}