fix: avatar style for incoming messages in groups

had to refactor a fair bit, but well...
pull/2940/head
Audric Ackermann 1 year ago
parent 8fa9b80fad
commit 4cbc452f26

@ -65,8 +65,7 @@ const StyledMessagesContainer = styled.div<{ isGroup: boolean }>`
padding-top: var(--margins-sm); padding-top: var(--margins-sm);
padding-right: var(--margins-lg); padding-right: var(--margins-lg);
padding-bottom: var(--margins-xl); padding-bottom: var(--margins-xl);
padding-left: ${props => padding-left: ${props => (props.isGroup ? 'var(--margins-xs)' : 'var(--margins-lg)')};
props.isGroup ? 'calc(var(--margins-lg) + 11px)' : 'var(--margins-lg)'};
.session-icon-button { .session-icon-button {
display: flex; display: flex;
@ -127,7 +126,7 @@ class SessionMessagesListContainerInner extends React.Component<Props> {
<StyledMessagesContainer <StyledMessagesContainer
className="messages-container" className="messages-container"
id={messageContainerDomID} id={messageContainerDomID}
isGroup={!conversation.isMe && !conversation.isPrivate && !conversation.isPublic} isGroup={!conversation.isPrivate}
onScroll={this.handleScroll} onScroll={this.handleScroll}
ref={this.props.messageContainerRef} ref={this.props.messageContainerRef}
data-testid="messages-container" data-testid="messages-container"

@ -5,6 +5,7 @@ import {
useAuthorName, useAuthorName,
useAuthorProfileName, useAuthorProfileName,
useFirstMessageOfSeries, useFirstMessageOfSeries,
useHideAvatarInMsgList,
useMessageAuthor, useMessageAuthor,
useMessageDirection, useMessageDirection,
} from '../../../../state/selectors'; } from '../../../../state/selectors';
@ -19,9 +20,10 @@ type Props = {
messageId: string; messageId: string;
}; };
const StyledAuthorContainer = styled(Flex)` const StyledAuthorContainer = styled(Flex)<{ hideAvatar: boolean }>`
color: var(--text-primary-color); color: var(--text-primary-color);
text-overflow: ellipsis; text-overflow: ellipsis;
margin-inline-start: ${props => (props.hideAvatar ? 0 : 'var(--width-avatar-group-msg-list)')};
`; `;
export const MessageAuthorText = (props: Props) => { export const MessageAuthorText = (props: Props) => {
@ -32,6 +34,7 @@ export const MessageAuthorText = (props: Props) => {
const sender = useMessageAuthor(props.messageId); const sender = useMessageAuthor(props.messageId);
const direction = useMessageDirection(props.messageId); const direction = useMessageDirection(props.messageId);
const firstMessageOfSeries = useFirstMessageOfSeries(props.messageId); const firstMessageOfSeries = useFirstMessageOfSeries(props.messageId);
const hideAvatar = useHideAvatarInMsgList(props.messageId);
if (!props.messageId || !sender || !direction) { if (!props.messageId || !sender || !direction) {
return null; return null;
@ -46,7 +49,7 @@ export const MessageAuthorText = (props: Props) => {
const displayedPubkey = authorProfileName ? PubKey.shorten(sender) : sender; const displayedPubkey = authorProfileName ? PubKey.shorten(sender) : sender;
return ( return (
<StyledAuthorContainer container={true}> <StyledAuthorContainer container={true} hideAvatar={hideAvatar}>
<ContactName <ContactName
pubkey={displayedPubkey} pubkey={displayedPubkey}
name={authorName} name={authorName}

@ -26,9 +26,11 @@ import { Avatar, AvatarSize, CrownIcon } from '../../../avatar/Avatar';
const StyledAvatar = styled.div` const StyledAvatar = styled.div`
position: relative; position: relative;
margin-inline-end: 20px; margin-inline-end: 10px;
padding-bottom: 6px; max-width: var(
padding-inline-end: 4px; --width-avatar-group-msg-list
); // enforcing this so we change the variable when changing the content of the avatar
overflow-y: hidden;
`; `;
export type MessageAvatarSelectorProps = Pick< export type MessageAvatarSelectorProps = Pick<
@ -129,13 +131,13 @@ export const MessageAvatar = (props: Props) => {
} }
if (!lastMessageOfSeries) { if (!lastMessageOfSeries) {
return <div style={{ marginInlineEnd: '60px' }} key={`msg-avatar-${sender}`} />; return <div style={{ marginInlineEnd: 'var(--width-avatar-group-msg-list)' }} />;
} }
/* eslint-disable @typescript-eslint/no-misused-promises */ /* eslint-disable @typescript-eslint/no-misused-promises */
// The styledAvatar, when rendered needs to have a width with margins included of var(--width-avatar-group-msg-list).
// This is so that the other message is still aligned when the avatar is not rendered (we need to make up for the space used by the avatar, and we use a margin of width-avatar-group-msg-list)
return ( return (
<StyledAvatar <StyledAvatar
key={`msg-avatar-${sender}`}
style={{ style={{
visibility: hideAvatar ? 'hidden' : undefined, visibility: hideAvatar ? 'hidden' : undefined,
}} }}

@ -7,14 +7,19 @@ import { useSelector } from 'react-redux';
import styled, { css, keyframes } from 'styled-components'; import styled, { css, keyframes } from 'styled-components';
import { MessageRenderingProps } from '../../../../models/messageType'; import { MessageRenderingProps } from '../../../../models/messageType';
import { StateType } from '../../../../state/reducer'; import { StateType } from '../../../../state/reducer';
import { useMessageIsDeleted } from '../../../../state/selectors'; import { useHideAvatarInMsgList, useMessageIsDeleted } from '../../../../state/selectors';
import { import {
getMessageContentSelectorProps, getMessageContentSelectorProps,
getQuotedMessageToAnimate, getQuotedMessageToAnimate,
getShouldHighlightMessage, getShouldHighlightMessage,
} from '../../../../state/selectors/conversations'; } from '../../../../state/selectors/conversations';
import {
useSelectedIsGroup,
useSelectedIsPrivate,
} from '../../../../state/selectors/selectedConversation';
import { ScrollToLoadedMessageContext } from '../../SessionMessagesListContainer'; import { ScrollToLoadedMessageContext } from '../../SessionMessagesListContainer';
import { MessageAttachment } from './MessageAttachment'; import { MessageAttachment } from './MessageAttachment';
import { MessageAvatar } from './MessageAvatar';
import { MessageLinkPreview } from './MessageLinkPreview'; import { MessageLinkPreview } from './MessageLinkPreview';
import { MessageQuote } from './MessageQuote'; import { MessageQuote } from './MessageQuote';
import { MessageText } from './MessageText'; import { MessageText } from './MessageText';
@ -46,7 +51,9 @@ function onClickOnMessageInnerContainer(event: React.MouseEvent<HTMLDivElement>)
} }
} }
const StyledMessageContent = styled.div``; const StyledMessageContent = styled.div`
display: flex;
`;
const opacityAnimation = keyframes` const opacityAnimation = keyframes`
0% { 0% {
@ -92,6 +99,12 @@ const StyledMessageOpaqueContent = styled(StyledMessageHighlighter)<{
export const IsMessageVisibleContext = createContext(false); export const IsMessageVisibleContext = createContext(false);
// NOTE aligns group member avatars with the ExpireTimer
const StyledAvatarContainer = styled.div<{ hideAvatar: boolean; isGroup: boolean }>`
/* margin-inline-start: ${props => (!props.hideAvatar && props.isGroup ? '-11px' : '')}; */
align-self: flex-end;
`;
export const MessageContent = (props: Props) => { export const MessageContent = (props: Props) => {
const [highlight, setHighlight] = useState(false); const [highlight, setHighlight] = useState(false);
const [didScroll, setDidScroll] = useState(false); const [didScroll, setDidScroll] = useState(false);
@ -102,6 +115,9 @@ export const MessageContent = (props: Props) => {
const [isMessageVisible, setMessageIsVisible] = useState(false); const [isMessageVisible, setMessageIsVisible] = useState(false);
const scrollToLoadedMessage = useContext(ScrollToLoadedMessageContext); const scrollToLoadedMessage = useContext(ScrollToLoadedMessageContext);
const selectedIsPrivate = useSelectedIsPrivate();
const isGroup = useSelectedIsGroup();
const hideAvatar = useHideAvatarInMsgList(props.messageId);
const [imageBroken, setImageBroken] = useState(false); const [imageBroken, setImageBroken] = useState(false);
@ -169,6 +185,14 @@ export const MessageContent = (props: Props) => {
onClick={onClickOnMessageInnerContainer} onClick={onClickOnMessageInnerContainer}
title={toolTipTitle} title={toolTipTitle}
> >
<StyledAvatarContainer hideAvatar={hideAvatar} isGroup={isGroup}>
<MessageAvatar
messageId={props.messageId}
hideAvatar={hideAvatar}
isPrivate={selectedIsPrivate}
/>
</StyledAvatarContainer>
<InView <InView
id={`inview-content-${props.messageId}`} id={`inview-content-${props.messageId}`}
onChange={onVisible} onChange={onVisible}

@ -7,6 +7,7 @@ import { MessageRenderingProps } from '../../../../models/messageType';
import { toggleSelectedMessageId } from '../../../../state/ducks/conversations'; import { toggleSelectedMessageId } from '../../../../state/ducks/conversations';
import { updateReactListModal } from '../../../../state/ducks/modalDialog'; import { updateReactListModal } from '../../../../state/ducks/modalDialog';
import { StateType } from '../../../../state/reducer'; import { StateType } from '../../../../state/reducer';
import { useHideAvatarInMsgList } from '../../../../state/selectors';
import { import {
getMessageContentWithStatusesSelectorProps, getMessageContentWithStatusesSelectorProps,
isMessageSelectionMode, isMessageSelectionMode,
@ -15,7 +16,6 @@ import { Reactions } from '../../../../util/reactions';
import { Flex } from '../../../basic/Flex'; import { Flex } from '../../../basic/Flex';
import { ExpirableReadableMessage } from '../message-item/ExpirableReadableMessage'; import { ExpirableReadableMessage } from '../message-item/ExpirableReadableMessage';
import { MessageAuthorText } from './MessageAuthorText'; import { MessageAuthorText } from './MessageAuthorText';
import { MessageAvatar } from './MessageAvatar';
import { MessageContent } from './MessageContent'; import { MessageContent } from './MessageContent';
import { MessageContextMenu } from './MessageContextMenu'; import { MessageContextMenu } from './MessageContextMenu';
import { MessageReactions, StyledMessageReactions } from './MessageReactions'; import { MessageReactions, StyledMessageReactions } from './MessageReactions';
@ -46,23 +46,19 @@ const StyledMessageContentContainer = styled.div<{ isIncoming: boolean }>`
} }
`; `;
const StyledMessageWithAuthor = styled.div<{ isIncoming: boolean }>` const StyledMessageWithAuthor = styled.div`
max-width: '100%'; max-width: '100%';
display: flex; display: flex;
flex-direction: column; flex-direction: column;
min-width: 0; min-width: 0;
`; `;
// NOTE aligns group member avatars with the ExpireTimer
const StyledAvatarContainer = styled.div<{ hideAvatar: boolean; isGroup: boolean }>`
margin-inline-start: ${props => (!props.hideAvatar && props.isGroup ? '-11px' : '')};
`;
export const MessageContentWithStatuses = (props: Props) => { export const MessageContentWithStatuses = (props: Props) => {
const contentProps = useSelector((state: StateType) => const contentProps = useSelector((state: StateType) =>
getMessageContentWithStatusesSelectorProps(state, props.messageId) getMessageContentWithStatusesSelectorProps(state, props.messageId)
); );
const dispatch = useDispatch(); const dispatch = useDispatch();
const hideAvatar = useHideAvatarInMsgList(props.messageId);
const multiSelectMode = useSelector(isMessageSelectionMode); const multiSelectMode = useSelector(isMessageSelectionMode);
@ -103,12 +99,9 @@ export const MessageContentWithStatuses = (props: Props) => {
return null; return null;
} }
const { conversationType, direction, isDeleted, isGroup } = contentProps; const { direction, isDeleted } = contentProps;
const isIncoming = direction === 'incoming'; const isIncoming = direction === 'incoming';
const isPrivate = conversationType === 'private';
const hideAvatar = isPrivate || direction === 'outgoing';
const handleMessageReaction = async (emoji: string) => { const handleMessageReaction = async (emoji: string) => {
await Reactions.sendMessageReaction(messageId, emoji); await Reactions.sendMessageReaction(messageId, emoji);
}; };
@ -132,12 +125,8 @@ export const MessageContentWithStatuses = (props: Props) => {
onDoubleClickCapture={onDoubleClickReplyToMessage} onDoubleClickCapture={onDoubleClickReplyToMessage}
dataTestId={dataTestId} dataTestId={dataTestId}
> >
<StyledAvatarContainer hideAvatar={hideAvatar} isGroup={isGroup}>
<MessageAvatar messageId={messageId} hideAvatar={hideAvatar} isPrivate={isPrivate} />
</StyledAvatarContainer>
<Flex container={true} flexDirection="column" flexShrink={0}> <Flex container={true} flexDirection="column" flexShrink={0}>
<StyledMessageWithAuthor isIncoming={isIncoming}> <StyledMessageWithAuthor>
<MessageAuthorText messageId={messageId} /> <MessageAuthorText messageId={messageId} />
<MessageContent messageId={messageId} isDetailView={isDetailView} /> <MessageContent messageId={messageId} isDetailView={isDetailView} />
</StyledMessageWithAuthor> </StyledMessageWithAuthor>

@ -8,6 +8,7 @@ import {
} from '../ducks/conversations'; } from '../ducks/conversations';
import { StateType } from '../reducer'; import { StateType } from '../reducer';
import { getMessagePropsByMessageId } from './conversations'; import { getMessagePropsByMessageId } from './conversations';
import { useSelectedIsPrivate } from './selectedConversation';
function useMessagePropsByMessageId(messageId: string | undefined) { function useMessagePropsByMessageId(messageId: string | undefined) {
return useSelector((state: StateType) => getMessagePropsByMessageId(state, messageId)); return useSelector((state: StateType) => getMessagePropsByMessageId(state, messageId));
@ -125,3 +126,9 @@ export function useMessageTimestamp(messageId: string) {
export function useMessageBody(messageId: string) { export function useMessageBody(messageId: string) {
return useMessagePropsByMessageId(messageId)?.propsForMessage.text; return useMessagePropsByMessageId(messageId)?.propsForMessage.text;
} }
export function useHideAvatarInMsgList(messageId?: string) {
const msgProps = useMessagePropsByMessageId(messageId);
const selectedIsPrivate = useSelectedIsPrivate();
return msgProps?.propsForMessage.direction === 'outgoing' || selectedIsPrivate;
}

@ -27,6 +27,7 @@ export type ThemeGlobals = {
/* Padding */ /* Padding */
'--padding-message-content': string; '--padding-message-content': string;
'--padding-link-preview': string; '--padding-link-preview': string;
'--width-avatar-group-msg-list': string;
/* Border Radius */ /* Border Radius */
'--border-radius': string; '--border-radius': string;
@ -108,6 +109,7 @@ export const THEME_GLOBALS: ThemeGlobals = {
'--padding-message-content': '7px 13px', '--padding-message-content': '7px 13px',
'--padding-link-preview': '-7px -13px 7px -13px', // bottom has positive value because a link preview has always a body below '--padding-link-preview': '-7px -13px 7px -13px', // bottom has positive value because a link preview has always a body below
'--width-avatar-group-msg-list': '46px', // the width used by the avatar (and its margins when rendered as part of a group.)
'--border-radius': '5px', '--border-radius': '5px',
'--border-radius-message-box': '16px', '--border-radius-message-box': '16px',

Loading…
Cancel
Save