Message: Make bubbles use more space on small screens (#1226)

This commit is contained in:
Alexander Zinchuk 2021-07-01 16:14:57 +03:00
parent 633e87ca8c
commit 75ffbf488b
11 changed files with 122 additions and 60 deletions

View File

@ -7,6 +7,9 @@
.content-inner & {
min-width: 14rem;
@media (max-width: 340px) {
min-width: 11rem;
}
}
display: flex;

View File

@ -11,34 +11,56 @@ export const AVATAR_FULL_DIMENSIONS = { width: 640, height: 640 };
const DEFAULT_MEDIA_DIMENSIONS: IDimensions = { width: 100, height: 100 };
export const LIKE_STICKER_ID = '1258816259753933';
const MOBILE_SCREEN_MAX_MESSAGE_SCREEN_WIDTH = 0.69;
const MOBILE_SCREEN_NO_AVATARS_MESSAGE_EXTRA_WIDTH_REM = 4.5;
const MOBILE_SCREEN_MESSAGE_EXTRA_WIDTH_REM = 7;
const MESSAGE_MAX_WIDTH_REM = 29;
const MESSAGE_OWN_MAX_WIDTH_REM = 30;
let cachedMaxWidthOwn: number | undefined;
let cachedMaxWidth: number | undefined;
let cachedMaxWidthNoAvatar: number | undefined;
function getMaxMessageWidthRem(fromOwnMessage: boolean) {
const regularMaxWidth = fromOwnMessage ? 30 : 29;
function getMaxMessageWidthRem(fromOwnMessage: boolean, noAvatars?: boolean) {
const regularMaxWidth = fromOwnMessage ? MESSAGE_OWN_MAX_WIDTH_REM : MESSAGE_MAX_WIDTH_REM;
if (!IS_SINGLE_COLUMN_LAYOUT) {
return regularMaxWidth;
}
const { width: windowWidth } = windowSize.get();
// @optimization Limitation: changing device screen width not supported
if (!cachedMaxWidthOwn) {
cachedMaxWidthOwn = Math.min(
MESSAGE_OWN_MAX_WIDTH_REM,
windowWidth / REM - MOBILE_SCREEN_NO_AVATARS_MESSAGE_EXTRA_WIDTH_REM,
);
}
if (!cachedMaxWidth) {
cachedMaxWidth = Math.min(
regularMaxWidth,
Math.floor(window.innerWidth * MOBILE_SCREEN_MAX_MESSAGE_SCREEN_WIDTH) / REM,
MESSAGE_MAX_WIDTH_REM,
windowWidth / REM - MOBILE_SCREEN_MESSAGE_EXTRA_WIDTH_REM,
);
}
if (!cachedMaxWidthNoAvatar) {
cachedMaxWidthNoAvatar = Math.min(
MESSAGE_MAX_WIDTH_REM,
windowWidth / REM - MOBILE_SCREEN_NO_AVATARS_MESSAGE_EXTRA_WIDTH_REM,
);
}
return cachedMaxWidth;
return fromOwnMessage
? cachedMaxWidthOwn
: (noAvatars ? cachedMaxWidthNoAvatar : cachedMaxWidth);
}
function getAvailableWidth(
export function getAvailableWidth(
fromOwnMessage: boolean,
isForwarded?: boolean,
isWebPagePhoto?: boolean,
noAvatars?: boolean,
) {
const extraPaddingRem = isForwarded || isWebPagePhoto ? 1.625 : 0;
const availableWidthRem = getMaxMessageWidthRem(fromOwnMessage) - extraPaddingRem;
const availableWidthRem = getMaxMessageWidthRem(fromOwnMessage, noAvatars) - extraPaddingRem;
return availableWidthRem * REM;
}
@ -61,6 +83,7 @@ function calculateDimensionsForMessageMedia({
isForwarded,
isWebPagePhoto,
isGif,
noAvatars,
}: {
width: number;
height: number;
@ -68,9 +91,10 @@ function calculateDimensionsForMessageMedia({
isForwarded?: boolean;
isWebPagePhoto?: boolean;
isGif?: boolean;
noAvatars?: boolean;
}): IDimensions {
const aspectRatio = height / width;
const availableWidth = getAvailableWidth(fromOwnMessage, isForwarded, isWebPagePhoto);
const availableWidth = getAvailableWidth(fromOwnMessage, isForwarded, isWebPagePhoto, noAvatars);
const availableHeight = getAvailableHeight(isGif, aspectRatio);
return calculateDimensions(availableWidth, availableHeight, width, height);
@ -95,6 +119,7 @@ export function calculateInlineImageDimensions(
fromOwnMessage: boolean,
isForwarded?: boolean,
isWebPagePhoto?: boolean,
noAvatars?: boolean,
) {
const { width, height } = getPhotoInlineDimensions(photo) || DEFAULT_MEDIA_DIMENSIONS;
@ -104,6 +129,7 @@ export function calculateInlineImageDimensions(
fromOwnMessage,
isForwarded,
isWebPagePhoto,
noAvatars,
});
}
@ -111,6 +137,7 @@ export function calculateVideoDimensions(
video: ApiVideo,
fromOwnMessage: boolean,
isForwarded?: boolean,
noAvatars?: boolean,
) {
const { width, height } = getVideoDimensions(video) || DEFAULT_MEDIA_DIMENSIONS;
@ -120,6 +147,7 @@ export function calculateVideoDimensions(
fromOwnMessage,
isForwarded,
isGif: video.isGif,
noAvatars,
});
}

View File

@ -508,11 +508,11 @@ const MessageList: FC<OwnProps & StateProps & DispatchProps> = ({
const isPrivate = Boolean(chatId && isChatPrivate(chatId));
const withUsers = Boolean((!isPrivate && !isChannelChat) || isChatWithSelf);
const noAvatars = Boolean(!withUsers || isChannelChat);
const className = buildClassName(
'MessageList custom-scroll',
!withUsers && 'no-avatars',
isChannelChat && 'no-avatars',
noAvatars && 'no-avatars',
!canPost && 'no-composer',
type === 'pinned' && 'type-pinned',
isSelectModeActive && 'select-mode-active',
@ -551,6 +551,7 @@ const MessageList: FC<OwnProps & StateProps & DispatchProps> = ({
observeIntersectionForMedia,
observeIntersectionForAnimatedStickers,
withUsers,
noAvatars,
anchorIdRef,
memoUnreadDividerBeforeIdRef,
threadId,
@ -577,6 +578,7 @@ function renderMessages(
observeIntersectionForMedia: ObserveFn,
observeIntersectionForAnimatedStickers: ObserveFn,
withUsers: boolean,
noAvatars: boolean,
currentAnchorIdRef: { current: string | undefined },
memoFirstUnreadIdRef: { current: number | undefined },
threadId: number,
@ -676,6 +678,7 @@ function renderMessages(
observeIntersectionForMedia={observeIntersectionForMedia}
observeIntersectionForAnimatedStickers={observeIntersectionForAnimatedStickers}
album={album}
noAvatars={noAvatars}
withAvatar={position.isLastInGroup && withUsers && !isOwn && !(message.id === threadTopMessageId)}
withSenderName={position.isFirstInGroup && withUsers && !isOwn}
threadId={threadId}

View File

@ -71,7 +71,6 @@ import renderText from '../../common/helpers/renderText';
import calculateAuthorWidth from './helpers/calculateAuthorWidth';
import { ObserveFn, useOnIntersect } from '../../../hooks/useIntersectionObserver';
import useFocusMessage from './hooks/useFocusMessage';
import useWindowSize from '../../../hooks/useWindowSize';
import useLang from '../../../hooks/useLang';
import useShowTransition from '../../../hooks/useShowTransition';
import useFlag from '../../../hooks/useFlag';
@ -112,6 +111,7 @@ type OwnProps = {
observeIntersectionForMedia: ObserveFn;
observeIntersectionForAnimatedStickers: ObserveFn;
album?: IAlbum;
noAvatars?: boolean;
withAvatar?: boolean;
withSenderName?: boolean;
threadId: number;
@ -175,6 +175,7 @@ const Message: FC<OwnProps & StateProps & DispatchProps> = ({
observeIntersectionForMedia,
observeIntersectionForAnimatedStickers,
album,
noAvatars,
withAvatar,
withSenderName,
noComments,
@ -236,8 +237,6 @@ const Message: FC<OwnProps & StateProps & DispatchProps> = ({
useOnIntersect(bottomMarkerRef, observeIntersectionForBottom);
const { width: windowWidth } = useWindowSize();
const {
isContextMenuOpen, contextMenuPosition,
handleBeforeContextMenu, handleContextMenu,
@ -277,6 +276,24 @@ const Message: FC<OwnProps & StateProps & DispatchProps> = ({
const customShape = getMessageCustomShape(message);
const textParts = renderMessageText(message, highlight, isEmojiOnlyMessage(customShape));
const isContextMenuShown = contextMenuPosition !== undefined;
const signature = (
(isChannel && message.adminTitle) || (forwardInfo && !asForwarded && forwardInfo.adminTitle) || undefined
);
const metaSafeAuthorWidth = useMemo(() => {
return signature ? calculateAuthorWidth(signature) : undefined;
}, [signature]);
const canShowActionButton = (
!(isContextMenuShown || isInSelectMode || isForwarding)
&& (!isInDocumentGroup || isLastInDocumentGroup)
);
const canForward = canShowActionButton && isChannel && !isScheduled;
const canFocus = Boolean(canShowActionButton && (
(forwardInfo && (forwardInfo.isChannelPost || (isChatWithSelf && !isOwn)) && forwardInfo.fromMessageId)
|| isPinnedList
));
const avatarPeer = forwardInfo && (isChatWithSelf || !sender) ? originSender : sender;
const senderPeer = forwardInfo ? originSender : sender;
const containerClassName = buildClassName(
'Message message-list-item',
isFirstInGroup && 'first-in-group',
@ -310,11 +327,6 @@ const Message: FC<OwnProps & StateProps & DispatchProps> = ({
forceSenderName,
hasComments: message.threadInfo && message.threadInfo.messagesCount > 0,
});
const avatarPeer = forwardInfo && (isChatWithSelf || !sender) ? originSender : sender;
const senderPeer = forwardInfo ? originSender : sender;
const signature = (
(isChannel && message.adminTitle) || (forwardInfo && !asForwarded && forwardInfo.adminTitle) || undefined
);
const withCommentButton = message.threadInfo && (!isInDocumentGroup || isLastInDocumentGroup)
&& messageListType === 'thread' && !noComments;
const withAppendix = contentClassName.includes('has-appendix');
@ -470,19 +482,19 @@ const Message: FC<OwnProps & StateProps & DispatchProps> = ({
let calculatedWidth;
let noMediaCorners = false;
const albumLayout = useMemo(() => {
return isAlbum ? calculateAlbumLayout(isOwn, Boolean(asForwarded), album!, windowWidth) : undefined;
}, [isAlbum, windowWidth, isOwn, asForwarded, album]);
return isAlbum ? calculateAlbumLayout(isOwn, Boolean(asForwarded), Boolean(noAvatars), album!) : undefined;
}, [isAlbum, isOwn, asForwarded, noAvatars, album]);
const extraPadding = asForwarded ? 28 : 0;
if (!isAlbum && (photo || video)) {
let width: number | undefined;
if (photo) {
width = calculateMediaDimensions(message).width;
width = calculateMediaDimensions(message, noAvatars).width;
} else if (video) {
if (video.isRound) {
width = ROUND_VIDEO_DIMENSIONS;
} else {
width = calculateMediaDimensions(message).width;
width = calculateMediaDimensions(message, noAvatars).width;
}
}
@ -577,6 +589,7 @@ const Message: FC<OwnProps & StateProps & DispatchProps> = ({
<Photo
message={message}
observeIntersection={observeIntersectionForMedia}
noAvatars={noAvatars}
shouldAutoLoad={shouldAutoLoadMedia}
uploadProgress={uploadProgress}
shouldAffectAppendix={hasCustomAppendix}
@ -597,6 +610,7 @@ const Message: FC<OwnProps & StateProps & DispatchProps> = ({
<Video
message={message}
observeIntersection={observeIntersectionForMedia}
noAvatars={noAvatars}
shouldAutoLoad={shouldAutoLoadMedia}
shouldAutoPlay={shouldAutoPlayMedia}
uploadProgress={uploadProgress}
@ -651,6 +665,7 @@ const Message: FC<OwnProps & StateProps & DispatchProps> = ({
<WebPage
message={message}
observeIntersection={observeIntersectionForMedia}
noAvatars={noAvatars}
shouldAutoLoad={shouldAutoLoadMedia}
onMediaClick={handleMediaClick}
onCancelMediaTransfer={handleCancelUpload}
@ -719,20 +734,6 @@ const Message: FC<OwnProps & StateProps & DispatchProps> = ({
);
}
const metaSafeAuthorWidth = useMemo(() => {
return signature ? calculateAuthorWidth(signature) : undefined;
}, [signature]);
const canShowActionButton = (
!(isContextMenuShown || isInSelectMode || isForwarding)
&& (!isInDocumentGroup || isLastInDocumentGroup)
);
const canForward = canShowActionButton && isChannel && !isScheduled;
const canFocus = canShowActionButton && (
(forwardInfo && (forwardInfo.isChannelPost || (isChatWithSelf && !isOwn)) && forwardInfo.fromMessageId)
|| isPinnedList
);
return (
<div
ref={ref}

View File

@ -28,6 +28,7 @@ export type OwnProps = {
id?: string;
message: ApiMessage;
observeIntersection?: ObserveFn;
noAvatars?: boolean;
shouldAutoLoad?: boolean;
isInSelectMode?: boolean;
isSelected?: boolean;
@ -46,6 +47,7 @@ const Photo: FC<OwnProps> = ({
id,
message,
observeIntersection,
noAvatars,
shouldAutoLoad,
isInSelectMode,
isSelected,
@ -115,7 +117,7 @@ const Photo: FC<OwnProps> = ({
}
}, [fullMediaData, isOwn, shouldAffectAppendix, isInSelectMode, isSelected]);
const { width, height, isSmall } = dimensions || calculateMediaDimensions(message);
const { width, height, isSmall } = dimensions || calculateMediaDimensions(message, noAvatars);
const className = buildClassName(
'media-inner',

View File

@ -34,6 +34,7 @@ export type OwnProps = {
id?: string;
message: ApiMessage;
observeIntersection: ObserveFn;
noAvatars?: boolean;
shouldAutoLoad?: boolean;
shouldAutoPlay?: boolean;
uploadProgress?: number;
@ -47,6 +48,7 @@ const Video: FC<OwnProps> = ({
id,
message,
observeIntersection,
noAvatars,
shouldAutoLoad,
shouldAutoPlay,
uploadProgress,
@ -107,7 +109,7 @@ const Video: FC<OwnProps> = ({
const isOwn = isOwnMessage(message);
const isForwarded = isForwardedMessage(message);
const { width, height } = dimensions || calculateVideoDimensions(video, isOwn, isForwarded);
const { width, height } = dimensions || calculateVideoDimensions(video, isOwn, isForwarded, noAvatars);
useHeavyAnimationCheckForVideo(videoRef, Boolean(isInline && shouldAutoPlay));

View File

@ -84,6 +84,7 @@
.site-description,
.site-title {
word-break: break-word;
max-width: 100%;
}
.site-name {

View File

@ -19,6 +19,7 @@ const MAX_TEXT_LENGTH = 170; // symbols
type OwnProps = {
message: ApiMessage;
observeIntersection?: ObserveFn;
noAvatars?: boolean;
shouldAutoLoad?: boolean;
inPreview?: boolean;
onMediaClick?: () => void;
@ -28,6 +29,7 @@ type OwnProps = {
const WebPage: FC<OwnProps> = ({
message,
observeIntersection,
noAvatars,
shouldAutoLoad,
inPreview,
onMediaClick,
@ -78,6 +80,7 @@ const WebPage: FC<OwnProps> = ({
<Photo
message={message}
observeIntersection={observeIntersection}
noAvatars={noAvatars}
shouldAutoLoad={shouldAutoLoad}
size={isSquarePhoto ? 'pictogram' : 'inline'}
nonInteractive={!isMediaInteractive}

View File

@ -3,7 +3,28 @@
max-width: var(--max-width);
@media (max-width: 600px) {
max-width: 69vw;
max-width: calc(100vw - 7rem);
// Workaround for sass function override - we should use CSS min() here
@supports (max-width: #{"min(29rem, 100vw - 7rem)"}) {
max-width: #{"min(29rem, 100vw - 7rem)"};
}
.MessageList.no-avatars & {
max-width: calc(100vw - 4.5rem);
@supports (max-width: #{"min(29rem, 100vw - 4.5rem)"}) {
max-width: #{"min(29rem, 100vw - 4.5rem)"};
}
}
.Message.own & {
max-width: calc(100vw - 4.5rem);
@supports (max-width: #{"min(30rem, 100vw - 4.5rem)"}) {
max-width: #{"min(30rem, 100vw - 4.5rem)"};
}
}
}
.text-content {
@ -437,6 +458,12 @@
@media (max-width: 600px) {
max-width: calc(90vw - 13rem);
}
@media (max-width: 340px) {
margin-left: -1rem;
z-index: 1;
max-width: calc(90vw - 12rem);
}
}
}
@ -548,6 +575,12 @@
.EmbeddedMessage {
margin-right: 0.5rem;
@media (max-width: 340px) {
margin-left: 0;
z-index: 1;
max-width: calc(90vw - 11.5rem);
}
}
}
}

View File

@ -7,13 +7,9 @@ import { IAlbum } from '../../../../types';
import { ApiMessage } from '../../../../api/types';
import { IDimensions } from '../../../../modules/helpers';
import { MOBILE_SCREEN_MAX_WIDTH } from '../../../../config';
import { REM } from '../../../common/helpers/mediaDimensions';
import { getAvailableWidth, REM } from '../../../common/helpers/mediaDimensions';
import { calculateMediaDimensions } from './mediaDimensions';
const MAX_WIDTH_MOBILE_VW = 69;
const MAX_WIDTH_DESK_OWN_REM = 30;
const MAX_WIDTH_DESK_REM = 29;
export const AlbumRectPart = {
None: 0,
Top: 1,
@ -50,16 +46,6 @@ export type IAlbumLayout = {
containerStyle: IDimensions;
};
function getMaxWidth(isOwn: boolean, isForwarded: boolean, windowWidth: number) {
if (windowWidth <= MOBILE_SCREEN_MAX_WIDTH) {
return (windowWidth / 100) * MAX_WIDTH_MOBILE_VW - (isForwarded ? 1.625 : 0) * REM;
}
const maxWidth = isOwn ? MAX_WIDTH_DESK_OWN_REM : MAX_WIDTH_DESK_REM;
return (maxWidth - (isForwarded ? 1.625 : 0)) * REM;
}
function getRatios(messages: ApiMessage[]) {
return messages.map(
(message) => {
@ -110,8 +96,8 @@ function calculateContainerSize(layout: IMediaLayout[]) {
export function calculateAlbumLayout(
isOwn: boolean,
isForwarded: boolean,
noAvatars: boolean,
album: IAlbum,
windowWidth: number,
): IAlbumLayout {
const spacing = 2;
const ratios = getRatios(album.messages);
@ -119,7 +105,7 @@ export function calculateAlbumLayout(
const averageRatio = getAverageRatio(ratios);
const albumCount = ratios.length;
const forceCalc = ratios.some((ratio) => ratio > 2);
const maxWidth = getMaxWidth(isOwn, isForwarded, windowWidth);
const maxWidth = getAvailableWidth(isOwn, isForwarded, false, noAvatars) - (isForwarded ? 2.5 : 0) * REM;
const maxHeight = maxWidth;
let layout;

View File

@ -21,7 +21,7 @@ export function getMinMediaWidth(hasText?: boolean, hasCommentButton?: boolean)
: MIN_MEDIA_WIDTH;
}
export function calculateMediaDimensions(message: ApiMessage) {
export function calculateMediaDimensions(message: ApiMessage, noAvatars?: boolean) {
const isOwn = isOwnMessage(message);
const isForwarded = isForwardedMessage(message);
const photo = getMessagePhoto(message) || getMessageWebPagePhoto(message);
@ -29,8 +29,8 @@ export function calculateMediaDimensions(message: ApiMessage) {
const isWebPagePhoto = Boolean(getMessageWebPagePhoto(message));
const { width, height } = photo
? calculateInlineImageDimensions(photo, isOwn, isForwarded, isWebPagePhoto)
: calculateVideoDimensions(video!, isOwn, isForwarded);
? calculateInlineImageDimensions(photo, isOwn, isForwarded, isWebPagePhoto, noAvatars)
: calculateVideoDimensions(video!, isOwn, isForwarded, noAvatars);
const hasText = Boolean(getMessageText(message));
const minMediaWidth = getMinMediaWidth(hasText);