Message: Fix narrow media in messages (#2868)
This commit is contained in:
parent
6176ef1a98
commit
4390800a46
@ -335,7 +335,7 @@ function getNodes(origin: MediaViewerOrigin, message?: ApiMessage) {
|
||||
case MediaViewerOrigin.Inline:
|
||||
default:
|
||||
containerSelector = `.Transition__slide--active > .MessageList #${getMessageHtmlId(message!.id)}`;
|
||||
mediaSelector = `${MESSAGE_CONTENT_SELECTOR} .full-media, ${MESSAGE_CONTENT_SELECTOR} .thumbnail`;
|
||||
mediaSelector = `${MESSAGE_CONTENT_SELECTOR} .full-media,${MESSAGE_CONTENT_SELECTOR} .thumbnail:not(.blurred-bg)`;
|
||||
}
|
||||
|
||||
const container = document.querySelector<HTMLElement>(containerSelector)!;
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
.Album {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
margin: auto;
|
||||
|
||||
.message-content.media.text & {
|
||||
margin: -0.3125rem -0.5rem 0.3125rem;
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
}
|
||||
|
||||
.invoice-image {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
max-height: 30rem;
|
||||
object-fit: cover;
|
||||
@ -42,4 +43,20 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.invoice-image-container {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.thumbnail {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,6 +13,7 @@ import getCustomAppendixBg from './helpers/getCustomAppendixBg';
|
||||
import useLayoutEffectWithPrevDeps from '../../../hooks/useLayoutEffectWithPrevDeps';
|
||||
import useLang from '../../../hooks/useLang';
|
||||
import useMedia from '../../../hooks/useMedia';
|
||||
import useBlurredMediaThumbRef from './hooks/useBlurredMediaThumbRef';
|
||||
|
||||
import Skeleton from '../../ui/Skeleton';
|
||||
|
||||
@ -24,6 +25,7 @@ type OwnProps = {
|
||||
isInSelectMode?: boolean;
|
||||
isSelected?: boolean;
|
||||
theme: ISettings['theme'];
|
||||
forcedWidth?: number;
|
||||
};
|
||||
|
||||
const Invoice: FC<OwnProps> = ({
|
||||
@ -32,6 +34,7 @@ const Invoice: FC<OwnProps> = ({
|
||||
isInSelectMode,
|
||||
isSelected,
|
||||
theme,
|
||||
forcedWidth,
|
||||
}) => {
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
@ -49,6 +52,8 @@ const Invoice: FC<OwnProps> = ({
|
||||
} = invoice!;
|
||||
|
||||
const photoUrl = useMedia(getWebDocumentHash(photo));
|
||||
const withBlurredBackground = Boolean(forcedWidth);
|
||||
const blurredBackgroundRef = useBlurredMediaThumbRef(message, !withBlurredBackground, photoUrl);
|
||||
|
||||
useLayoutEffectWithPrevDeps(([prevShouldAffectAppendix]) => {
|
||||
if (!shouldAffectAppendix) {
|
||||
@ -79,16 +84,26 @@ const Invoice: FC<OwnProps> = ({
|
||||
<div>{renderText(text, ['emoji', 'br'])}</div>
|
||||
)}
|
||||
<div className={`description ${photo ? 'has-image' : ''}`}>
|
||||
{photoUrl && (
|
||||
<img
|
||||
className="invoice-image"
|
||||
src={photoUrl}
|
||||
alt=""
|
||||
crossOrigin="anonymous"
|
||||
/>
|
||||
)}
|
||||
{!photoUrl && photo && (
|
||||
<Skeleton width={photo.dimensions?.width} height={photo.dimensions?.height} forceAspectRatio />
|
||||
{Boolean(photo) && (
|
||||
<div className="invoice-image-container">
|
||||
{withBlurredBackground && <canvas ref={blurredBackgroundRef} className="thumbnail blurred-bg" />}
|
||||
{photoUrl && (
|
||||
<img
|
||||
className="invoice-image"
|
||||
src={photoUrl}
|
||||
alt=""
|
||||
style={forcedWidth ? `width: ${forcedWidth}px` : undefined}
|
||||
crossOrigin="anonymous"
|
||||
/>
|
||||
)}
|
||||
{!photoUrl && photo && (
|
||||
<Skeleton
|
||||
width={forcedWidth || photo.dimensions?.width}
|
||||
height={photo.dimensions?.height}
|
||||
forceAspectRatio
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<p className="description-text">
|
||||
{formatCurrency(amount, currency, lang.code)}
|
||||
|
||||
@ -96,7 +96,12 @@ import {
|
||||
ROUND_VIDEO_DIMENSIONS_PX,
|
||||
} from '../../common/helpers/mediaDimensions';
|
||||
import { buildContentClassName } from './helpers/buildContentClassName';
|
||||
import { getMinMediaWidth, calculateMediaDimensions } from './helpers/mediaDimensions';
|
||||
import {
|
||||
getMinMediaWidth,
|
||||
calculateMediaDimensions,
|
||||
MIN_MEDIA_WIDTH_WITH_COMMENTS,
|
||||
MIN_MEDIA_WIDTH_WITH_TEXT,
|
||||
} from './helpers/mediaDimensions';
|
||||
import { calculateAlbumLayout } from './helpers/calculateAlbumLayout';
|
||||
import renderText from '../../common/helpers/renderText';
|
||||
import calculateAuthorWidth from './helpers/calculateAuthorWidth';
|
||||
@ -223,6 +228,7 @@ type StateProps = {
|
||||
isPinnedList?: boolean;
|
||||
canAutoLoadMedia?: boolean;
|
||||
canAutoPlayMedia?: boolean;
|
||||
hasLinkedChat?: boolean;
|
||||
shouldLoopStickers?: boolean;
|
||||
autoLoadFileMaxSizeMb: number;
|
||||
repliesThreadInfo?: ApiThreadInfo;
|
||||
@ -317,6 +323,7 @@ const Message: FC<OwnProps & StateProps> = ({
|
||||
animatedEmoji,
|
||||
animatedCustomEmoji,
|
||||
genericEffects,
|
||||
hasLinkedChat,
|
||||
isInSelectMode,
|
||||
isSelected,
|
||||
isGroupSelected,
|
||||
@ -565,6 +572,10 @@ const Message: FC<OwnProps & StateProps> = ({
|
||||
|
||||
const { phoneCall } = action || {};
|
||||
|
||||
const isMediaWidthWithCommentButton = (repliesThreadInfo || (hasLinkedChat && isChannel && isLocal))
|
||||
&& !isInDocumentGroupNotLast
|
||||
&& messageListType === 'thread'
|
||||
&& !noComments;
|
||||
const withCommentButton = repliesThreadInfo && !isInDocumentGroupNotLast && messageListType === 'thread'
|
||||
&& !noComments;
|
||||
const withQuickReactionButton = !IS_TOUCH_ENV && !phoneCall && !isInSelectMode && defaultReaction
|
||||
@ -657,6 +668,7 @@ const Message: FC<OwnProps & StateProps> = ({
|
||||
let style = '';
|
||||
let calculatedWidth;
|
||||
let reactionsMaxWidth;
|
||||
let contentWidth: number | undefined;
|
||||
let noMediaCorners = false;
|
||||
const albumLayout = useMemo(() => {
|
||||
return isAlbum
|
||||
@ -690,14 +702,17 @@ const Message: FC<OwnProps & StateProps> = ({
|
||||
}
|
||||
|
||||
if (width) {
|
||||
calculatedWidth = Math.max(getMinMediaWidth(Boolean(currentText), withCommentButton), width);
|
||||
if (width < (isMediaWidthWithCommentButton ? MIN_MEDIA_WIDTH_WITH_COMMENTS : MIN_MEDIA_WIDTH_WITH_TEXT)) {
|
||||
contentWidth = width;
|
||||
}
|
||||
calculatedWidth = Math.max(getMinMediaWidth(Boolean(currentText), isMediaWidthWithCommentButton), width);
|
||||
if (invoice?.extendedMedia && calculatedWidth - width > NO_MEDIA_CORNERS_THRESHOLD) {
|
||||
noMediaCorners = true;
|
||||
}
|
||||
}
|
||||
} else if (albumLayout) {
|
||||
calculatedWidth = Math.max(
|
||||
getMinMediaWidth(Boolean(currentText), withCommentButton), albumLayout.containerStyle.width,
|
||||
getMinMediaWidth(Boolean(currentText), isMediaWidthWithCommentButton), albumLayout.containerStyle.width,
|
||||
);
|
||||
if (calculatedWidth - albumLayout.containerStyle.width > NO_MEDIA_CORNERS_THRESHOLD) {
|
||||
noMediaCorners = true;
|
||||
@ -900,6 +915,7 @@ const Message: FC<OwnProps & StateProps> = ({
|
||||
isProtected={isProtected}
|
||||
asForwarded={asForwarded}
|
||||
theme={theme}
|
||||
forcedWidth={contentWidth}
|
||||
onClick={handleMediaClick}
|
||||
onCancelUpload={handleCancelUpload}
|
||||
/>
|
||||
@ -918,6 +934,7 @@ const Message: FC<OwnProps & StateProps> = ({
|
||||
message={message}
|
||||
observeIntersectionForLoading={observeIntersectionForLoading}
|
||||
observeIntersectionForPlaying={observeIntersectionForPlaying}
|
||||
forcedWidth={contentWidth}
|
||||
noAvatars={noAvatars}
|
||||
canAutoLoad={canAutoLoadMedia}
|
||||
canAutoPlay={canAutoPlayMedia}
|
||||
@ -1038,6 +1055,7 @@ const Message: FC<OwnProps & StateProps> = ({
|
||||
isInSelectMode={isInSelectMode}
|
||||
isSelected={isSelected}
|
||||
theme={theme}
|
||||
forcedWidth={contentWidth}
|
||||
/>
|
||||
)}
|
||||
{location && (
|
||||
@ -1410,6 +1428,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
chatTranslations,
|
||||
areTranslationsEnabled: global.settings.byKey.canTranslate,
|
||||
requestedTranslationLanguage,
|
||||
hasLinkedChat: Boolean(chat?.fullInfo?.linkedChatId),
|
||||
...((canShowSender || isLocation) && { sender }),
|
||||
...(isOutgoing && { outgoingStatus: selectOutgoingStatus(global, message, messageListType === 'scheduled') }),
|
||||
...(typeof uploadProgress === 'number' && { uploadProgress }),
|
||||
|
||||
@ -20,7 +20,7 @@ import {
|
||||
} from '../../../global/helpers';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import getCustomAppendixBg from './helpers/getCustomAppendixBg';
|
||||
import { calculateMediaDimensions } from './helpers/mediaDimensions';
|
||||
import { calculateMediaDimensions, MIN_MEDIA_HEIGHT } from './helpers/mediaDimensions';
|
||||
|
||||
import { useIsIntersecting } from '../../../hooks/useIntersectionObserver';
|
||||
import useMediaWithLoadProgress from '../../../hooks/useMediaWithLoadProgress';
|
||||
@ -44,6 +44,7 @@ export type OwnProps = {
|
||||
isInSelectMode?: boolean;
|
||||
isSelected?: boolean;
|
||||
uploadProgress?: number;
|
||||
forcedWidth?: number;
|
||||
size?: 'inline' | 'pictogram';
|
||||
shouldAffectAppendix?: boolean;
|
||||
dimensions?: IMediaDimensions & { isSmall?: boolean };
|
||||
@ -65,6 +66,7 @@ const Photo: FC<OwnProps> = ({
|
||||
isInSelectMode,
|
||||
isSelected,
|
||||
uploadProgress,
|
||||
forcedWidth,
|
||||
size = 'inline',
|
||||
dimensions,
|
||||
asForwarded,
|
||||
@ -92,9 +94,11 @@ const Photo: FC<OwnProps> = ({
|
||||
} = useMediaWithLoadProgress(getMessageMediaHash(message, size), !shouldLoad);
|
||||
const fullMediaData = localBlobUrl || mediaData;
|
||||
|
||||
const withBlurredBackground = Boolean(forcedWidth);
|
||||
const [withThumb] = useState(!fullMediaData);
|
||||
const noThumb = Boolean(fullMediaData);
|
||||
const thumbRef = useBlurredMediaThumbRef(message, noThumb);
|
||||
const blurredBackgroundRef = useBlurredMediaThumbRef(message, !withBlurredBackground);
|
||||
const thumbClassNames = useMediaTransition(!noThumb);
|
||||
const thumbDataUri = getMessageMediaThumbDataUri(message);
|
||||
|
||||
@ -170,6 +174,7 @@ const Photo: FC<OwnProps> = ({
|
||||
!isUploading && !nonInteractive && 'interactive',
|
||||
isSmall && 'small-image',
|
||||
width === height && 'square-image',
|
||||
height < MIN_MEDIA_HEIGHT && 'fix-min-height',
|
||||
);
|
||||
|
||||
const dimensionsStyle = dimensions ? ` width: ${width}px; left: ${dimensions.x}px; top: ${dimensions.y}px;` : '';
|
||||
@ -183,10 +188,12 @@ const Photo: FC<OwnProps> = ({
|
||||
style={style}
|
||||
onClick={isUploading ? undefined : handleClick}
|
||||
>
|
||||
{withBlurredBackground && <canvas ref={blurredBackgroundRef} className="thumbnail blurred-bg" />}
|
||||
<img
|
||||
src={fullMediaData}
|
||||
className="full-media"
|
||||
className={buildClassName('full-media', withBlurredBackground && 'with-blurred-bg')}
|
||||
alt=""
|
||||
style={forcedWidth ? `width: ${forcedWidth}px` : undefined}
|
||||
draggable={!isProtected}
|
||||
/>
|
||||
{withThumb && (
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import React, { useCallback, useRef, useState } from '../../../lib/teact/teact';
|
||||
import { getActions } from '../../../global';
|
||||
|
||||
import type { FC } from '../../../lib/teact/teact';
|
||||
import type { ApiMessage } from '../../../api/types';
|
||||
import type { IMediaDimensions } from './helpers/calculateAlbumLayout';
|
||||
import type { ObserveFn } from '../../../hooks/useIntersectionObserver';
|
||||
|
||||
import { MIN_MEDIA_HEIGHT } from './helpers/mediaDimensions';
|
||||
import { formatMediaDuration } from '../../../util/dateFormat';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import { calculateVideoDimensions } from '../../common/helpers/mediaDimensions';
|
||||
@ -42,6 +43,7 @@ export type OwnProps = {
|
||||
canAutoLoad?: boolean;
|
||||
canAutoPlay?: boolean;
|
||||
uploadProgress?: number;
|
||||
forcedWidth?: number;
|
||||
dimensions?: IMediaDimensions;
|
||||
asForwarded?: boolean;
|
||||
lastSyncTime?: number;
|
||||
@ -60,6 +62,7 @@ const Video: FC<OwnProps> = ({
|
||||
canAutoLoad,
|
||||
canAutoPlay,
|
||||
uploadProgress,
|
||||
forcedWidth,
|
||||
lastSyncTime,
|
||||
dimensions,
|
||||
asForwarded,
|
||||
@ -103,6 +106,7 @@ const Video: FC<OwnProps> = ({
|
||||
|
||||
const thumbDataUri = getMessageMediaThumbDataUri(message);
|
||||
const hasThumb = Boolean(thumbDataUri);
|
||||
const withBlurredBackground = Boolean(forcedWidth);
|
||||
|
||||
const previewMediaHash = getMessageMediaHash(message, 'preview');
|
||||
const [isPreviewPreloaded] = useState(Boolean(previewMediaHash && mediaLoader.getFromMemory(previewMediaHash)));
|
||||
@ -112,6 +116,7 @@ const Video: FC<OwnProps> = ({
|
||||
|
||||
const noThumb = !hasThumb || previewBlobUrl || isPlayerReady;
|
||||
const thumbRef = useBlurredMediaThumbRef(message, noThumb);
|
||||
const blurredBackgroundRef = useBlurredMediaThumbRef(message, !withBlurredBackground);
|
||||
const thumbClassNames = useMediaTransition(!noThumb);
|
||||
|
||||
const isInline = fullMediaData && wasIntersectedRef.current;
|
||||
@ -182,7 +187,11 @@ const Video: FC<OwnProps> = ({
|
||||
hideSpoiler,
|
||||
]);
|
||||
|
||||
const className = buildClassName('media-inner dark', !isUploading && 'interactive');
|
||||
const className = buildClassName(
|
||||
'media-inner dark',
|
||||
!isUploading && 'interactive',
|
||||
height < MIN_MEDIA_HEIGHT && 'fix-min-height',
|
||||
);
|
||||
|
||||
const dimensionsStyle = dimensions ? ` width: ${width}px; left: ${dimensions.x}px; top: ${dimensions.y}px;` : '';
|
||||
const style = `height: ${height}px;${dimensionsStyle}`;
|
||||
@ -195,11 +204,12 @@ const Video: FC<OwnProps> = ({
|
||||
style={style}
|
||||
onClick={isUploading ? undefined : handleClick}
|
||||
>
|
||||
{withBlurredBackground && <canvas ref={blurredBackgroundRef} className="thumbnail blurred-bg" />}
|
||||
{isInline && (
|
||||
<OptimizedVideo
|
||||
ref={videoRef}
|
||||
src={fullMediaData}
|
||||
className="full-media"
|
||||
className={buildClassName('full-media', withBlurredBackground && 'with-blurred-bg')}
|
||||
canPlay={isPlayAllowed && isIntersectingForPlaying}
|
||||
muted
|
||||
loop
|
||||
@ -207,12 +217,14 @@ const Video: FC<OwnProps> = ({
|
||||
draggable={!isProtected}
|
||||
onTimeUpdate={handleTimeUpdate}
|
||||
onReady={markPlayerReady}
|
||||
style={forcedWidth ? `width: ${forcedWidth}px` : undefined}
|
||||
/>
|
||||
)}
|
||||
<img
|
||||
src={previewBlobUrl}
|
||||
className={buildClassName('thumbnail', previewClassNames)}
|
||||
className={buildClassName('thumbnail', previewClassNames, withBlurredBackground && 'with-blurred-bg')}
|
||||
alt=""
|
||||
style={forcedWidth ? `width: ${forcedWidth}px;` : undefined}
|
||||
draggable={!isProtected}
|
||||
/>
|
||||
{hasThumb && !isPreviewPreloaded && (
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { ApiMessage } from '../../../../api/types';
|
||||
import { calculateInlineImageDimensions, calculateVideoDimensions } from '../../../common/helpers/mediaDimensions';
|
||||
import { calculateInlineImageDimensions, calculateVideoDimensions, REM } from '../../../common/helpers/mediaDimensions';
|
||||
import {
|
||||
getMessageText,
|
||||
getMessagePhoto,
|
||||
@ -9,11 +9,11 @@ import {
|
||||
getMessageWebPageVideo,
|
||||
} from '../../../../global/helpers';
|
||||
|
||||
const MIN_MEDIA_WIDTH = 100;
|
||||
const MIN_MEDIA_WIDTH_WITH_COMMENTS = 238;
|
||||
const MIN_MEDIA_WIDTH_WITH_TEXT = 175;
|
||||
const MIN_MEDIA_WIDTH_WITH_TEXT_AND_COMMENTS = 238;
|
||||
const MIN_MEDIA_HEIGHT = 90;
|
||||
export const MIN_MEDIA_WIDTH_WITH_COMMENTS = 20 * REM;
|
||||
export const MIN_MEDIA_WIDTH_WITH_TEXT = 15 * REM;
|
||||
const MIN_MEDIA_WIDTH_WITH_TEXT_AND_COMMENTS = 20 * REM;
|
||||
const MIN_MEDIA_WIDTH = 7 * REM;
|
||||
export const MIN_MEDIA_HEIGHT = 5 * REM;
|
||||
const SMALL_IMAGE_THRESHOLD = 12;
|
||||
|
||||
export function getMinMediaWidth(hasText?: boolean, hasCommentButton?: boolean) {
|
||||
|
||||
@ -5,11 +5,17 @@ import { getMessageMediaThumbDataUri } from '../../../../global/helpers';
|
||||
import useCanvasBlur from '../../../../hooks/useCanvasBlur';
|
||||
import useAppLayout from '../../../../hooks/useAppLayout';
|
||||
|
||||
export default function useBlurredMediaThumbRef(message: ApiMessage, isDisabled?: boolean | string) {
|
||||
export default function useBlurredMediaThumbRef(
|
||||
message: ApiMessage,
|
||||
isDisabled?: boolean | string,
|
||||
forcedUri?: string,
|
||||
) {
|
||||
const { isMobile } = useAppLayout();
|
||||
|
||||
const dataUri = forcedUri || getMessageMediaThumbDataUri(message);
|
||||
|
||||
return useCanvasBlur(
|
||||
getMessageMediaThumbDataUri(message),
|
||||
dataUri,
|
||||
Boolean(isDisabled),
|
||||
isMobile && !IS_CANVAS_FILTER_SUPPORTED,
|
||||
);
|
||||
|
||||
@ -14,6 +14,11 @@
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.full-media.with-blurred-bg {
|
||||
position: relative;
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
|
||||
video {
|
||||
background: no-repeat 50% 50%;
|
||||
background-size: contain;
|
||||
@ -23,11 +28,21 @@
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
&.with-blurred-bg {
|
||||
border-radius: 0 !important;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
}
|
||||
|
||||
.media-loading {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
&.fix-min-height {
|
||||
min-height: 5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.animated-close-icon {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user