Merge pull request #3055 from Bilb/fix/ses-1514/message-info-avatar-overflow

Fix/ses 1514/message info avatar overflow
pull/3060/head
Audric Ackermann 1 year ago committed by GitHub
commit 1c71c8fb67
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -210,7 +210,6 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
margin-right: -20px; // offsets the edit icon button so it's centered
p { p {
font-size: $session-font-md; font-size: $session-font-md;

@ -609,6 +609,9 @@
flex-direction: column; flex-direction: column;
align-items: stretch; align-items: stretch;
overflow: hidden; overflow: hidden;
max-height: 100%;
display: flex;
gap: 5px;
.session-icon-button:first-child { .session-icon-button:first-child {
margin-right: var(--margins-sm); margin-right: var(--margins-sm);

@ -1,12 +1,11 @@
import React from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import { CSSProperties } from 'styled-components'; import React from 'react';
import { Emojify } from './Emojify';
import { import {
useNicknameOrProfileNameOrShortenedPubkey,
useIsPrivate, useIsPrivate,
useNicknameOrProfileNameOrShortenedPubkey,
} from '../../hooks/useParamSelector'; } from '../../hooks/useParamSelector';
import { Emojify } from './Emojify';
type Props = { type Props = {
pubkey: string; pubkey: string;
@ -25,12 +24,20 @@ export const ContactName = (props: Props) => {
const convoName = useNicknameOrProfileNameOrShortenedPubkey(pubkey); const convoName = useNicknameOrProfileNameOrShortenedPubkey(pubkey);
const isPrivate = useIsPrivate(pubkey); const isPrivate = useIsPrivate(pubkey);
const shouldShowProfile = Boolean(convoName || profileName || name); const shouldShowProfile = Boolean(convoName || profileName || name);
const commonStyles = {
'min-width': 0,
'text-overflow': 'ellipsis',
overflow: 'hidden',
} as React.CSSProperties;
const styles = ( const styles = (
boldProfileName boldProfileName
? { ? {
fontWeight: 'bold', fontWeight: 'bold',
...commonStyles,
} }
: {} : commonStyles
) as React.CSSProperties; ) as React.CSSProperties;
const textProfile = profileName || name || convoName || window.i18n('anonymous'); const textProfile = profileName || name || convoName || window.i18n('anonymous');
@ -39,15 +46,19 @@ export const ContactName = (props: Props) => {
className={classNames(prefix, compact && 'compact')} className={classNames(prefix, compact && 'compact')}
dir="auto" dir="auto"
data-testid={`${prefix}__profile-name`} data-testid={`${prefix}__profile-name`}
style={{ textOverflow: 'inherit' }} style={{
textOverflow: 'inherit',
display: 'flex',
flexDirection: 'row',
gap: 'var(--margins-xs)',
}}
> >
{shouldShowProfile ? ( {shouldShowProfile ? (
<span style={styles as CSSProperties} className={`${prefix}__profile-name`}> <div style={styles} className={`${prefix}__profile-name`}>
<Emojify text={textProfile} sizeClass="small" isGroup={!isPrivate} /> <Emojify text={textProfile} sizeClass="small" isGroup={!isPrivate} />
</span> </div>
) : null} ) : null}
{shouldShowProfile ? ' ' : null} {shouldShowPubkey ? <div className={`${prefix}__profile-number`}>{pubkey}</div> : null}
{shouldShowPubkey ? <span className={`${prefix}__profile-number`}>{pubkey}</span> : null}
</span> </span>
); );
}; };

@ -1,4 +1,4 @@
import React, { useContext } from 'react'; import React from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import { import {
@ -10,10 +10,10 @@ import {
isVideoAttachment, isVideoAttachment,
} from '../../types/Attachment'; } from '../../types/Attachment';
import { useIsMessageVisible } from '../../contexts/isMessageVisibleContext';
import { useMessageSelected } from '../../state/selectors'; import { useMessageSelected } from '../../state/selectors';
import { THUMBNAIL_SIDE } from '../../types/attachments/VisualAttachment'; import { THUMBNAIL_SIDE } from '../../types/attachments/VisualAttachment';
import { Image } from './Image'; import { Image } from './Image';
import { IsMessageVisibleContext } from './message/message-content/MessageContent';
type Props = { type Props = {
attachments: Array<AttachmentTypeWithPath>; attachments: Array<AttachmentTypeWithPath>;
@ -46,7 +46,7 @@ const Row = (
totalAttachmentsCount, totalAttachmentsCount,
selected, selected,
} = props; } = props;
const isMessageVisible = useContext(IsMessageVisibleContext); const isMessageVisible = useIsMessageVisible();
const moreMessagesOverlay = totalAttachmentsCount > 3; const moreMessagesOverlay = totalAttachmentsCount > 3;
const moreMessagesOverlayText = moreMessagesOverlay ? `+${totalAttachmentsCount - 3}` : undefined; const moreMessagesOverlayText = moreMessagesOverlay ? `+${totalAttachmentsCount - 3}` : undefined;

@ -1,9 +1,9 @@
import React, { useContext, useLayoutEffect } from 'react'; import React, { useLayoutEffect } from 'react';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import styled from 'styled-components'; import styled from 'styled-components';
import { useScrollToLoadedMessage } from '../../contexts/ScrollToLoadedMessage';
import { getQuotedMessageToAnimate } from '../../state/selectors/conversations'; import { getQuotedMessageToAnimate } from '../../state/selectors/conversations';
import { isDarkTheme } from '../../state/selectors/theme'; import { isDarkTheme } from '../../state/selectors/theme';
import { ScrollToLoadedMessageContext } from './SessionMessagesListContainer';
const LastSeenBar = styled.div` const LastSeenBar = styled.div`
height: 2px; height: 2px;
@ -52,7 +52,7 @@ export const SessionLastSeenIndicator = (props: {
const darkMode = useSelector(isDarkTheme); const darkMode = useSelector(isDarkTheme);
// if this unread-indicator is not unique it's going to cause issues // if this unread-indicator is not unique it's going to cause issues
const quotedMessageToAnimate = useSelector(getQuotedMessageToAnimate); const quotedMessageToAnimate = useSelector(getQuotedMessageToAnimate);
const scrollToLoadedMessage = useContext(ScrollToLoadedMessageContext); const scrollToLoadedMessage = useScrollToLoadedMessage();
const { messageId, didScroll, setDidScroll } = props; const { messageId, didScroll, setDidScroll } = props;

@ -26,9 +26,10 @@ import { Message } from './message/message-item/Message';
import { MessageRequestResponse } from './message/message-item/MessageRequestResponse'; import { MessageRequestResponse } from './message/message-item/MessageRequestResponse';
import { CallNotification } from './message/message-item/notification-bubble/CallNotification'; import { CallNotification } from './message/message-item/notification-bubble/CallNotification';
import { DataExtractionNotification } from './message/message-item/DataExtractionNotification'; import { IsDetailMessageViewContext } from '../../contexts/isDetailViewContext';
import { SessionLastSeenIndicator } from './SessionLastSeenIndicator'; import { SessionLastSeenIndicator } from './SessionLastSeenIndicator';
import { TimerNotification } from './TimerNotification'; import { TimerNotification } from './TimerNotification';
import { DataExtractionNotification } from './message/message-item/DataExtractionNotification';
import { InteractionNotification } from './message/message-item/InteractionNotification'; import { InteractionNotification } from './message/message-item/InteractionNotification';
function isNotTextboxEvent(e: KeyboardEvent) { function isNotTextboxEvent(e: KeyboardEvent) {
@ -98,7 +99,7 @@ export const SessionMessagesList = (props: {
} }
return ( return (
<> <IsDetailMessageViewContext.Provider value={false}>
{messagesProps.map(messageProps => { {messagesProps.map(messageProps => {
const messageId = messageProps.message.props.messageId; const messageId = messageProps.message.props.messageId;
const unreadIndicator = messageProps.showUnreadIndicator ? ( const unreadIndicator = messageProps.showUnreadIndicator ? (
@ -170,6 +171,6 @@ export const SessionMessagesList = (props: {
return [<Message messageId={messageId} key={messageId} />, ...componentToMerge]; return [<Message messageId={messageId} key={messageId} />, ...componentToMerge];
})} })}
</> </IsDetailMessageViewContext.Provider>
); );
}; };

@ -15,6 +15,10 @@ import {
} from '../../state/ducks/conversations'; } from '../../state/ducks/conversations';
import { SessionScrollButton } from '../SessionScrollButton'; import { SessionScrollButton } from '../SessionScrollButton';
import {
ScrollToLoadedMessageContext,
ScrollToLoadedReasons,
} from '../../contexts/ScrollToLoadedMessage';
import { StateType } from '../../state/reducer'; import { StateType } from '../../state/reducer';
import { import {
getQuotedMessageToAnimate, getQuotedMessageToAnimate,
@ -31,17 +35,6 @@ export type SessionMessageListProps = {
}; };
export const messageContainerDomID = 'messages-container'; export const messageContainerDomID = 'messages-container';
export type ScrollToLoadedReasons =
| 'quote-or-search-result'
| 'go-to-bottom'
| 'unread-indicator'
| 'load-more-top'
| 'load-more-bottom';
export const ScrollToLoadedMessageContext = React.createContext(
(_loadedMessageIdToScrollTo: string, _reason: ScrollToLoadedReasons) => {}
);
type Props = SessionMessageListProps & { type Props = SessionMessageListProps & {
conversationKey?: string; conversationKey?: string;
messagesProps: Array<SortedMessageModelProps>; messagesProps: Array<SortedMessageModelProps>;

@ -38,10 +38,10 @@ export type MessageAvatarSelectorProps = Pick<
'sender' | 'isSenderAdmin' | 'lastMessageOfSeries' 'sender' | 'isSenderAdmin' | 'lastMessageOfSeries'
>; >;
type Props = { messageId: string; hideAvatar: boolean; isPrivate: boolean; isDetailView?: boolean }; type Props = { messageId: string; isPrivate: boolean };
export const MessageAvatar = (props: Props) => { export const MessageAvatar = (props: Props) => {
const { messageId, hideAvatar, isPrivate, isDetailView } = props; const { messageId, isPrivate } = props;
const dispatch = useDispatch(); const dispatch = useDispatch();
const selectedConvoKey = useSelectedConversationKey(); const selectedConvoKey = useSelectedConversationKey();
@ -137,13 +137,9 @@ export const MessageAvatar = (props: Props) => {
// The styledAvatar, when rendered needs to have a width with margins included of var(--width-avatar-group-msg-list). // 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) // 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>
style={{
visibility: hideAvatar ? 'hidden' : undefined,
}}
>
<Avatar size={AvatarSize.S} onAvatarClick={onMessageAvatarClick} pubkey={sender} /> <Avatar size={AvatarSize.S} onAvatarClick={onMessageAvatarClick} pubkey={sender} />
{!isDetailView && isSenderAdmin ? <CrownIcon /> : null} {isSenderAdmin ? <CrownIcon /> : null}
</StyledAvatar> </StyledAvatar>
); );
}; };

@ -1,10 +1,13 @@
import classNames from 'classnames'; import classNames from 'classnames';
import { isEmpty } from 'lodash'; import { isEmpty } from 'lodash';
import moment from 'moment'; import moment from 'moment';
import React, { createContext, useCallback, useContext, useLayoutEffect, useState } from 'react'; import React, { useCallback, useLayoutEffect, useState } from 'react';
import { InView } from 'react-intersection-observer'; import { InView } from 'react-intersection-observer';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import styled from 'styled-components'; import styled from 'styled-components';
import { useScrollToLoadedMessage } from '../../../../contexts/ScrollToLoadedMessage';
import { useIsDetailMessageView } from '../../../../contexts/isDetailViewContext';
import { IsMessageVisibleContext } from '../../../../contexts/isMessageVisibleContext';
import { MessageModelType, MessageRenderingProps } from '../../../../models/messageType'; import { MessageModelType, MessageRenderingProps } from '../../../../models/messageType';
import { StateType } from '../../../../state/reducer'; import { StateType } from '../../../../state/reducer';
import { import {
@ -19,7 +22,6 @@ import {
} from '../../../../state/selectors/conversations'; } from '../../../../state/selectors/conversations';
import { useSelectedIsPrivate } from '../../../../state/selectors/selectedConversation'; import { useSelectedIsPrivate } from '../../../../state/selectors/selectedConversation';
import { canDisplayImage } from '../../../../types/Attachment'; import { canDisplayImage } from '../../../../types/Attachment';
import { ScrollToLoadedMessageContext } from '../../SessionMessagesListContainer';
import { MessageAttachment } from './MessageAttachment'; import { MessageAttachment } from './MessageAttachment';
import { MessageAvatar } from './MessageAvatar'; import { MessageAvatar } from './MessageAvatar';
import { MessageHighlighter } from './MessageHighlighter'; import { MessageHighlighter } from './MessageHighlighter';
@ -34,7 +36,6 @@ export type MessageContentSelectorProps = Pick<
type Props = { type Props = {
messageId: string; messageId: string;
isDetailView?: boolean;
}; };
// TODO not too sure what is this doing? It is not preventDefault() // TODO not too sure what is this doing? It is not preventDefault()
@ -76,13 +77,13 @@ const StyledMessageOpaqueContent = styled(MessageHighlighter)<{
${props => props.selected && `box-shadow: var(--drop-shadow);`} ${props => props.selected && `box-shadow: var(--drop-shadow);`}
`; `;
export const IsMessageVisibleContext = createContext(false);
const StyledAvatarContainer = styled.div` const StyledAvatarContainer = styled.div`
align-self: flex-end; align-self: flex-end;
`; `;
export const MessageContent = (props: Props) => { export const MessageContent = (props: Props) => {
const isDetailView = useIsDetailMessageView();
const [highlight, setHighlight] = useState(false); const [highlight, setHighlight] = useState(false);
const [didScroll, setDidScroll] = useState(false); const [didScroll, setDidScroll] = useState(false);
const contentProps = useSelector((state: StateType) => const contentProps = useSelector((state: StateType) =>
@ -91,9 +92,9 @@ export const MessageContent = (props: Props) => {
const isDeleted = useMessageIsDeleted(props.messageId); const isDeleted = useMessageIsDeleted(props.messageId);
const [isMessageVisible, setMessageIsVisible] = useState(false); const [isMessageVisible, setMessageIsVisible] = useState(false);
const scrollToLoadedMessage = useContext(ScrollToLoadedMessageContext); const scrollToLoadedMessage = useScrollToLoadedMessage();
const selectedIsPrivate = useSelectedIsPrivate(); const selectedIsPrivate = useSelectedIsPrivate();
const hideAvatar = useHideAvatarInMsgList(props.messageId); const hideAvatar = useHideAvatarInMsgList(props.messageId, isDetailView);
const [imageBroken, setImageBroken] = useState(false); const [imageBroken, setImageBroken] = useState(false);
@ -153,8 +154,7 @@ export const MessageContent = (props: Props) => {
const toolTipTitle = moment(serverTimestamp || timestamp).format('llll'); const toolTipTitle = moment(serverTimestamp || timestamp).format('llll');
const isDetailViewAndSupportsAttachmentCarousel = const isDetailViewAndSupportsAttachmentCarousel = isDetailView && canDisplayImage(attachments);
props.isDetailView && canDisplayImage(attachments);
return ( return (
<StyledMessageContent <StyledMessageContent
@ -164,14 +164,11 @@ export const MessageContent = (props: Props) => {
title={toolTipTitle} title={toolTipTitle}
msgDirection={direction} msgDirection={direction}
> >
{hideAvatar ? null : (
<StyledAvatarContainer> <StyledAvatarContainer>
<MessageAvatar <MessageAvatar messageId={props.messageId} isPrivate={selectedIsPrivate} />
messageId={props.messageId}
hideAvatar={hideAvatar}
isPrivate={selectedIsPrivate}
isDetailView={props.isDetailView}
/>
</StyledAvatarContainer> </StyledAvatarContainer>
)}
<InView <InView
id={`inview-content-${props.messageId}`} id={`inview-content-${props.messageId}`}
@ -184,6 +181,7 @@ export const MessageContent = (props: Props) => {
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
gap: 'var(--margins-xs)', gap: 'var(--margins-xs)',
maxWidth: '100%',
}} }}
> >
<IsMessageVisibleContext.Provider value={isMessageVisible}> <IsMessageVisibleContext.Provider value={isMessageVisible}>

@ -2,6 +2,7 @@ import classNames from 'classnames';
import React, { useCallback, useState } from 'react'; import React, { useCallback, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import styled from 'styled-components'; import styled from 'styled-components';
import { useIsDetailMessageView } from '../../../../contexts/isDetailViewContext';
import { replyToMessage } from '../../../../interactions/conversationInteractions'; import { replyToMessage } from '../../../../interactions/conversationInteractions';
import { MessageRenderingProps } from '../../../../models/messageType'; import { MessageRenderingProps } from '../../../../models/messageType';
import { toggleSelectedMessageId } from '../../../../state/ducks/conversations'; import { toggleSelectedMessageId } from '../../../../state/ducks/conversations';
@ -29,30 +30,33 @@ export type MessageContentWithStatusSelectorProps = { isGroup: boolean } & Pick<
type Props = { type Props = {
messageId: string; messageId: string;
ctxMenuID: string; ctxMenuID: string;
isDetailView?: boolean;
dataTestId: string; dataTestId: string;
enableReactions: boolean; enableReactions: boolean;
}; };
const StyledMessageContentContainer = styled.div<{ isIncoming: boolean }>` const StyledMessageContentContainer = styled.div<{ isIncoming: boolean; isDetailView: boolean }>`
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: flex-start; justify-content: flex-start;
align-items: ${props => (props.isIncoming ? 'flex-start' : 'flex-end')}; align-items: ${props => (props.isIncoming ? 'flex-start' : 'flex-end')};
padding-left: ${props => (props.isIncoming ? 0 : '25%')}; padding-left: ${props => (props.isDetailView || props.isIncoming ? 0 : '25%')};
padding-right: ${props => (props.isIncoming ? '25%' : 0)}; padding-right: ${props => (props.isDetailView || !props.isIncoming ? 0 : '25%')};
width: 100%; width: 100%;
max-width: '100%';
margin-right: var(--margins-md); margin-right: var(--margins-md);
`; `;
const StyledMessageWithAuthor = styled.div` 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;
gap: var(--margins-xs);
`; `;
export const MessageContentWithStatuses = (props: Props) => { export const MessageContentWithStatuses = (props: Props) => {
const isDetailView = useIsDetailMessageView();
const contentProps = useSelector((state: StateType) => const contentProps = useSelector((state: StateType) =>
getMessageContentWithStatusesSelectorProps(state, props.messageId) getMessageContentWithStatusesSelectorProps(state, props.messageId)
); );
@ -91,7 +95,7 @@ export const MessageContentWithStatuses = (props: Props) => {
} }
}; };
const { messageId, ctxMenuID, isDetailView = false, dataTestId, enableReactions } = props; const { messageId, ctxMenuID, dataTestId, enableReactions } = props;
const [popupReaction, setPopupReaction] = useState(''); const [popupReaction, setPopupReaction] = useState('');
if (!contentProps) { if (!contentProps) {
@ -119,6 +123,7 @@ export const MessageContentWithStatuses = (props: Props) => {
return ( return (
<StyledMessageContentContainer <StyledMessageContentContainer
isIncoming={isIncoming} isIncoming={isIncoming}
isDetailView={isDetailView}
onMouseLeave={() => { onMouseLeave={() => {
setPopupReaction(''); setPopupReaction('');
}} }}
@ -127,21 +132,22 @@ export const MessageContentWithStatuses = (props: Props) => {
messageId={messageId} messageId={messageId}
className={classNames('module-message', `module-message--${direction}`)} className={classNames('module-message', `module-message--${direction}`)}
role={'button'} role={'button'}
isDetailView={isDetailView}
onClick={onClickOnMessageOuterContainer} onClick={onClickOnMessageOuterContainer}
onDoubleClickCapture={onDoubleClickReplyToMessage} onDoubleClickCapture={onDoubleClickReplyToMessage}
dataTestId={dataTestId} dataTestId={dataTestId}
> >
<Flex container={true} flexDirection="column" flexShrink={0} alignItems="flex-end"> <Flex
container={true}
flexDirection="column"
flexShrink={0}
alignItems="flex-end"
maxWidth="100%"
>
<StyledMessageWithAuthor> <StyledMessageWithAuthor>
{!isDetailView && <MessageAuthorText messageId={messageId} />} {!isDetailView && <MessageAuthorText messageId={messageId} />}
<MessageContent messageId={messageId} isDetailView={isDetailView} /> <MessageContent messageId={messageId} />
</StyledMessageWithAuthor> </StyledMessageWithAuthor>
<MessageStatus <MessageStatus dataTestId="msg-status" messageId={messageId} />
dataTestId="msg-status"
messageId={messageId}
isDetailView={isDetailView}
/>
</Flex> </Flex>
{!isDeleted && ( {!isDeleted && (
<MessageContextMenu <MessageContextMenu
@ -160,7 +166,6 @@ export const MessageContentWithStatuses = (props: Props) => {
setPopupReaction={setPopupReaction} setPopupReaction={setPopupReaction}
onPopupClick={handlePopupClick} onPopupClick={handlePopupClick}
noAvatar={hideAvatar} noAvatar={hideAvatar}
isDetailView={isDetailView}
/> />
) : null} ) : null}
</StyledMessageContentContainer> </StyledMessageContentContainer>

@ -1,6 +1,7 @@
import { isEmpty, toNumber } from 'lodash'; import { isEmpty, toNumber } from 'lodash';
import React from 'react'; import React from 'react';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { useIsDetailMessageView } from '../../../../contexts/isDetailViewContext';
import { Data } from '../../../../data/data'; import { Data } from '../../../../data/data';
import { MessageRenderingProps } from '../../../../models/messageType'; import { MessageRenderingProps } from '../../../../models/messageType';
import { ToastUtils } from '../../../../session/utils'; import { ToastUtils } from '../../../../session/utils';
@ -19,6 +20,7 @@ export type MessageQuoteSelectorProps = Pick<MessageRenderingProps, 'quote' | 'd
export const MessageQuote = (props: Props) => { export const MessageQuote = (props: Props) => {
const selected = useSelector((state: StateType) => getMessageQuoteProps(state, props.messageId)); const selected = useSelector((state: StateType) => getMessageQuoteProps(state, props.messageId));
const direction = useMessageDirection(props.messageId); const direction = useMessageDirection(props.messageId);
const isMessageDetailView = useIsDetailMessageView();
if (!selected || isEmpty(selected)) { if (!selected || isEmpty(selected)) {
return null; return null;
@ -38,6 +40,10 @@ export const MessageQuote = (props: Props) => {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
if (isMessageDetailView) {
return;
}
if (!quote) { if (!quote) {
ToastUtils.pushOriginalNotFound(); ToastUtils.pushOriginalNotFound();
window.log.warn('onQuoteClick: quote not valid'); window.log.warn('onQuoteClick: quote not valid');

@ -1,6 +1,7 @@
import { isEmpty, isEqual } from 'lodash'; import { isEmpty, isEqual } from 'lodash';
import React, { ReactElement, useEffect, useState } from 'react'; import React, { ReactElement, useEffect, useState } from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import { useIsDetailMessageView } from '../../../../contexts/isDetailViewContext';
import { useMessageReactsPropsById } from '../../../../hooks/useParamSelector'; import { useMessageReactsPropsById } from '../../../../hooks/useParamSelector';
import { MessageRenderingProps } from '../../../../models/messageType'; import { MessageRenderingProps } from '../../../../models/messageType';
import { REACT_LIMIT } from '../../../../session/constants'; import { REACT_LIMIT } from '../../../../session/constants';
@ -147,10 +148,11 @@ type Props = {
inModal?: boolean; inModal?: boolean;
onSelected?: (emoji: string) => boolean; onSelected?: (emoji: string) => boolean;
noAvatar: boolean; noAvatar: boolean;
isDetailView?: boolean;
}; };
export const MessageReactions = (props: Props) => { export const MessageReactions = (props: Props) => {
const isDetailView = useIsDetailMessageView();
const { const {
messageId, messageId,
hasReactLimit = true, hasReactLimit = true,
@ -161,7 +163,6 @@ export const MessageReactions = (props: Props) => {
inModal = false, inModal = false,
onSelected, onSelected,
noAvatar, noAvatar,
isDetailView,
} = props; } = props;
const [reactions, setReactions] = useState<SortedReactionList>([]); const [reactions, setReactions] = useState<SortedReactionList>([]);

@ -5,6 +5,7 @@ import styled from 'styled-components';
import { useMessageExpirationPropsById } from '../../../../hooks/useParamSelector'; import { useMessageExpirationPropsById } from '../../../../hooks/useParamSelector';
import { useMessageStatus } from '../../../../state/selectors'; import { useMessageStatus } from '../../../../state/selectors';
import { useIsDetailMessageView } from '../../../../contexts/isDetailViewContext';
import { getMostRecentMessageId } from '../../../../state/selectors/conversations'; import { getMostRecentMessageId } from '../../../../state/selectors/conversations';
import { useSelectedIsGroupOrCommunity } from '../../../../state/selectors/selectedConversation'; import { useSelectedIsGroupOrCommunity } from '../../../../state/selectors/selectedConversation';
import { SpacerXS } from '../../../basic/Text'; import { SpacerXS } from '../../../basic/Text';
@ -12,7 +13,6 @@ import { SessionIcon, SessionIconType } from '../../../icon';
import { ExpireTimer } from '../../ExpireTimer'; import { ExpireTimer } from '../../ExpireTimer';
type Props = { type Props = {
isDetailView: boolean;
messageId: string; messageId: string;
dataTestId?: string | undefined; dataTestId?: string | undefined;
}; };
@ -30,7 +30,9 @@ type Props = {
* - if the message is incoming: do not show anything (3) * - if the message is incoming: do not show anything (3)
* - if the message is outgoing: show the text for the last message, or a message sending, or in the error state. (4) * - if the message is outgoing: show the text for the last message, or a message sending, or in the error state. (4)
*/ */
export const MessageStatus = ({ isDetailView, messageId, dataTestId }: Props) => { export const MessageStatus = ({ messageId, dataTestId }: Props) => {
const isDetailView = useIsDetailMessageView();
const status = useMessageStatus(messageId); const status = useMessageStatus(messageId);
const selected = useMessageExpirationPropsById(messageId); const selected = useMessageExpirationPropsById(messageId);

@ -3,10 +3,10 @@ import React, { MouseEvent, useState } from 'react';
import { isEmpty } from 'lodash'; import { isEmpty } from 'lodash';
import styled from 'styled-components'; import styled from 'styled-components';
import { useIsMessageSelectionMode } from '../../../../../state/selectors/selectedConversation'; import { useIsMessageSelectionMode } from '../../../../../state/selectors/selectedConversation';
import * as MIME from '../../../../../types/MIME';
import { QuoteAuthor } from './QuoteAuthor'; import { QuoteAuthor } from './QuoteAuthor';
import { QuoteIconContainer } from './QuoteIconContainer'; import { QuoteIconContainer } from './QuoteIconContainer';
import { QuoteText } from './QuoteText'; import { QuoteText } from './QuoteText';
import * as MIME from '../../../../../types/MIME';
const StyledQuoteContainer = styled.div` const StyledQuoteContainer = styled.div`
min-width: 300px; // if the quoted content is small it doesn't look very good so we set a minimum min-width: 300px; // if the quoted content is small it doesn't look very good so we set a minimum

@ -2,9 +2,9 @@ import React from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import { useQuoteAuthorName } from '../../../../../hooks/useParamSelector'; import { useQuoteAuthorName } from '../../../../../hooks/useParamSelector';
import { PubKey } from '../../../../../session/types'; import { PubKey } from '../../../../../session/types';
import { useSelectedIsPublic } from '../../../../../state/selectors/selectedConversation';
import { ContactName } from '../../../ContactName'; import { ContactName } from '../../../ContactName';
import { QuoteProps } from './Quote'; import { QuoteProps } from './Quote';
import { useSelectedIsPublic } from '../../../../../state/selectors/selectedConversation';
const StyledQuoteAuthor = styled.div<{ isIncoming: boolean }>` const StyledQuoteAuthor = styled.div<{ isIncoming: boolean }>`
color: ${props => color: ${props =>
@ -18,6 +18,7 @@ const StyledQuoteAuthor = styled.div<{ isIncoming: boolean }>`
overflow-x: hidden; overflow-x: hidden;
white-space: nowrap; white-space: nowrap;
text-overflow: ellipsis; text-overflow: ellipsis;
.module-contact-name { .module-contact-name {
font-weight: bold; font-weight: bold;
} }

@ -2,6 +2,7 @@ import React, { useCallback, useState } from 'react';
import { useDispatch } from 'react-redux'; import { useDispatch } from 'react-redux';
import { useInterval, useMount } from 'react-use'; import { useInterval, useMount } from 'react-use';
import styled from 'styled-components'; import styled from 'styled-components';
import { useIsDetailMessageView } from '../../../../contexts/isDetailViewContext';
import { Data } from '../../../../data/data'; import { Data } from '../../../../data/data';
import { useMessageExpirationPropsById } from '../../../../hooks/useParamSelector'; import { useMessageExpirationPropsById } from '../../../../hooks/useParamSelector';
import { MessageModelType } from '../../../../models/messageType'; import { MessageModelType } from '../../../../models/messageType';
@ -84,7 +85,6 @@ export interface ExpirableReadableMessageProps
extends Omit<ReadableMessageProps, 'receivedAt' | 'isUnread'> { extends Omit<ReadableMessageProps, 'receivedAt' | 'isUnread'> {
messageId: string; messageId: string;
isControlMessage?: boolean; isControlMessage?: boolean;
isDetailView?: boolean;
} }
function ExpireTimerControlMessage({ function ExpireTimerControlMessage({
@ -109,6 +109,7 @@ function ExpireTimerControlMessage({
export const ExpirableReadableMessage = (props: ExpirableReadableMessageProps) => { export const ExpirableReadableMessage = (props: ExpirableReadableMessageProps) => {
const selected = useMessageExpirationPropsById(props.messageId); const selected = useMessageExpirationPropsById(props.messageId);
const isDetailView = useIsDetailMessageView();
const { isControlMessage, onClick, onDoubleClickCapture, role, dataTestId } = props; const { isControlMessage, onClick, onDoubleClickCapture, role, dataTestId } = props;
@ -135,7 +136,7 @@ export const ExpirableReadableMessage = (props: ExpirableReadableMessageProps) =
} = selected; } = selected;
// NOTE we want messages on the left in the message detail view regardless of direction // NOTE we want messages on the left in the message detail view regardless of direction
const direction = props.isDetailView ? 'incoming' : _direction; const direction = isDetailView ? 'incoming' : _direction;
const isIncoming = direction === 'incoming'; const isIncoming = direction === 'incoming';
return ( return (

@ -4,6 +4,7 @@ import React, { useCallback, useEffect, useState } from 'react';
import { contextMenu } from 'react-contexify'; import { contextMenu } from 'react-contexify';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import styled, { keyframes } from 'styled-components'; import styled, { keyframes } from 'styled-components';
import { useIsDetailMessageView } from '../../../../contexts/isDetailViewContext';
import { MessageRenderingProps } from '../../../../models/messageType'; import { MessageRenderingProps } from '../../../../models/messageType';
import { getConversationController } from '../../../../session/conversations'; import { getConversationController } from '../../../../session/conversations';
import { StateType } from '../../../../state/reducer'; import { StateType } from '../../../../state/reducer';
@ -29,7 +30,6 @@ export type GenericReadableMessageSelectorProps = Pick<
type Props = { type Props = {
messageId: string; messageId: string;
ctxMenuID: string; ctxMenuID: string;
isDetailView?: boolean;
}; };
const highlightedMessageAnimation = keyframes` const highlightedMessageAnimation = keyframes`
@ -40,8 +40,8 @@ const highlightedMessageAnimation = keyframes`
const StyledReadableMessage = styled.div<{ const StyledReadableMessage = styled.div<{
selected: boolean; selected: boolean;
isDetailView: boolean;
isRightClicked: boolean; isRightClicked: boolean;
isDetailView?: boolean;
}>` }>`
display: flex; display: flex;
align-items: center; align-items: center;
@ -64,7 +64,9 @@ const StyledReadableMessage = styled.div<{
`; `;
export const GenericReadableMessage = (props: Props) => { export const GenericReadableMessage = (props: Props) => {
const { ctxMenuID, messageId, isDetailView } = props; const isDetailView = useIsDetailMessageView();
const { ctxMenuID, messageId } = props;
const [enableReactions, setEnableReactions] = useState(true); const [enableReactions, setEnableReactions] = useState(true);
@ -148,7 +150,6 @@ export const GenericReadableMessage = (props: Props) => {
<MessageContentWithStatuses <MessageContentWithStatuses
ctxMenuID={ctxMenuID} ctxMenuID={ctxMenuID}
messageId={messageId} messageId={messageId}
isDetailView={isDetailView}
dataTestId={'message-content'} dataTestId={'message-content'}
enableReactions={enableReactions} enableReactions={enableReactions}
/> />

@ -12,7 +12,6 @@ export const MINIMUM_LINK_PREVIEW_IMAGE_WIDTH = THUMBNAIL_SIDE;
type Props = { type Props = {
messageId: string; messageId: string;
isDetailView?: boolean; // when the detail is shown for a message, we disable click and some other stuff
}; };
export const Message = (props: Props) => { export const Message = (props: Props) => {
@ -26,11 +25,5 @@ export const Message = (props: Props) => {
return null; return null;
} }
return ( return <GenericReadableMessage ctxMenuID={ctxMenuID} messageId={props.messageId} />;
<GenericReadableMessage
ctxMenuID={ctxMenuID}
messageId={props.messageId}
isDetailView={props.isDetailView}
/>
);
}; };

@ -39,7 +39,7 @@ export const MessageRequestResponse = (props: PropsForMessageRequestResponse) =>
id={`msg-${messageId}`} id={`msg-${messageId}`}
> >
<SpacerSM /> <SpacerSM />
<Text text={msgText} subtle={true} ellipsisOverflow={true} /> <Text text={msgText} subtle={true} ellipsisOverflow={false} textAlign="center" />
</Flex> </Flex>
</ReadableMessage> </ReadableMessage>
); );

@ -1,14 +1,8 @@
import { debounce, noop } from 'lodash'; import { debounce, noop } from 'lodash';
import React, { import React, { AriaRole, MouseEventHandler, useCallback, useLayoutEffect, useState } from 'react';
AriaRole,
MouseEventHandler,
useCallback,
useContext,
useLayoutEffect,
useState,
} from 'react';
import { InView } from 'react-intersection-observer'; import { InView } from 'react-intersection-observer';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import { useScrollToLoadedMessage } from '../../../../contexts/ScrollToLoadedMessage';
import { Data } from '../../../../data/data'; import { Data } from '../../../../data/data';
import { useHasUnread } from '../../../../hooks/useParamSelector'; import { useHasUnread } from '../../../../hooks/useParamSelector';
import { getConversationController } from '../../../../session/conversations'; import { getConversationController } from '../../../../session/conversations';
@ -28,7 +22,6 @@ import {
} from '../../../../state/selectors/conversations'; } from '../../../../state/selectors/conversations';
import { getIsAppFocused } from '../../../../state/selectors/section'; import { getIsAppFocused } from '../../../../state/selectors/section';
import { useSelectedConversationKey } from '../../../../state/selectors/selectedConversation'; import { useSelectedConversationKey } from '../../../../state/selectors/selectedConversation';
import { ScrollToLoadedMessageContext } from '../../SessionMessagesListContainer';
export type ReadableMessageProps = { export type ReadableMessageProps = {
children: React.ReactNode; children: React.ReactNode;
@ -95,7 +88,7 @@ export const ReadableMessage = (props: ReadableMessageProps) => {
const [didScroll, setDidScroll] = useState(false); const [didScroll, setDidScroll] = useState(false);
const quotedMessageToAnimate = useSelector(getQuotedMessageToAnimate); const quotedMessageToAnimate = useSelector(getQuotedMessageToAnimate);
const scrollToLoadedMessage = useContext(ScrollToLoadedMessageContext); const scrollToLoadedMessage = useScrollToLoadedMessage();
// if this unread-indicator is rendered, // if this unread-indicator is rendered,
// we want to scroll here only if the conversation was not opened to a specific message // we want to scroll here only if the conversation was not opened to a specific message

@ -10,6 +10,7 @@ import { getMessageInfoId } from '../../../../../state/selectors/conversations';
import { Flex } from '../../../../basic/Flex'; import { Flex } from '../../../../basic/Flex';
import { Header, HeaderTitle, StyledScrollContainer } from '../components'; import { Header, HeaderTitle, StyledScrollContainer } from '../components';
import { IsDetailMessageViewContext } from '../../../../../contexts/isDetailViewContext';
import { Data } from '../../../../../data/data'; import { Data } from '../../../../../data/data';
import { useRightOverlayMode } from '../../../../../hooks/useUI'; import { useRightOverlayMode } from '../../../../../hooks/useUI';
import { import {
@ -71,9 +72,11 @@ const MessageBody = ({
} }
return ( return (
<IsDetailMessageViewContext.Provider value={true}>
<StyledMessageBody> <StyledMessageBody>
<Message messageId={messageId} isDetailView={true} /> <Message messageId={messageId} />
</StyledMessageBody> </StyledMessageBody>
</IsDetailMessageViewContext.Provider>
); );
}; };

@ -2,7 +2,7 @@ import React from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import { MessageInfoLabel } from '.'; import { MessageInfoLabel } from '.';
import { useConversationUsername } from '../../../../../../hooks/useParamSelector'; import { useConversationUsername } from '../../../../../../hooks/useParamSelector';
import { Avatar, AvatarSize } from '../../../../../avatar/Avatar'; import { Avatar, AvatarSize, CrownIcon } from '../../../../../avatar/Avatar';
const StyledFromContainer = styled.div` const StyledFromContainer = styled.div`
display: flex; display: flex;
@ -30,8 +30,12 @@ const StyledMessageInfoAuthor = styled.div`
font-size: var(--font-size-lg); font-size: var(--font-size-lg);
`; `;
export const MessageFrom = (props: { sender: string }) => { const StyledAvatar = styled.div`
const { sender } = props; position: relative;
`;
export const MessageFrom = (props: { sender: string; isSenderAdmin: boolean }) => {
const { sender, isSenderAdmin } = props;
const profileName = useConversationUsername(sender); const profileName = useConversationUsername(sender);
const from = window.i18n('from'); const from = window.i18n('from');
@ -39,7 +43,10 @@ export const MessageFrom = (props: { sender: string }) => {
<StyledMessageInfoAuthor> <StyledMessageInfoAuthor>
<MessageInfoLabel>{from}</MessageInfoLabel> <MessageInfoLabel>{from}</MessageInfoLabel>
<StyledFromContainer> <StyledFromContainer>
<StyledAvatar>
<Avatar size={AvatarSize.M} pubkey={sender} onAvatarClick={undefined} /> <Avatar size={AvatarSize.M} pubkey={sender} onAvatarClick={undefined} />
{isSenderAdmin ? <CrownIcon /> : null}
</StyledAvatar>
<StyledAuthorNamesContainer> <StyledAuthorNamesContainer>
{!!profileName && <Name>{profileName}</Name>} {!!profileName && <Name>{profileName}</Name>}
<Pubkey>{sender}</Pubkey> <Pubkey>{sender}</Pubkey>

@ -14,6 +14,7 @@ import {
useMessageHash, useMessageHash,
useMessageReceivedAt, useMessageReceivedAt,
useMessageSender, useMessageSender,
useMessageSenderIsAdmin,
useMessageServerId, useMessageServerId,
useMessageServerTimestamp, useMessageServerTimestamp,
useMessageTimestamp, useMessageTimestamp,
@ -111,6 +112,7 @@ export const MessageInfo = ({ messageId, errors }: { messageId: string; errors:
const sentAt = useMessageTimestamp(messageId); const sentAt = useMessageTimestamp(messageId);
const serverTimestamp = useMessageServerTimestamp(messageId); const serverTimestamp = useMessageServerTimestamp(messageId);
const receivedAt = useMessageReceivedAt(messageId); const receivedAt = useMessageReceivedAt(messageId);
const isSenderAdmin = useMessageSenderIsAdmin(messageId);
if (!messageId || !sender) { if (!messageId || !sender) {
return null; return null;
@ -137,7 +139,7 @@ export const MessageInfo = ({ messageId, errors }: { messageId: string; errors:
<LabelWithInfo label={`${window.i18n('received')}:`} info={receivedAtStr} /> <LabelWithInfo label={`${window.i18n('received')}:`} info={receivedAtStr} />
) : null} ) : null}
<SpacerSM /> <SpacerSM />
<MessageFrom sender={sender} /> <MessageFrom sender={sender} isSenderAdmin={isSenderAdmin} />
{hasError && ( {hasError && (
<> <>
<SpacerSM /> <SpacerSM />

@ -10,6 +10,10 @@ import { Avatar, AvatarSize } from '../../avatar/Avatar';
import { openConversationWithMessages } from '../../../state/ducks/conversations'; import { openConversationWithMessages } from '../../../state/ducks/conversations';
import { updateUserDetailsModal } from '../../../state/ducks/modalDialog'; import { updateUserDetailsModal } from '../../../state/ducks/modalDialog';
import {
ContextConversationProvider,
useConvoIdFromContext,
} from '../../../contexts/ConvoIdContext';
import { import {
useAvatarPath, useAvatarPath,
useConversationUsername, useConversationUsername,
@ -21,7 +25,6 @@ import {
import { isSearching } from '../../../state/selectors/search'; import { isSearching } from '../../../state/selectors/search';
import { useSelectedConversationKey } from '../../../state/selectors/selectedConversation'; import { useSelectedConversationKey } from '../../../state/selectors/selectedConversation';
import { MemoConversationListItemContextMenu } from '../../menu/ConversationListItemContextMenu'; import { MemoConversationListItemContextMenu } from '../../menu/ConversationListItemContextMenu';
import { ContextConversationProvider, useConvoIdFromContext } from './ConvoIdContext';
import { ConversationListItemHeaderItem } from './HeaderItem'; import { ConversationListItemHeaderItem } from './HeaderItem';
import { MessageItem } from './MessageItem'; import { MessageItem } from './MessageItem';

@ -2,6 +2,7 @@ import classNames from 'classnames';
import React from 'react'; import React from 'react';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import styled from 'styled-components'; import styled from 'styled-components';
import { useConvoIdFromContext } from '../../../contexts/ConvoIdContext';
import { Data } from '../../../data/data'; import { Data } from '../../../data/data';
import { import {
useActiveAt, useActiveAt,
@ -20,7 +21,6 @@ import { isSearching } from '../../../state/selectors/search';
import { getIsMessageSection } from '../../../state/selectors/section'; import { getIsMessageSection } from '../../../state/selectors/section';
import { Timestamp } from '../../conversation/Timestamp'; import { Timestamp } from '../../conversation/Timestamp';
import { SessionIcon } from '../../icon'; import { SessionIcon } from '../../icon';
import { useConvoIdFromContext } from './ConvoIdContext';
import { UserItem } from './UserItem'; import { UserItem } from './UserItem';
const NotificationSettingIcon = () => { const NotificationSettingIcon = () => {

@ -2,6 +2,7 @@ import classNames from 'classnames';
import { isEmpty } from 'lodash'; import { isEmpty } from 'lodash';
import React from 'react'; import React from 'react';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { useConvoIdFromContext } from '../../../contexts/ConvoIdContext';
import { import {
useHasUnread, useHasUnread,
useIsPrivate, useIsPrivate,
@ -15,7 +16,6 @@ import { assertUnreachable } from '../../../types/sqlSharedTypes';
import { TypingAnimation } from '../../conversation/TypingAnimation'; import { TypingAnimation } from '../../conversation/TypingAnimation';
import { MessageBody } from '../../conversation/message/message-content/MessageBody'; import { MessageBody } from '../../conversation/message/message-content/MessageBody';
import { SessionIcon } from '../../icon'; import { SessionIcon } from '../../icon';
import { useConvoIdFromContext } from './ConvoIdContext';
import { InteractionItem } from './InteractionItem'; import { InteractionItem } from './InteractionItem';
export const MessageItem = () => { export const MessageItem = () => {

@ -1,5 +1,6 @@
import React from 'react'; import React from 'react';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { useConvoIdFromContext } from '../../../contexts/ConvoIdContext';
import { import {
useConversationRealName, useConversationRealName,
useConversationUsername, useConversationUsername,
@ -9,7 +10,6 @@ import {
import { PubKey } from '../../../session/types'; import { PubKey } from '../../../session/types';
import { isSearching } from '../../../state/selectors/search'; import { isSearching } from '../../../state/selectors/search';
import { ContactName } from '../../conversation/ContactName'; import { ContactName } from '../../conversation/ContactName';
import { useConvoIdFromContext } from './ConvoIdContext';
export const UserItem = () => { export const UserItem = () => {
const conversationId = useConvoIdFromContext(); const conversationId = useConvoIdFromContext();
@ -36,7 +36,6 @@ export const UserItem = () => {
} }
return ( return (
<div className="module-conversation__user">
<ContactName <ContactName
pubkey={displayedPubkey} pubkey={displayedPubkey}
name={username} name={username}
@ -45,6 +44,5 @@ export const UserItem = () => {
boldProfileName={true} boldProfileName={true}
shouldShowPubkey={shouldShowPubkey} shouldShowPubkey={shouldShowPubkey}
/> />
</div>
); );
}; };

@ -2,10 +2,11 @@ import React from 'react';
import { Item, Menu } from 'react-contexify'; import { Item, Menu } from 'react-contexify';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { useConvoIdFromContext } from '../../contexts/ConvoIdContext';
import { useIsPinned, useIsPrivate, useIsPrivateAndFriend } from '../../hooks/useParamSelector'; import { useIsPinned, useIsPrivate, useIsPrivateAndFriend } from '../../hooks/useParamSelector';
import { getConversationController } from '../../session/conversations'; import { getConversationController } from '../../session/conversations';
import { isSearching } from '../../state/selectors/search';
import { getIsMessageSection } from '../../state/selectors/section'; import { getIsMessageSection } from '../../state/selectors/section';
import { useConvoIdFromContext } from '../leftpane/conversation-list-item/ConvoIdContext';
import { SessionContextMenuContainer } from '../SessionContextMenuContainer'; import { SessionContextMenuContainer } from '../SessionContextMenuContainer';
import { import {
AcceptMsgRequestMenuItem, AcceptMsgRequestMenuItem,
@ -17,16 +18,15 @@ import {
DeclineAndBlockMsgRequestMenuItem, DeclineAndBlockMsgRequestMenuItem,
DeclineMsgRequestMenuItem, DeclineMsgRequestMenuItem,
DeleteMessagesMenuItem, DeleteMessagesMenuItem,
DeletePrivateConversationMenuItem,
InviteContactMenuItem, InviteContactMenuItem,
LeaveGroupOrCommunityMenuItem, LeaveGroupOrCommunityMenuItem,
MarkAllReadMenuItem, MarkAllReadMenuItem,
MarkConversationUnreadMenuItem, MarkConversationUnreadMenuItem,
NotificationForConvoMenuItem,
ShowUserDetailsMenuItem, ShowUserDetailsMenuItem,
UnbanMenuItem, UnbanMenuItem,
DeletePrivateConversationMenuItem,
NotificationForConvoMenuItem,
} from './Menu'; } from './Menu';
import { isSearching } from '../../state/selectors/search';
export type PropsContextConversationItem = { export type PropsContextConversationItem = {
triggerId: string; triggerId: string;

@ -2,6 +2,7 @@ import React from 'react';
import { Item, Submenu } from 'react-contexify'; import { Item, Submenu } from 'react-contexify';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import { useConvoIdFromContext } from '../../contexts/ConvoIdContext';
import { import {
useAvatarPath, useAvatarPath,
useConversationUsername, useConversationUsername,
@ -56,7 +57,6 @@ import { getIsMessageSection } from '../../state/selectors/section';
import { useSelectedConversationKey } from '../../state/selectors/selectedConversation'; import { useSelectedConversationKey } from '../../state/selectors/selectedConversation';
import { LocalizerKeys } from '../../types/LocalizerKeys'; import { LocalizerKeys } from '../../types/LocalizerKeys';
import { SessionButtonColor } from '../basic/SessionButton'; import { SessionButtonColor } from '../basic/SessionButton';
import { useConvoIdFromContext } from '../leftpane/conversation-list-item/ConvoIdContext';
/** Menu items standardized */ /** Menu items standardized */

@ -1,15 +1,15 @@
import React from 'react'; import React from 'react';
import styled, { CSSProperties } from 'styled-components'; import styled, { CSSProperties } from 'styled-components';
import { useConversationUsername, useIsPrivate } from '../../hooks/useParamSelector';
import { MessageAttributes } from '../../models/messageType';
import { UserUtils } from '../../session/utils';
import { getOurPubKeyStrFromCache } from '../../session/utils/User'; import { getOurPubKeyStrFromCache } from '../../session/utils/User';
import { openConversationToSpecificMessage } from '../../state/ducks/conversations'; import { openConversationToSpecificMessage } from '../../state/ducks/conversations';
import { ContactName } from '../conversation/ContactName';
import { Avatar, AvatarSize } from '../avatar/Avatar'; import { Avatar, AvatarSize } from '../avatar/Avatar';
import { Timestamp } from '../conversation/Timestamp';
import { MessageBodyHighlight } from '../basic/MessageBodyHighlight'; import { MessageBodyHighlight } from '../basic/MessageBodyHighlight';
import { MessageAttributes } from '../../models/messageType'; import { ContactName } from '../conversation/ContactName';
import { useConversationUsername, useIsPrivate } from '../../hooks/useParamSelector'; import { Timestamp } from '../conversation/Timestamp';
import { UserUtils } from '../../session/utils';
export type MessageResultProps = MessageAttributes & { snippet: string }; export type MessageResultProps = MessageAttributes & { snippet: string };
@ -58,6 +58,7 @@ const StyledResultText = styled.div`
display: inline-flex; display: inline-flex;
flex-direction: column; flex-direction: column;
align-items: stretch; align-items: stretch;
min-width: 0;
`; `;
const ResultsHeader = styled.div` const ResultsHeader = styled.div`

@ -0,0 +1,16 @@
import { createContext, useContext } from 'react';
export type ScrollToLoadedReasons =
| 'quote-or-search-result'
| 'go-to-bottom'
| 'unread-indicator'
| 'load-more-top'
| 'load-more-bottom';
export const ScrollToLoadedMessageContext = createContext(
(_loadedMessageIdToScrollTo: string, _reason: ScrollToLoadedReasons) => {}
);
export function useScrollToLoadedMessage() {
return useContext(ScrollToLoadedMessageContext);
}

@ -0,0 +1,10 @@
import { createContext, useContext } from 'react';
/**
* When the message is rendered as part of the detailView (right panel) we disable onClick and make some other minor UI changes
*/
export const IsDetailMessageViewContext = createContext<boolean>(false);
export function useIsDetailMessageView() {
return useContext(IsDetailMessageViewContext);
}

@ -0,0 +1,7 @@
import { createContext, useContext } from 'react';
export const IsMessageVisibleContext = createContext(false);
export function useIsMessageVisible() {
return useContext(IsMessageVisibleContext);
}

@ -160,10 +160,10 @@ export const useMessageText = (messageId: string | undefined): string | undefine
return useMessagePropsByMessageId(messageId)?.propsForMessage.text; return useMessagePropsByMessageId(messageId)?.propsForMessage.text;
}; };
export function useHideAvatarInMsgList(messageId?: string) { export function useHideAvatarInMsgList(messageId?: string, isDetailView?: boolean) {
const msgProps = useMessagePropsByMessageId(messageId); const msgProps = useMessagePropsByMessageId(messageId);
const selectedIsPrivate = useSelectedIsPrivate(); const selectedIsPrivate = useSelectedIsPrivate();
return msgProps?.propsForMessage.direction === 'outgoing' || selectedIsPrivate; return isDetailView || msgProps?.propsForMessage.direction === 'outgoing' || selectedIsPrivate;
} }
export function useMessageSelected(messageId?: string) { export function useMessageSelected(messageId?: string) {

Loading…
Cancel
Save