Show inverted media (#4677)

This commit is contained in:
Alexander Zinchuk 2024-06-18 16:30:39 +02:00
parent 72e5dc177d
commit 7385d8da2f
8 changed files with 230 additions and 92 deletions

View File

@ -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<ApiMessage>({
@ -233,6 +235,7 @@ export function buildApiMessageWithChatId(
senderBoosts,
viaBusinessBotId: mtpMessage.viaBusinessBotId?.toString(),
factCheck,
isInvertedMedia,
});
}

View File

@ -606,6 +606,7 @@ export interface ApiMessage {
savedPeerId?: string;
senderBoosts?: number;
factCheck?: ApiFactCheck;
isInvertedMedia?: true;
}
export interface ApiReactions {

View File

@ -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,

View File

@ -671,6 +671,7 @@ const Message: FC<OwnProps & StateProps> = ({
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<OwnProps & StateProps> = ({
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<OwnProps & StateProps> = ({
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<OwnProps & StateProps> = ({
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<OwnProps & StateProps> = ({
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<OwnProps & StateProps> = ({
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<OwnProps & StateProps> = ({
activeEmojiInteractions={activeEmojiInteractions}
/>
)}
{isAlbum && (
<Album
album={album!}
albumLayout={albumLayout!}
observeIntersection={observeIntersectionForLoading}
isOwn={isOwn}
isProtected={isProtected}
hasCustomAppendix={hasCustomAppendix}
onMediaClick={handleAlbumMediaClick}
/>
)}
{phoneCall && (
<MessagePhoneCall
message={message}
@ -1099,23 +1096,6 @@ const Message: FC<OwnProps & StateProps> = ({
chatId={chatId}
/>
)}
{!isAlbum && photo && (
<Photo
message={message}
observeIntersection={observeIntersectionForLoading}
noAvatars={noAvatars}
canAutoLoad={canAutoLoadMedia}
uploadProgress={uploadProgress}
shouldAffectAppendix={hasCustomAppendix}
isDownloading={isDownloading}
isProtected={isProtected}
asForwarded={asForwarded}
theme={theme}
forcedWidth={contentWidth}
onClick={handlePhotoMediaClick}
onCancelUpload={handleCancelUpload}
/>
)}
{!isAlbum && video && video.isRound && (
<RoundVideo
message={message}
@ -1125,23 +1105,6 @@ const Message: FC<OwnProps & StateProps> = ({
onReadMedia={shouldReadMedia ? handleReadMedia : undefined}
/>
)}
{!isAlbum && video && !video.isRound && (
<Video
message={message}
observeIntersectionForLoading={observeIntersectionForLoading}
observeIntersectionForPlaying={observeIntersectionForPlaying}
forcedWidth={contentWidth}
noAvatars={noAvatars}
canAutoLoad={canAutoLoadMedia}
canAutoPlay={canAutoPlayMedia}
uploadProgress={uploadProgress}
isDownloading={isDownloading}
isProtected={isProtected}
asForwarded={asForwarded}
onClick={handleVideoMediaClick}
onCancelUpload={handleCancelUpload}
/>
)}
{(audio || voice) && (
<Audio
theme={theme}
@ -1222,45 +1185,31 @@ const Message: FC<OwnProps & StateProps> = ({
</p>
)}
{!hasAnimatedEmoji && hasText && (
<div className={textContentClass} dir="auto">
{renderMessageText()}
{isTranslationPending && (
<div className="translation-animation">
<div className="text-loading">
{renderMessageText(true)}
</div>
{isInvertedMedia && renderInvertedMediaContent(hasCustomAppendix)}
{!isInvertedMedia && (
<>
{renderInvertibleMediaContent(hasCustomAppendix)}
{hasText && !hasAnimatedEmoji && (
<div className={textContentClass} dir="auto">
{renderMessageText()}
{isTranslationPending && (
<div className="translation-animation">
<div className="text-loading">
{renderMessageText(true)}
</div>
</div>
)}
{hasFactCheck && (
<FactCheck factCheck={factCheck} isToggleDisabled={isInSelectMode} />
)}
{metaPosition === 'in-text' && renderReactionsAndMeta()}
</div>
)}
{hasFactCheck && (
<FactCheck factCheck={factCheck} isToggleDisabled={isInSelectMode} />
)}
{metaPosition === 'in-text' && renderReactionsAndMeta()}
</div>
{renderWebPage()}
</>
)}
{webPage && (
<WebPage
message={message}
observeIntersectionForLoading={observeIntersectionForLoading}
observeIntersectionForPlaying={observeIntersectionForPlaying}
noAvatars={noAvatars}
canAutoLoad={canAutoLoadMedia}
canAutoPlay={canAutoPlayMedia}
asForwarded={asForwarded}
isDownloading={isDownloading}
isProtected={isProtected}
theme={theme}
story={webPageStory}
isConnected={isConnected}
backgroundEmojiId={sender?.color?.backgroundEmojiId}
shouldWarnAboutSvg={shouldWarnAboutSvg}
autoLoadFileMaxSizeMb={autoLoadFileMaxSizeMb}
onAudioPlay={handleAudioPlay}
onMediaClick={handleMediaClick}
onCancelMediaTransfer={handleCancelUpload}
/>
)}
{invoice && !invoice.extendedMedia && (
<Invoice
message={message}
@ -1284,6 +1233,134 @@ const Message: FC<OwnProps & StateProps> = ({
);
}
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 && (
<div className={textContentClass} dir="auto">
{renderMessageText()}
{isTranslationPending && (
<div className="translation-animation">
<div className="text-loading">
{renderMessageText(true)}
</div>
</div>
)}
{!hasContentAfterText && isMetaInText && renderReactionsAndMeta()}
</div>
)}
{hasContentAfterText && (
<>
{renderInvertibleMediaContent(hasCustomAppendix)}
{!hasAnimatedEmoji && (
<div className={footerClass} dir="auto">
{hasFactCheck && (
<FactCheck factCheck={factCheck} isToggleDisabled={isInSelectMode} />
)}
{isMetaInText && renderReactionsAndMeta()}
</div>
)}
</>
)}
</>
);
}
function renderWebPage() {
return webPage && (
<WebPage
message={message}
observeIntersectionForLoading={observeIntersectionForLoading}
observeIntersectionForPlaying={observeIntersectionForPlaying}
noAvatars={noAvatars}
canAutoLoad={canAutoLoadMedia}
canAutoPlay={canAutoPlayMedia}
asForwarded={asForwarded}
isDownloading={isDownloading}
isProtected={isProtected}
theme={theme}
story={webPageStory}
isConnected={isConnected}
backgroundEmojiId={sender?.color?.backgroundEmojiId}
shouldWarnAboutSvg={shouldWarnAboutSvg}
autoLoadFileMaxSizeMb={autoLoadFileMaxSizeMb}
onAudioPlay={handleAudioPlay}
onMediaClick={handleMediaClick}
onCancelMediaTransfer={handleCancelUpload}
/>
);
}
function renderInvertibleMediaContent(hasCustomAppendix : boolean) {
return (
<>
{isAlbum && (
<Album
album={album!}
albumLayout={albumLayout!}
observeIntersection={observeIntersectionForLoading}
isOwn={isOwn}
isProtected={isProtected}
hasCustomAppendix={hasCustomAppendix}
onMediaClick={handleAlbumMediaClick}
/>
)}
{!isAlbum && photo && (
<Photo
message={message}
observeIntersection={observeIntersectionForLoading}
noAvatars={noAvatars}
canAutoLoad={canAutoLoadMedia}
uploadProgress={uploadProgress}
shouldAffectAppendix={hasCustomAppendix}
isDownloading={isDownloading}
isProtected={isProtected}
asForwarded={asForwarded}
theme={theme}
forcedWidth={contentWidth}
onClick={handlePhotoMediaClick}
onCancelUpload={handleCancelUpload}
/>
)}
{!isAlbum && video && !video.isRound && (
<Video
message={message}
observeIntersectionForLoading={observeIntersectionForLoading}
observeIntersectionForPlaying={observeIntersectionForPlaying}
forcedWidth={contentWidth}
noAvatars={noAvatars}
canAutoLoad={canAutoLoadMedia}
canAutoPlay={canAutoPlayMedia}
uploadProgress={uploadProgress}
isDownloading={isDownloading}
isProtected={isProtected}
asForwarded={asForwarded}
onClick={handleVideoMediaClick}
onCancelUpload={handleCancelUpload}
/>
)}
</>
);
}
function renderSenderName() {
const media = photo || video || location;
const shouldRender = !(isCustomShape && !viaBotId) && (

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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 & {

View File

@ -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(' ');
}