From dcdea007486de4872d519e0ca442399189cdafd0 Mon Sep 17 00:00:00 2001 From: William Grant Date: Thu, 29 Feb 2024 10:39:25 +1100 Subject: [PATCH 1/6] fix: dont show message avatar in message info panels also show admin crown --- .../message/message-content/MessageAvatar.tsx | 12 ++++-------- .../message/message-content/MessageContent.tsx | 15 ++++++--------- .../message-info/components/MessageFrom.tsx | 15 +++++++++++---- .../message-info/components/MessageInfo.tsx | 4 +++- ts/state/selectors/messages.ts | 4 ++-- 5 files changed, 26 insertions(+), 24 deletions(-) diff --git a/ts/components/conversation/message/message-content/MessageAvatar.tsx b/ts/components/conversation/message/message-content/MessageAvatar.tsx index 1cbe2dde8..e9b0dd9ff 100644 --- a/ts/components/conversation/message/message-content/MessageAvatar.tsx +++ b/ts/components/conversation/message/message-content/MessageAvatar.tsx @@ -38,10 +38,10 @@ export type MessageAvatarSelectorProps = Pick< 'sender' | 'isSenderAdmin' | 'lastMessageOfSeries' >; -type Props = { messageId: string; hideAvatar: boolean; isPrivate: boolean; isDetailView?: boolean }; +type Props = { messageId: string; isPrivate: boolean }; export const MessageAvatar = (props: Props) => { - const { messageId, hideAvatar, isPrivate, isDetailView } = props; + const { messageId, isPrivate } = props; const dispatch = useDispatch(); 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). // 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 ( - + - {!isDetailView && isSenderAdmin ? : null} + {isSenderAdmin ? : null} ); }; diff --git a/ts/components/conversation/message/message-content/MessageContent.tsx b/ts/components/conversation/message/message-content/MessageContent.tsx index 24a0619b2..768a4e3f9 100644 --- a/ts/components/conversation/message/message-content/MessageContent.tsx +++ b/ts/components/conversation/message/message-content/MessageContent.tsx @@ -93,7 +93,7 @@ export const MessageContent = (props: Props) => { const scrollToLoadedMessage = useContext(ScrollToLoadedMessageContext); const selectedIsPrivate = useSelectedIsPrivate(); - const hideAvatar = useHideAvatarInMsgList(props.messageId); + const hideAvatar = useHideAvatarInMsgList(props.messageId, props.isDetailView); const [imageBroken, setImageBroken] = useState(false); @@ -164,14 +164,11 @@ export const MessageContent = (props: Props) => { title={toolTipTitle} msgDirection={direction} > - - - + {hideAvatar ? null : ( + + + + )} { - const { sender } = props; +const StyledAvatar = styled.div` + position: relative; +`; + +export const MessageFrom = (props: { sender: string; isSenderAdmin: boolean }) => { + const { sender, isSenderAdmin } = props; const profileName = useConversationUsername(sender); const from = window.i18n('from'); @@ -39,7 +43,10 @@ export const MessageFrom = (props: { sender: string }) => { {from} - + + + {isSenderAdmin ? : null} + {!!profileName && {profileName}} {sender} diff --git a/ts/components/conversation/right-panel/overlay/message-info/components/MessageInfo.tsx b/ts/components/conversation/right-panel/overlay/message-info/components/MessageInfo.tsx index 1875298f0..4bda46496 100644 --- a/ts/components/conversation/right-panel/overlay/message-info/components/MessageInfo.tsx +++ b/ts/components/conversation/right-panel/overlay/message-info/components/MessageInfo.tsx @@ -14,6 +14,7 @@ import { useMessageHash, useMessageReceivedAt, useMessageSender, + useMessageSenderIsAdmin, useMessageServerId, useMessageServerTimestamp, useMessageTimestamp, @@ -111,6 +112,7 @@ export const MessageInfo = ({ messageId, errors }: { messageId: string; errors: const sentAt = useMessageTimestamp(messageId); const serverTimestamp = useMessageServerTimestamp(messageId); const receivedAt = useMessageReceivedAt(messageId); + const isSenderAdmin = useMessageSenderIsAdmin(messageId); if (!messageId || !sender) { return null; @@ -137,7 +139,7 @@ export const MessageInfo = ({ messageId, errors }: { messageId: string; errors: ) : null} - + {hasError && ( <> diff --git a/ts/state/selectors/messages.ts b/ts/state/selectors/messages.ts index b998bf7f7..6c87ca3e6 100644 --- a/ts/state/selectors/messages.ts +++ b/ts/state/selectors/messages.ts @@ -160,10 +160,10 @@ export const useMessageText = (messageId: string | undefined): string | undefine return useMessagePropsByMessageId(messageId)?.propsForMessage.text; }; -export function useHideAvatarInMsgList(messageId?: string) { +export function useHideAvatarInMsgList(messageId?: string, isDetailView?: boolean) { const msgProps = useMessagePropsByMessageId(messageId); const selectedIsPrivate = useSelectedIsPrivate(); - return msgProps?.propsForMessage.direction === 'outgoing' || selectedIsPrivate; + return isDetailView || msgProps?.propsForMessage.direction === 'outgoing' || selectedIsPrivate; } export function useMessageSelected(messageId?: string) { From 8cb6b3099b3d96b973f09904ce1f84ca4b94b754 Mon Sep 17 00:00:00 2001 From: William Grant Date: Thu, 29 Feb 2024 10:53:26 +1100 Subject: [PATCH 2/6] fix: no padding on message content container in message info panel --- .../message/message-content/MessageContentWithStatus.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ts/components/conversation/message/message-content/MessageContentWithStatus.tsx b/ts/components/conversation/message/message-content/MessageContentWithStatus.tsx index 9e4a23d63..7caccde17 100644 --- a/ts/components/conversation/message/message-content/MessageContentWithStatus.tsx +++ b/ts/components/conversation/message/message-content/MessageContentWithStatus.tsx @@ -34,13 +34,13 @@ type Props = { enableReactions: boolean; }; -const StyledMessageContentContainer = styled.div<{ isIncoming: boolean }>` +const StyledMessageContentContainer = styled.div<{ isIncoming: boolean; isDetailView: boolean }>` display: flex; flex-direction: column; justify-content: flex-start; align-items: ${props => (props.isIncoming ? 'flex-start' : 'flex-end')}; - padding-left: ${props => (props.isIncoming ? 0 : '25%')}; - padding-right: ${props => (props.isIncoming ? '25%' : 0)}; + padding-left: ${props => (props.isDetailView || props.isIncoming ? 0 : '25%')}; + padding-right: ${props => (props.isDetailView || !props.isIncoming ? 0 : '25%')}; width: 100%; margin-right: var(--margins-md); `; @@ -119,6 +119,7 @@ export const MessageContentWithStatuses = (props: Props) => { return ( { setPopupReaction(''); }} From 500f994d5dff0065dcec43d577452ca197c5da25 Mon Sep 17 00:00:00 2001 From: William Grant Date: Thu, 29 Feb 2024 11:06:43 +1100 Subject: [PATCH 3/6] fix: quote author in message info will now wrap otherwise it causes the message bubble to overflow --- .../message/message-content/MessageContent.tsx | 2 +- .../message/message-content/MessageQuote.tsx | 2 ++ .../message/message-content/quote/Quote.tsx | 7 ++++--- .../message/message-content/quote/QuoteAuthor.tsx | 12 ++++++------ 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/ts/components/conversation/message/message-content/MessageContent.tsx b/ts/components/conversation/message/message-content/MessageContent.tsx index 768a4e3f9..149ab12a9 100644 --- a/ts/components/conversation/message/message-content/MessageContent.tsx +++ b/ts/components/conversation/message/message-content/MessageContent.tsx @@ -192,7 +192,7 @@ export const MessageContent = (props: Props) => { > {!isDeleted && ( <> - + ; @@ -97,6 +98,7 @@ export const MessageQuote = (props: Props) => { author={quote.author} referencedMessageNotFound={quoteNotFound} isFromMe={Boolean(quote.isFromMe)} + isDetailView={props.isDetailView} /> ); }; diff --git a/ts/components/conversation/message/message-content/quote/Quote.tsx b/ts/components/conversation/message/message-content/quote/Quote.tsx index 342ee705c..f60aed64c 100644 --- a/ts/components/conversation/message/message-content/quote/Quote.tsx +++ b/ts/components/conversation/message/message-content/quote/Quote.tsx @@ -3,10 +3,10 @@ import React, { MouseEvent, useState } from 'react'; import { isEmpty } from 'lodash'; import styled from 'styled-components'; import { useIsMessageSelectionMode } from '../../../../../state/selectors/selectedConversation'; +import * as MIME from '../../../../../types/MIME'; import { QuoteAuthor } from './QuoteAuthor'; import { QuoteIconContainer } from './QuoteIconContainer'; import { QuoteText } from './QuoteText'; -import * as MIME from '../../../../../types/MIME'; const StyledQuoteContainer = styled.div` min-width: 300px; // if the quoted content is small it doesn't look very good so we set a minimum @@ -50,6 +50,7 @@ export type QuoteProps = { referencedMessageNotFound: boolean; text?: string; attachment?: QuotedAttachmentType; + isDetailView?: boolean; onClick?: (e: React.MouseEvent) => void; }; @@ -70,7 +71,7 @@ export interface QuotedAttachmentType { export const Quote = (props: QuoteProps) => { const isSelectionMode = useIsMessageSelectionMode(); - const { isIncoming, attachment, text, referencedMessageNotFound, onClick } = props; + const { isIncoming, attachment, text, isDetailView, referencedMessageNotFound, onClick } = props; const [imageBroken, setImageBroken] = useState(false); const handleImageErrorBound = () => { @@ -95,7 +96,7 @@ export const Quote = (props: QuoteProps) => { referencedMessageNotFound={referencedMessageNotFound} /> - + ` +const StyledQuoteAuthor = styled.div<{ isIncoming: boolean; isDetailView?: boolean }>` color: ${props => props.isIncoming ? 'var(--message-bubbles-received-text-color)' @@ -16,17 +16,17 @@ const StyledQuoteAuthor = styled.div<{ isIncoming: boolean }>` line-height: 18px; margin-bottom: 2px; overflow-x: hidden; - white-space: nowrap; + white-space: ${props => (props.isDetailView ? undefined : 'nowrap')}; text-overflow: ellipsis; .module-contact-name { font-weight: bold; } `; -type QuoteAuthorProps = Pick; +type QuoteAuthorProps = Pick & { isDetailView?: boolean }; export const QuoteAuthor = (props: QuoteAuthorProps) => { - const { author, isIncoming } = props; + const { author, isIncoming, isDetailView } = props; const isPublic = useSelectedIsPublic(); const { authorName, isMe } = useQuoteAuthorName(author); @@ -36,7 +36,7 @@ export const QuoteAuthor = (props: QuoteAuthorProps) => { } return ( - + Date: Mon, 4 Mar 2024 10:27:05 +1100 Subject: [PATCH 4/6] fix: consolidate isDetailView into one type called hasDetailView which we import in various components --- .../message/message-content/MessageContent.tsx | 4 ++-- .../message-content/MessageContentWithStatus.tsx | 6 +++--- .../message/message-content/MessageQuote.tsx | 4 ++-- .../message/message-content/MessageReactions.tsx | 4 ++-- .../message/message-content/MessageStatus.tsx | 4 ++-- .../message/message-content/quote/Quote.tsx | 4 ++-- .../message/message-content/quote/QuoteAuthor.tsx | 5 +++-- .../message-item/ExpirableReadableMessage.tsx | 5 +++-- .../message-item/GenericReadableMessage.tsx | 15 ++++++++------- .../conversation/message/message-item/Message.tsx | 6 ++++-- 10 files changed, 31 insertions(+), 26 deletions(-) diff --git a/ts/components/conversation/message/message-content/MessageContent.tsx b/ts/components/conversation/message/message-content/MessageContent.tsx index 149ab12a9..04f8b9e1f 100644 --- a/ts/components/conversation/message/message-content/MessageContent.tsx +++ b/ts/components/conversation/message/message-content/MessageContent.tsx @@ -20,6 +20,7 @@ import { import { useSelectedIsPrivate } from '../../../../state/selectors/selectedConversation'; import { canDisplayImage } from '../../../../types/Attachment'; import { ScrollToLoadedMessageContext } from '../../SessionMessagesListContainer'; +import { hasDetailView } from '../message-item/Message'; import { MessageAttachment } from './MessageAttachment'; import { MessageAvatar } from './MessageAvatar'; import { MessageHighlighter } from './MessageHighlighter'; @@ -32,9 +33,8 @@ export type MessageContentSelectorProps = Pick< 'text' | 'direction' | 'timestamp' | 'serverTimestamp' | 'previews' | 'quote' | 'attachments' >; -type Props = { +type Props = hasDetailView & { messageId: string; - isDetailView?: boolean; }; // TODO not too sure what is this doing? It is not preventDefault() diff --git a/ts/components/conversation/message/message-content/MessageContentWithStatus.tsx b/ts/components/conversation/message/message-content/MessageContentWithStatus.tsx index 7caccde17..3b415d578 100644 --- a/ts/components/conversation/message/message-content/MessageContentWithStatus.tsx +++ b/ts/components/conversation/message/message-content/MessageContentWithStatus.tsx @@ -15,6 +15,7 @@ import { import { Reactions } from '../../../../util/reactions'; import { Flex } from '../../../basic/Flex'; import { ExpirableReadableMessage } from '../message-item/ExpirableReadableMessage'; +import { hasDetailView } from '../message-item/Message'; import { MessageAuthorText } from './MessageAuthorText'; import { MessageContent } from './MessageContent'; import { MessageContextMenu } from './MessageContextMenu'; @@ -26,15 +27,14 @@ export type MessageContentWithStatusSelectorProps = { isGroup: boolean } & Pick< 'conversationType' | 'direction' | 'isDeleted' >; -type Props = { +type Props = hasDetailView & { messageId: string; ctxMenuID: string; - isDetailView?: boolean; dataTestId: string; enableReactions: boolean; }; -const StyledMessageContentContainer = styled.div<{ isIncoming: boolean; isDetailView: boolean }>` +const StyledMessageContentContainer = styled.div` display: flex; flex-direction: column; justify-content: flex-start; diff --git a/ts/components/conversation/message/message-content/MessageQuote.tsx b/ts/components/conversation/message/message-content/MessageQuote.tsx index b05b6b78a..e241d662e 100644 --- a/ts/components/conversation/message/message-content/MessageQuote.tsx +++ b/ts/components/conversation/message/message-content/MessageQuote.tsx @@ -8,11 +8,11 @@ import { openConversationToSpecificMessage } from '../../../../state/ducks/conve import { StateType } from '../../../../state/reducer'; import { useMessageDirection } from '../../../../state/selectors'; import { getMessageQuoteProps } from '../../../../state/selectors/conversations'; +import { hasDetailView } from '../message-item/Message'; import { Quote } from './quote/Quote'; -type Props = { +type Props = hasDetailView & { messageId: string; - isDetailView?: boolean; }; export type MessageQuoteSelectorProps = Pick; diff --git a/ts/components/conversation/message/message-content/MessageReactions.tsx b/ts/components/conversation/message/message-content/MessageReactions.tsx index bd2741e2e..0dbb27353 100644 --- a/ts/components/conversation/message/message-content/MessageReactions.tsx +++ b/ts/components/conversation/message/message-content/MessageReactions.tsx @@ -9,6 +9,7 @@ import { SortedReactionList } from '../../../../types/Reaction'; import { nativeEmojiData } from '../../../../util/emoji'; import { Flex } from '../../../basic/Flex'; import { SessionIcon } from '../../../icon'; +import { hasDetailView } from '../message-item/Message'; import { Reaction, ReactionProps } from '../reactions/Reaction'; import { StyledPopupContainer } from '../reactions/ReactionPopup'; @@ -137,7 +138,7 @@ export type MessageReactsSelectorProps = Pick< 'convoId' | 'serverId' | 'reacts' | 'sortedReacts' >; -type Props = { +type Props = hasDetailView & { messageId: string; hasReactLimit?: boolean; onClick: (emoji: string) => void; @@ -147,7 +148,6 @@ type Props = { inModal?: boolean; onSelected?: (emoji: string) => boolean; noAvatar: boolean; - isDetailView?: boolean; }; export const MessageReactions = (props: Props) => { diff --git a/ts/components/conversation/message/message-content/MessageStatus.tsx b/ts/components/conversation/message/message-content/MessageStatus.tsx index d28a41ef7..eeaa3da59 100644 --- a/ts/components/conversation/message/message-content/MessageStatus.tsx +++ b/ts/components/conversation/message/message-content/MessageStatus.tsx @@ -10,9 +10,9 @@ import { useSelectedIsGroupOrCommunity } from '../../../../state/selectors/selec import { SpacerXS } from '../../../basic/Text'; import { SessionIcon, SessionIconType } from '../../../icon'; import { ExpireTimer } from '../../ExpireTimer'; +import { hasDetailView } from '../message-item/Message'; -type Props = { - isDetailView: boolean; +type Props = hasDetailView & { messageId: string; dataTestId?: string | undefined; }; diff --git a/ts/components/conversation/message/message-content/quote/Quote.tsx b/ts/components/conversation/message/message-content/quote/Quote.tsx index f60aed64c..b5e86717f 100644 --- a/ts/components/conversation/message/message-content/quote/Quote.tsx +++ b/ts/components/conversation/message/message-content/quote/Quote.tsx @@ -4,6 +4,7 @@ import { isEmpty } from 'lodash'; import styled from 'styled-components'; import { useIsMessageSelectionMode } from '../../../../../state/selectors/selectedConversation'; import * as MIME from '../../../../../types/MIME'; +import { hasDetailView } from '../../message-item/Message'; import { QuoteAuthor } from './QuoteAuthor'; import { QuoteIconContainer } from './QuoteIconContainer'; import { QuoteText } from './QuoteText'; @@ -43,14 +44,13 @@ const StyledQuoteTextContent = styled.div` justify-content: center; `; -export type QuoteProps = { +export type QuoteProps = hasDetailView & { author: string; isFromMe: boolean; isIncoming: boolean; referencedMessageNotFound: boolean; text?: string; attachment?: QuotedAttachmentType; - isDetailView?: boolean; onClick?: (e: React.MouseEvent) => void; }; diff --git a/ts/components/conversation/message/message-content/quote/QuoteAuthor.tsx b/ts/components/conversation/message/message-content/quote/QuoteAuthor.tsx index f9c508c4c..4d5ab0a63 100644 --- a/ts/components/conversation/message/message-content/quote/QuoteAuthor.tsx +++ b/ts/components/conversation/message/message-content/quote/QuoteAuthor.tsx @@ -4,9 +4,10 @@ import { useQuoteAuthorName } from '../../../../../hooks/useParamSelector'; import { PubKey } from '../../../../../session/types'; import { useSelectedIsPublic } from '../../../../../state/selectors/selectedConversation'; import { ContactName } from '../../../ContactName'; +import { hasDetailView } from '../../message-item/Message'; import { QuoteProps } from './Quote'; -const StyledQuoteAuthor = styled.div<{ isIncoming: boolean; isDetailView?: boolean }>` +const StyledQuoteAuthor = styled.div` color: ${props => props.isIncoming ? 'var(--message-bubbles-received-text-color)' @@ -23,7 +24,7 @@ const StyledQuoteAuthor = styled.div<{ isIncoming: boolean; isDetailView?: boole } `; -type QuoteAuthorProps = Pick & { isDetailView?: boolean }; +type QuoteAuthorProps = Pick & hasDetailView; export const QuoteAuthor = (props: QuoteAuthorProps) => { const { author, isIncoming, isDetailView } = props; diff --git a/ts/components/conversation/message/message-item/ExpirableReadableMessage.tsx b/ts/components/conversation/message/message-item/ExpirableReadableMessage.tsx index d6e5c7c12..d3696385b 100644 --- a/ts/components/conversation/message/message-item/ExpirableReadableMessage.tsx +++ b/ts/components/conversation/message/message-item/ExpirableReadableMessage.tsx @@ -9,6 +9,7 @@ import { getConversationController } from '../../../../session/conversations'; import { PropsForExpiringMessage, messagesExpired } from '../../../../state/ducks/conversations'; import { getIncrement } from '../../../../util/timer'; import { ExpireTimer } from '../../ExpireTimer'; +import { hasDetailView } from './Message'; import { ReadableMessage, ReadableMessageProps } from './ReadableMessage'; const EXPIRATION_CHECK_MINIMUM = 2000; @@ -81,10 +82,10 @@ const StyledReadableMessage = styled(ReadableMessage)<{ `; export interface ExpirableReadableMessageProps - extends Omit { + extends hasDetailView, + Omit { messageId: string; isControlMessage?: boolean; - isDetailView?: boolean; } function ExpireTimerControlMessage({ diff --git a/ts/components/conversation/message/message-item/GenericReadableMessage.tsx b/ts/components/conversation/message/message-item/GenericReadableMessage.tsx index 2d91f3871..2a2d28a5c 100644 --- a/ts/components/conversation/message/message-item/GenericReadableMessage.tsx +++ b/ts/components/conversation/message/message-item/GenericReadableMessage.tsx @@ -14,6 +14,7 @@ import { } from '../../../../state/selectors/conversations'; import { MessageContentWithStatuses } from '../message-content/MessageContentWithStatus'; import { StyledMessageReactionsContainer } from '../message-content/MessageReactions'; +import { hasDetailView } from './Message'; export type GenericReadableMessageSelectorProps = Pick< MessageRenderingProps, @@ -26,10 +27,9 @@ export type GenericReadableMessageSelectorProps = Pick< | 'isDeleted' >; -type Props = { +type Props = hasDetailView & { messageId: string; ctxMenuID: string; - isDetailView?: boolean; }; const highlightedMessageAnimation = keyframes` @@ -38,11 +38,12 @@ const highlightedMessageAnimation = keyframes` } `; -const StyledReadableMessage = styled.div<{ - selected: boolean; - isRightClicked: boolean; - isDetailView?: boolean; -}>` +const StyledReadableMessage = styled.div< + hasDetailView & { + selected: boolean; + isRightClicked: boolean; + } +>` display: flex; align-items: center; width: 100%; diff --git a/ts/components/conversation/message/message-item/Message.tsx b/ts/components/conversation/message/message-item/Message.tsx index 5d44a40ee..b18400ed2 100644 --- a/ts/components/conversation/message/message-item/Message.tsx +++ b/ts/components/conversation/message/message-item/Message.tsx @@ -10,9 +10,11 @@ import { GenericReadableMessage } from './GenericReadableMessage'; // Same as MIN_WIDTH in ImageGrid.tsx export const MINIMUM_LINK_PREVIEW_IMAGE_WIDTH = THUMBNAIL_SIDE; -type Props = { +/** when the message info panel is displayed for a message, we disable onClick and make some other minor UI changes */ +export type hasDetailView = { isDetailView?: boolean }; + +type Props = hasDetailView & { messageId: string; - isDetailView?: boolean; // when the detail is shown for a message, we disable click and some other stuff }; export const Message = (props: Props) => { From cef59be00536e1095ef4ba5f17b04dbe216afe51 Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Tue, 19 Mar 2024 15:49:13 +1100 Subject: [PATCH 5/6] fix: ellipsis on name rather than pubkey also cleaned up our contexts a little bit --- stylesheets/_modules.scss | 3 ++ ts/components/conversation/ContactName.tsx | 31 +++++++++++++------ ts/components/conversation/ImageGrid.tsx | 6 ++-- .../conversation/SessionLastSeenIndicator.tsx | 6 ++-- .../conversation/SessionMessagesList.tsx | 7 +++-- .../SessionMessagesListContainer.tsx | 15 +++------ .../message-content/MessageContent.tsx | 23 +++++++------- .../MessageContentWithStatus.tsx | 31 ++++++++++--------- .../message/message-content/MessageQuote.tsx | 10 ++++-- .../message-content/MessageReactions.tsx | 7 +++-- .../message/message-content/MessageStatus.tsx | 8 +++-- .../message/message-content/quote/Quote.tsx | 7 ++--- .../message-content/quote/QuoteAuthor.tsx | 12 +++---- .../message-item/ExpirableReadableMessage.tsx | 8 ++--- .../message-item/GenericReadableMessage.tsx | 20 ++++++------ .../message/message-item/Message.tsx | 13 ++------ .../message/message-item/ReadableMessage.tsx | 13 ++------ .../message-info/OverlayMessageInfo.tsx | 9 ++++-- .../ConversationListItem.tsx | 5 ++- .../conversation-list-item/HeaderItem.tsx | 2 +- .../conversation-list-item/MessageItem.tsx | 2 +- .../conversation-list-item/UserItem.tsx | 20 ++++++------ .../menu/ConversationListItemContextMenu.tsx | 8 ++--- ts/components/menu/Menu.tsx | 2 +- ts/components/search/MessageSearchResults.tsx | 11 ++++--- .../ConvoIdContext.tsx | 0 ts/contexts/ScrollToLoadedMessage.tsx | 16 ++++++++++ ts/contexts/isDetailViewContext.tsx | 10 ++++++ ts/contexts/isMessageVisibleContext.tsx | 7 +++++ 29 files changed, 176 insertions(+), 136 deletions(-) rename ts/{components/leftpane/conversation-list-item => contexts}/ConvoIdContext.tsx (100%) create mode 100644 ts/contexts/ScrollToLoadedMessage.tsx create mode 100644 ts/contexts/isDetailViewContext.tsx create mode 100644 ts/contexts/isMessageVisibleContext.tsx diff --git a/stylesheets/_modules.scss b/stylesheets/_modules.scss index 31b7840df..491c846d4 100644 --- a/stylesheets/_modules.scss +++ b/stylesheets/_modules.scss @@ -609,6 +609,9 @@ flex-direction: column; align-items: stretch; overflow: hidden; + max-height: 100%; + display: flex; + gap: 5px; .session-icon-button:first-child { margin-right: var(--margins-sm); diff --git a/ts/components/conversation/ContactName.tsx b/ts/components/conversation/ContactName.tsx index 718467b78..5216371f3 100644 --- a/ts/components/conversation/ContactName.tsx +++ b/ts/components/conversation/ContactName.tsx @@ -1,12 +1,11 @@ -import React from 'react'; import classNames from 'classnames'; -import { CSSProperties } from 'styled-components'; +import React from 'react'; -import { Emojify } from './Emojify'; import { - useNicknameOrProfileNameOrShortenedPubkey, useIsPrivate, + useNicknameOrProfileNameOrShortenedPubkey, } from '../../hooks/useParamSelector'; +import { Emojify } from './Emojify'; type Props = { pubkey: string; @@ -25,12 +24,20 @@ export const ContactName = (props: Props) => { const convoName = useNicknameOrProfileNameOrShortenedPubkey(pubkey); const isPrivate = useIsPrivate(pubkey); const shouldShowProfile = Boolean(convoName || profileName || name); + + const commonStyles = { + 'min-width': 0, + 'text-overflow': 'ellipsis', + overflow: 'hidden', + } as React.CSSProperties; + const styles = ( boldProfileName ? { fontWeight: 'bold', + ...commonStyles, } - : {} + : commonStyles ) as React.CSSProperties; const textProfile = profileName || name || convoName || window.i18n('anonymous'); @@ -39,15 +46,19 @@ export const ContactName = (props: Props) => { className={classNames(prefix, compact && 'compact')} dir="auto" data-testid={`${prefix}__profile-name`} - style={{ textOverflow: 'inherit' }} + style={{ + textOverflow: 'inherit', + display: 'flex', + flexDirection: 'row', + gap: 'var(--margins-xs)', + }} > {shouldShowProfile ? ( - +
- +
) : null} - {shouldShowProfile ? ' ' : null} - {shouldShowPubkey ? {pubkey} : null} + {shouldShowPubkey ?
{pubkey}
: null}
); }; diff --git a/ts/components/conversation/ImageGrid.tsx b/ts/components/conversation/ImageGrid.tsx index 6d47e9b44..d4f9c3ce7 100644 --- a/ts/components/conversation/ImageGrid.tsx +++ b/ts/components/conversation/ImageGrid.tsx @@ -1,4 +1,4 @@ -import React, { useContext } from 'react'; +import React from 'react'; import styled from 'styled-components'; import { @@ -10,10 +10,10 @@ import { isVideoAttachment, } from '../../types/Attachment'; +import { useIsMessageVisible } from '../../contexts/isMessageVisibleContext'; import { useMessageSelected } from '../../state/selectors'; import { THUMBNAIL_SIDE } from '../../types/attachments/VisualAttachment'; import { Image } from './Image'; -import { IsMessageVisibleContext } from './message/message-content/MessageContent'; type Props = { attachments: Array; @@ -46,7 +46,7 @@ const Row = ( totalAttachmentsCount, selected, } = props; - const isMessageVisible = useContext(IsMessageVisibleContext); + const isMessageVisible = useIsMessageVisible(); const moreMessagesOverlay = totalAttachmentsCount > 3; const moreMessagesOverlayText = moreMessagesOverlay ? `+${totalAttachmentsCount - 3}` : undefined; diff --git a/ts/components/conversation/SessionLastSeenIndicator.tsx b/ts/components/conversation/SessionLastSeenIndicator.tsx index 9b65f07cd..31ff6458a 100644 --- a/ts/components/conversation/SessionLastSeenIndicator.tsx +++ b/ts/components/conversation/SessionLastSeenIndicator.tsx @@ -1,9 +1,9 @@ -import React, { useContext, useLayoutEffect } from 'react'; +import React, { useLayoutEffect } from 'react'; import { useSelector } from 'react-redux'; import styled from 'styled-components'; +import { useScrollToLoadedMessage } from '../../contexts/ScrollToLoadedMessage'; import { getQuotedMessageToAnimate } from '../../state/selectors/conversations'; import { isDarkTheme } from '../../state/selectors/theme'; -import { ScrollToLoadedMessageContext } from './SessionMessagesListContainer'; const LastSeenBar = styled.div` height: 2px; @@ -52,7 +52,7 @@ export const SessionLastSeenIndicator = (props: { const darkMode = useSelector(isDarkTheme); // if this unread-indicator is not unique it's going to cause issues const quotedMessageToAnimate = useSelector(getQuotedMessageToAnimate); - const scrollToLoadedMessage = useContext(ScrollToLoadedMessageContext); + const scrollToLoadedMessage = useScrollToLoadedMessage(); const { messageId, didScroll, setDidScroll } = props; diff --git a/ts/components/conversation/SessionMessagesList.tsx b/ts/components/conversation/SessionMessagesList.tsx index 30a0a04bf..3f7f4d42a 100644 --- a/ts/components/conversation/SessionMessagesList.tsx +++ b/ts/components/conversation/SessionMessagesList.tsx @@ -26,9 +26,10 @@ import { Message } from './message/message-item/Message'; import { MessageRequestResponse } from './message/message-item/MessageRequestResponse'; 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 { TimerNotification } from './TimerNotification'; +import { DataExtractionNotification } from './message/message-item/DataExtractionNotification'; import { InteractionNotification } from './message/message-item/InteractionNotification'; function isNotTextboxEvent(e: KeyboardEvent) { @@ -98,7 +99,7 @@ export const SessionMessagesList = (props: { } return ( - <> + {messagesProps.map(messageProps => { const messageId = messageProps.message.props.messageId; const unreadIndicator = messageProps.showUnreadIndicator ? ( @@ -170,6 +171,6 @@ export const SessionMessagesList = (props: { return [, ...componentToMerge]; })} - + ); }; diff --git a/ts/components/conversation/SessionMessagesListContainer.tsx b/ts/components/conversation/SessionMessagesListContainer.tsx index 042b84a41..5da6eccfc 100644 --- a/ts/components/conversation/SessionMessagesListContainer.tsx +++ b/ts/components/conversation/SessionMessagesListContainer.tsx @@ -15,6 +15,10 @@ import { } from '../../state/ducks/conversations'; import { SessionScrollButton } from '../SessionScrollButton'; +import { + ScrollToLoadedMessageContext, + ScrollToLoadedReasons, +} from '../../contexts/ScrollToLoadedMessage'; import { StateType } from '../../state/reducer'; import { getQuotedMessageToAnimate, @@ -31,17 +35,6 @@ export type SessionMessageListProps = { }; 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 & { conversationKey?: string; messagesProps: Array; diff --git a/ts/components/conversation/message/message-content/MessageContent.tsx b/ts/components/conversation/message/message-content/MessageContent.tsx index 04f8b9e1f..143185577 100644 --- a/ts/components/conversation/message/message-content/MessageContent.tsx +++ b/ts/components/conversation/message/message-content/MessageContent.tsx @@ -1,10 +1,13 @@ import classNames from 'classnames'; import { isEmpty } from 'lodash'; 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 { useSelector } from 'react-redux'; 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 { StateType } from '../../../../state/reducer'; import { @@ -19,8 +22,6 @@ import { } from '../../../../state/selectors/conversations'; import { useSelectedIsPrivate } from '../../../../state/selectors/selectedConversation'; import { canDisplayImage } from '../../../../types/Attachment'; -import { ScrollToLoadedMessageContext } from '../../SessionMessagesListContainer'; -import { hasDetailView } from '../message-item/Message'; import { MessageAttachment } from './MessageAttachment'; import { MessageAvatar } from './MessageAvatar'; import { MessageHighlighter } from './MessageHighlighter'; @@ -33,7 +34,7 @@ export type MessageContentSelectorProps = Pick< 'text' | 'direction' | 'timestamp' | 'serverTimestamp' | 'previews' | 'quote' | 'attachments' >; -type Props = hasDetailView & { +type Props = { messageId: string; }; @@ -76,13 +77,13 @@ const StyledMessageOpaqueContent = styled(MessageHighlighter)<{ ${props => props.selected && `box-shadow: var(--drop-shadow);`} `; -export const IsMessageVisibleContext = createContext(false); - const StyledAvatarContainer = styled.div` align-self: flex-end; `; export const MessageContent = (props: Props) => { + const isDetailView = useIsDetailMessageView(); + const [highlight, setHighlight] = useState(false); const [didScroll, setDidScroll] = useState(false); const contentProps = useSelector((state: StateType) => @@ -91,9 +92,9 @@ export const MessageContent = (props: Props) => { const isDeleted = useMessageIsDeleted(props.messageId); const [isMessageVisible, setMessageIsVisible] = useState(false); - const scrollToLoadedMessage = useContext(ScrollToLoadedMessageContext); + const scrollToLoadedMessage = useScrollToLoadedMessage(); const selectedIsPrivate = useSelectedIsPrivate(); - const hideAvatar = useHideAvatarInMsgList(props.messageId, props.isDetailView); + const hideAvatar = useHideAvatarInMsgList(props.messageId, isDetailView); const [imageBroken, setImageBroken] = useState(false); @@ -153,8 +154,7 @@ export const MessageContent = (props: Props) => { const toolTipTitle = moment(serverTimestamp || timestamp).format('llll'); - const isDetailViewAndSupportsAttachmentCarousel = - props.isDetailView && canDisplayImage(attachments); + const isDetailViewAndSupportsAttachmentCarousel = isDetailView && canDisplayImage(attachments); return ( { display: 'flex', flexDirection: 'column', gap: 'var(--margins-xs)', + maxWidth: '100%', }} > @@ -192,7 +193,7 @@ export const MessageContent = (props: Props) => { > {!isDeleted && ( <> - + ; -type Props = hasDetailView & { +type Props = { messageId: string; ctxMenuID: string; dataTestId: string; enableReactions: boolean; }; -const StyledMessageContentContainer = styled.div` +const StyledMessageContentContainer = styled.div<{ isIncoming: boolean; isDetailView: boolean }>` display: flex; flex-direction: column; justify-content: flex-start; @@ -42,17 +42,20 @@ const StyledMessageContentContainer = styled.div (props.isDetailView || props.isIncoming ? 0 : '25%')}; padding-right: ${props => (props.isDetailView || !props.isIncoming ? 0 : '25%')}; width: 100%; + max-width: '100%'; margin-right: var(--margins-md); `; const StyledMessageWithAuthor = styled.div` - max-width: '100%'; + max-width: 100%; display: flex; flex-direction: column; min-width: 0; `; export const MessageContentWithStatuses = (props: Props) => { + const isDetailView = useIsDetailMessageView(); + const contentProps = useSelector((state: StateType) => getMessageContentWithStatusesSelectorProps(state, props.messageId) ); @@ -91,7 +94,7 @@ export const MessageContentWithStatuses = (props: Props) => { } }; - const { messageId, ctxMenuID, isDetailView = false, dataTestId, enableReactions } = props; + const { messageId, ctxMenuID, dataTestId, enableReactions } = props; const [popupReaction, setPopupReaction] = useState(''); if (!contentProps) { @@ -128,21 +131,22 @@ export const MessageContentWithStatuses = (props: Props) => { messageId={messageId} className={classNames('module-message', `module-message--${direction}`)} role={'button'} - isDetailView={isDetailView} onClick={onClickOnMessageOuterContainer} onDoubleClickCapture={onDoubleClickReplyToMessage} dataTestId={dataTestId} > - + {!isDetailView && } - + - + {!isDeleted && ( { setPopupReaction={setPopupReaction} onPopupClick={handlePopupClick} noAvatar={hideAvatar} - isDetailView={isDetailView} /> ) : null}
diff --git a/ts/components/conversation/message/message-content/MessageQuote.tsx b/ts/components/conversation/message/message-content/MessageQuote.tsx index e241d662e..12e20a289 100644 --- a/ts/components/conversation/message/message-content/MessageQuote.tsx +++ b/ts/components/conversation/message/message-content/MessageQuote.tsx @@ -1,6 +1,7 @@ import { isEmpty, toNumber } from 'lodash'; import React from 'react'; import { useSelector } from 'react-redux'; +import { useIsDetailMessageView } from '../../../../contexts/isDetailViewContext'; import { Data } from '../../../../data/data'; import { MessageRenderingProps } from '../../../../models/messageType'; import { ToastUtils } from '../../../../session/utils'; @@ -8,10 +9,9 @@ import { openConversationToSpecificMessage } from '../../../../state/ducks/conve import { StateType } from '../../../../state/reducer'; import { useMessageDirection } from '../../../../state/selectors'; import { getMessageQuoteProps } from '../../../../state/selectors/conversations'; -import { hasDetailView } from '../message-item/Message'; import { Quote } from './quote/Quote'; -type Props = hasDetailView & { +type Props = { messageId: string; }; @@ -20,6 +20,7 @@ export type MessageQuoteSelectorProps = Pick { const selected = useSelector((state: StateType) => getMessageQuoteProps(state, props.messageId)); const direction = useMessageDirection(props.messageId); + const isMessageDetailView = useIsDetailMessageView(); if (!selected || isEmpty(selected)) { return null; @@ -39,6 +40,10 @@ export const MessageQuote = (props: Props) => { event.preventDefault(); event.stopPropagation(); + if (isMessageDetailView) { + return; + } + if (!quote) { ToastUtils.pushOriginalNotFound(); window.log.warn('onQuoteClick: quote not valid'); @@ -98,7 +103,6 @@ export const MessageQuote = (props: Props) => { author={quote.author} referencedMessageNotFound={quoteNotFound} isFromMe={Boolean(quote.isFromMe)} - isDetailView={props.isDetailView} /> ); }; diff --git a/ts/components/conversation/message/message-content/MessageReactions.tsx b/ts/components/conversation/message/message-content/MessageReactions.tsx index 0dbb27353..80e571ab7 100644 --- a/ts/components/conversation/message/message-content/MessageReactions.tsx +++ b/ts/components/conversation/message/message-content/MessageReactions.tsx @@ -1,6 +1,7 @@ import { isEmpty, isEqual } from 'lodash'; import React, { ReactElement, useEffect, useState } from 'react'; import styled from 'styled-components'; +import { useIsDetailMessageView } from '../../../../contexts/isDetailViewContext'; import { useMessageReactsPropsById } from '../../../../hooks/useParamSelector'; import { MessageRenderingProps } from '../../../../models/messageType'; import { REACT_LIMIT } from '../../../../session/constants'; @@ -9,7 +10,6 @@ import { SortedReactionList } from '../../../../types/Reaction'; import { nativeEmojiData } from '../../../../util/emoji'; import { Flex } from '../../../basic/Flex'; import { SessionIcon } from '../../../icon'; -import { hasDetailView } from '../message-item/Message'; import { Reaction, ReactionProps } from '../reactions/Reaction'; import { StyledPopupContainer } from '../reactions/ReactionPopup'; @@ -138,7 +138,7 @@ export type MessageReactsSelectorProps = Pick< 'convoId' | 'serverId' | 'reacts' | 'sortedReacts' >; -type Props = hasDetailView & { +type Props = { messageId: string; hasReactLimit?: boolean; onClick: (emoji: string) => void; @@ -151,6 +151,8 @@ type Props = hasDetailView & { }; export const MessageReactions = (props: Props) => { + const isDetailView = useIsDetailMessageView(); + const { messageId, hasReactLimit = true, @@ -161,7 +163,6 @@ export const MessageReactions = (props: Props) => { inModal = false, onSelected, noAvatar, - isDetailView, } = props; const [reactions, setReactions] = useState([]); diff --git a/ts/components/conversation/message/message-content/MessageStatus.tsx b/ts/components/conversation/message/message-content/MessageStatus.tsx index eeaa3da59..41bcdb42c 100644 --- a/ts/components/conversation/message/message-content/MessageStatus.tsx +++ b/ts/components/conversation/message/message-content/MessageStatus.tsx @@ -5,14 +5,14 @@ import styled from 'styled-components'; import { useMessageExpirationPropsById } from '../../../../hooks/useParamSelector'; import { useMessageStatus } from '../../../../state/selectors'; +import { useIsDetailMessageView } from '../../../../contexts/isDetailViewContext'; import { getMostRecentMessageId } from '../../../../state/selectors/conversations'; import { useSelectedIsGroupOrCommunity } from '../../../../state/selectors/selectedConversation'; import { SpacerXS } from '../../../basic/Text'; import { SessionIcon, SessionIconType } from '../../../icon'; import { ExpireTimer } from '../../ExpireTimer'; -import { hasDetailView } from '../message-item/Message'; -type Props = hasDetailView & { +type Props = { messageId: string; dataTestId?: string | undefined; }; @@ -30,7 +30,9 @@ type Props = hasDetailView & { * - 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) */ -export const MessageStatus = ({ isDetailView, messageId, dataTestId }: Props) => { +export const MessageStatus = ({ messageId, dataTestId }: Props) => { + const isDetailView = useIsDetailMessageView(); + const status = useMessageStatus(messageId); const selected = useMessageExpirationPropsById(messageId); diff --git a/ts/components/conversation/message/message-content/quote/Quote.tsx b/ts/components/conversation/message/message-content/quote/Quote.tsx index b5e86717f..3c42b14ca 100644 --- a/ts/components/conversation/message/message-content/quote/Quote.tsx +++ b/ts/components/conversation/message/message-content/quote/Quote.tsx @@ -4,7 +4,6 @@ import { isEmpty } from 'lodash'; import styled from 'styled-components'; import { useIsMessageSelectionMode } from '../../../../../state/selectors/selectedConversation'; import * as MIME from '../../../../../types/MIME'; -import { hasDetailView } from '../../message-item/Message'; import { QuoteAuthor } from './QuoteAuthor'; import { QuoteIconContainer } from './QuoteIconContainer'; import { QuoteText } from './QuoteText'; @@ -44,7 +43,7 @@ const StyledQuoteTextContent = styled.div` justify-content: center; `; -export type QuoteProps = hasDetailView & { +export type QuoteProps = { author: string; isFromMe: boolean; isIncoming: boolean; @@ -71,7 +70,7 @@ export interface QuotedAttachmentType { export const Quote = (props: QuoteProps) => { const isSelectionMode = useIsMessageSelectionMode(); - const { isIncoming, attachment, text, isDetailView, referencedMessageNotFound, onClick } = props; + const { isIncoming, attachment, text, referencedMessageNotFound, onClick } = props; const [imageBroken, setImageBroken] = useState(false); const handleImageErrorBound = () => { @@ -96,7 +95,7 @@ export const Quote = (props: QuoteProps) => { referencedMessageNotFound={referencedMessageNotFound} /> - + ` +const StyledQuoteAuthor = styled.div<{ isIncoming: boolean }>` color: ${props => props.isIncoming ? 'var(--message-bubbles-received-text-color)' @@ -17,17 +16,18 @@ const StyledQuoteAuthor = styled.div` line-height: 18px; margin-bottom: 2px; overflow-x: hidden; - white-space: ${props => (props.isDetailView ? undefined : 'nowrap')}; + white-space: nowrap; text-overflow: ellipsis; + .module-contact-name { font-weight: bold; } `; -type QuoteAuthorProps = Pick & hasDetailView; +type QuoteAuthorProps = Pick; export const QuoteAuthor = (props: QuoteAuthorProps) => { - const { author, isIncoming, isDetailView } = props; + const { author, isIncoming } = props; const isPublic = useSelectedIsPublic(); const { authorName, isMe } = useQuoteAuthorName(author); @@ -37,7 +37,7 @@ export const QuoteAuthor = (props: QuoteAuthorProps) => { } return ( - + { + extends Omit { messageId: string; isControlMessage?: boolean; } @@ -110,6 +109,7 @@ function ExpireTimerControlMessage({ export const ExpirableReadableMessage = (props: ExpirableReadableMessageProps) => { const selected = useMessageExpirationPropsById(props.messageId); + const isDetailView = useIsDetailMessageView(); const { isControlMessage, onClick, onDoubleClickCapture, role, dataTestId } = props; @@ -136,7 +136,7 @@ export const ExpirableReadableMessage = (props: ExpirableReadableMessageProps) = } = selected; // 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'; return ( diff --git a/ts/components/conversation/message/message-item/GenericReadableMessage.tsx b/ts/components/conversation/message/message-item/GenericReadableMessage.tsx index 2a2d28a5c..ab1254b97 100644 --- a/ts/components/conversation/message/message-item/GenericReadableMessage.tsx +++ b/ts/components/conversation/message/message-item/GenericReadableMessage.tsx @@ -4,6 +4,7 @@ import React, { useCallback, useEffect, useState } from 'react'; import { contextMenu } from 'react-contexify'; import { useSelector } from 'react-redux'; import styled, { keyframes } from 'styled-components'; +import { useIsDetailMessageView } from '../../../../contexts/isDetailViewContext'; import { MessageRenderingProps } from '../../../../models/messageType'; import { getConversationController } from '../../../../session/conversations'; import { StateType } from '../../../../state/reducer'; @@ -14,7 +15,6 @@ import { } from '../../../../state/selectors/conversations'; import { MessageContentWithStatuses } from '../message-content/MessageContentWithStatus'; import { StyledMessageReactionsContainer } from '../message-content/MessageReactions'; -import { hasDetailView } from './Message'; export type GenericReadableMessageSelectorProps = Pick< MessageRenderingProps, @@ -27,7 +27,7 @@ export type GenericReadableMessageSelectorProps = Pick< | 'isDeleted' >; -type Props = hasDetailView & { +type Props = { messageId: string; ctxMenuID: string; }; @@ -38,12 +38,11 @@ const highlightedMessageAnimation = keyframes` } `; -const StyledReadableMessage = styled.div< - hasDetailView & { - selected: boolean; - isRightClicked: boolean; - } ->` +const StyledReadableMessage = styled.div<{ + selected: boolean; + isDetailView: boolean; + isRightClicked: boolean; +}>` display: flex; align-items: center; width: 100%; @@ -65,7 +64,9 @@ const StyledReadableMessage = styled.div< `; export const GenericReadableMessage = (props: Props) => { - const { ctxMenuID, messageId, isDetailView } = props; + const isDetailView = useIsDetailMessageView(); + + const { ctxMenuID, messageId } = props; const [enableReactions, setEnableReactions] = useState(true); @@ -149,7 +150,6 @@ export const GenericReadableMessage = (props: Props) => { diff --git a/ts/components/conversation/message/message-item/Message.tsx b/ts/components/conversation/message/message-item/Message.tsx index b18400ed2..b038a3de8 100644 --- a/ts/components/conversation/message/message-item/Message.tsx +++ b/ts/components/conversation/message/message-item/Message.tsx @@ -10,10 +10,7 @@ import { GenericReadableMessage } from './GenericReadableMessage'; // Same as MIN_WIDTH in ImageGrid.tsx export const MINIMUM_LINK_PREVIEW_IMAGE_WIDTH = THUMBNAIL_SIDE; -/** when the message info panel is displayed for a message, we disable onClick and make some other minor UI changes */ -export type hasDetailView = { isDetailView?: boolean }; - -type Props = hasDetailView & { +type Props = { messageId: string; }; @@ -28,11 +25,5 @@ export const Message = (props: Props) => { return null; } - return ( - - ); + return ; }; diff --git a/ts/components/conversation/message/message-item/ReadableMessage.tsx b/ts/components/conversation/message/message-item/ReadableMessage.tsx index 619563732..0d436d9d8 100644 --- a/ts/components/conversation/message/message-item/ReadableMessage.tsx +++ b/ts/components/conversation/message/message-item/ReadableMessage.tsx @@ -1,14 +1,8 @@ import { debounce, noop } from 'lodash'; -import React, { - AriaRole, - MouseEventHandler, - useCallback, - useContext, - useLayoutEffect, - useState, -} from 'react'; +import React, { AriaRole, MouseEventHandler, useCallback, useLayoutEffect, useState } from 'react'; import { InView } from 'react-intersection-observer'; import { useDispatch, useSelector } from 'react-redux'; +import { useScrollToLoadedMessage } from '../../../../contexts/ScrollToLoadedMessage'; import { Data } from '../../../../data/data'; import { useHasUnread } from '../../../../hooks/useParamSelector'; import { getConversationController } from '../../../../session/conversations'; @@ -28,7 +22,6 @@ import { } from '../../../../state/selectors/conversations'; import { getIsAppFocused } from '../../../../state/selectors/section'; import { useSelectedConversationKey } from '../../../../state/selectors/selectedConversation'; -import { ScrollToLoadedMessageContext } from '../../SessionMessagesListContainer'; export type ReadableMessageProps = { children: React.ReactNode; @@ -95,7 +88,7 @@ export const ReadableMessage = (props: ReadableMessageProps) => { const [didScroll, setDidScroll] = useState(false); const quotedMessageToAnimate = useSelector(getQuotedMessageToAnimate); - const scrollToLoadedMessage = useContext(ScrollToLoadedMessageContext); + const scrollToLoadedMessage = useScrollToLoadedMessage(); // if this unread-indicator is rendered, // we want to scroll here only if the conversation was not opened to a specific message diff --git a/ts/components/conversation/right-panel/overlay/message-info/OverlayMessageInfo.tsx b/ts/components/conversation/right-panel/overlay/message-info/OverlayMessageInfo.tsx index 496babbeb..5e271d796 100644 --- a/ts/components/conversation/right-panel/overlay/message-info/OverlayMessageInfo.tsx +++ b/ts/components/conversation/right-panel/overlay/message-info/OverlayMessageInfo.tsx @@ -10,6 +10,7 @@ import { getMessageInfoId } from '../../../../../state/selectors/conversations'; import { Flex } from '../../../../basic/Flex'; import { Header, HeaderTitle, StyledScrollContainer } from '../components'; +import { IsDetailMessageViewContext } from '../../../../../contexts/isDetailViewContext'; import { Data } from '../../../../../data/data'; import { useRightOverlayMode } from '../../../../../hooks/useUI'; import { @@ -71,9 +72,11 @@ const MessageBody = ({ } return ( - - - + + + + + ); }; diff --git a/ts/components/leftpane/conversation-list-item/ConversationListItem.tsx b/ts/components/leftpane/conversation-list-item/ConversationListItem.tsx index f032d36ca..d1ce3e5f7 100644 --- a/ts/components/leftpane/conversation-list-item/ConversationListItem.tsx +++ b/ts/components/leftpane/conversation-list-item/ConversationListItem.tsx @@ -10,6 +10,10 @@ import { Avatar, AvatarSize } from '../../avatar/Avatar'; import { openConversationWithMessages } from '../../../state/ducks/conversations'; import { updateUserDetailsModal } from '../../../state/ducks/modalDialog'; +import { + ContextConversationProvider, + useConvoIdFromContext, +} from '../../../contexts/ConvoIdContext'; import { useAvatarPath, useConversationUsername, @@ -21,7 +25,6 @@ import { import { isSearching } from '../../../state/selectors/search'; import { useSelectedConversationKey } from '../../../state/selectors/selectedConversation'; import { MemoConversationListItemContextMenu } from '../../menu/ConversationListItemContextMenu'; -import { ContextConversationProvider, useConvoIdFromContext } from './ConvoIdContext'; import { ConversationListItemHeaderItem } from './HeaderItem'; import { MessageItem } from './MessageItem'; diff --git a/ts/components/leftpane/conversation-list-item/HeaderItem.tsx b/ts/components/leftpane/conversation-list-item/HeaderItem.tsx index 1f435bfb1..81d37e82f 100644 --- a/ts/components/leftpane/conversation-list-item/HeaderItem.tsx +++ b/ts/components/leftpane/conversation-list-item/HeaderItem.tsx @@ -2,6 +2,7 @@ import classNames from 'classnames'; import React from 'react'; import { useSelector } from 'react-redux'; import styled from 'styled-components'; +import { useConvoIdFromContext } from '../../../contexts/ConvoIdContext'; import { Data } from '../../../data/data'; import { useActiveAt, @@ -20,7 +21,6 @@ import { isSearching } from '../../../state/selectors/search'; import { getIsMessageSection } from '../../../state/selectors/section'; import { Timestamp } from '../../conversation/Timestamp'; import { SessionIcon } from '../../icon'; -import { useConvoIdFromContext } from './ConvoIdContext'; import { UserItem } from './UserItem'; const NotificationSettingIcon = () => { diff --git a/ts/components/leftpane/conversation-list-item/MessageItem.tsx b/ts/components/leftpane/conversation-list-item/MessageItem.tsx index e4fc216fb..5b0d8bf1a 100644 --- a/ts/components/leftpane/conversation-list-item/MessageItem.tsx +++ b/ts/components/leftpane/conversation-list-item/MessageItem.tsx @@ -2,6 +2,7 @@ import classNames from 'classnames'; import { isEmpty } from 'lodash'; import React from 'react'; import { useSelector } from 'react-redux'; +import { useConvoIdFromContext } from '../../../contexts/ConvoIdContext'; import { useHasUnread, useIsPrivate, @@ -15,7 +16,6 @@ import { assertUnreachable } from '../../../types/sqlSharedTypes'; import { TypingAnimation } from '../../conversation/TypingAnimation'; import { MessageBody } from '../../conversation/message/message-content/MessageBody'; import { SessionIcon } from '../../icon'; -import { useConvoIdFromContext } from './ConvoIdContext'; import { InteractionItem } from './InteractionItem'; export const MessageItem = () => { diff --git a/ts/components/leftpane/conversation-list-item/UserItem.tsx b/ts/components/leftpane/conversation-list-item/UserItem.tsx index ec293ae3c..fc6ad9028 100644 --- a/ts/components/leftpane/conversation-list-item/UserItem.tsx +++ b/ts/components/leftpane/conversation-list-item/UserItem.tsx @@ -1,5 +1,6 @@ import React from 'react'; import { useSelector } from 'react-redux'; +import { useConvoIdFromContext } from '../../../contexts/ConvoIdContext'; import { useConversationRealName, useConversationUsername, @@ -9,7 +10,6 @@ import { import { PubKey } from '../../../session/types'; import { isSearching } from '../../../state/selectors/search'; import { ContactName } from '../../conversation/ContactName'; -import { useConvoIdFromContext } from './ConvoIdContext'; export const UserItem = () => { const conversationId = useConvoIdFromContext(); @@ -36,15 +36,13 @@ export const UserItem = () => { } return ( -
- -
+ ); }; diff --git a/ts/components/menu/ConversationListItemContextMenu.tsx b/ts/components/menu/ConversationListItemContextMenu.tsx index 9e3e0680b..d0e2808e9 100644 --- a/ts/components/menu/ConversationListItemContextMenu.tsx +++ b/ts/components/menu/ConversationListItemContextMenu.tsx @@ -2,10 +2,11 @@ import React from 'react'; import { Item, Menu } from 'react-contexify'; import { useSelector } from 'react-redux'; +import { useConvoIdFromContext } from '../../contexts/ConvoIdContext'; import { useIsPinned, useIsPrivate, useIsPrivateAndFriend } from '../../hooks/useParamSelector'; import { getConversationController } from '../../session/conversations'; +import { isSearching } from '../../state/selectors/search'; import { getIsMessageSection } from '../../state/selectors/section'; -import { useConvoIdFromContext } from '../leftpane/conversation-list-item/ConvoIdContext'; import { SessionContextMenuContainer } from '../SessionContextMenuContainer'; import { AcceptMsgRequestMenuItem, @@ -17,16 +18,15 @@ import { DeclineAndBlockMsgRequestMenuItem, DeclineMsgRequestMenuItem, DeleteMessagesMenuItem, + DeletePrivateConversationMenuItem, InviteContactMenuItem, LeaveGroupOrCommunityMenuItem, MarkAllReadMenuItem, MarkConversationUnreadMenuItem, + NotificationForConvoMenuItem, ShowUserDetailsMenuItem, UnbanMenuItem, - DeletePrivateConversationMenuItem, - NotificationForConvoMenuItem, } from './Menu'; -import { isSearching } from '../../state/selectors/search'; export type PropsContextConversationItem = { triggerId: string; diff --git a/ts/components/menu/Menu.tsx b/ts/components/menu/Menu.tsx index 69cb07118..407dfe289 100644 --- a/ts/components/menu/Menu.tsx +++ b/ts/components/menu/Menu.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { Item, Submenu } from 'react-contexify'; import { useDispatch, useSelector } from 'react-redux'; +import { useConvoIdFromContext } from '../../contexts/ConvoIdContext'; import { useAvatarPath, useConversationUsername, @@ -56,7 +57,6 @@ import { getIsMessageSection } from '../../state/selectors/section'; import { useSelectedConversationKey } from '../../state/selectors/selectedConversation'; import { LocalizerKeys } from '../../types/LocalizerKeys'; import { SessionButtonColor } from '../basic/SessionButton'; -import { useConvoIdFromContext } from '../leftpane/conversation-list-item/ConvoIdContext'; /** Menu items standardized */ diff --git a/ts/components/search/MessageSearchResults.tsx b/ts/components/search/MessageSearchResults.tsx index 13ac83098..e452d194c 100644 --- a/ts/components/search/MessageSearchResults.tsx +++ b/ts/components/search/MessageSearchResults.tsx @@ -1,15 +1,15 @@ import React from 'react'; 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 { openConversationToSpecificMessage } from '../../state/ducks/conversations'; -import { ContactName } from '../conversation/ContactName'; import { Avatar, AvatarSize } from '../avatar/Avatar'; -import { Timestamp } from '../conversation/Timestamp'; import { MessageBodyHighlight } from '../basic/MessageBodyHighlight'; -import { MessageAttributes } from '../../models/messageType'; -import { useConversationUsername, useIsPrivate } from '../../hooks/useParamSelector'; -import { UserUtils } from '../../session/utils'; +import { ContactName } from '../conversation/ContactName'; +import { Timestamp } from '../conversation/Timestamp'; export type MessageResultProps = MessageAttributes & { snippet: string }; @@ -58,6 +58,7 @@ const StyledResultText = styled.div` display: inline-flex; flex-direction: column; align-items: stretch; + min-width: 0; `; const ResultsHeader = styled.div` diff --git a/ts/components/leftpane/conversation-list-item/ConvoIdContext.tsx b/ts/contexts/ConvoIdContext.tsx similarity index 100% rename from ts/components/leftpane/conversation-list-item/ConvoIdContext.tsx rename to ts/contexts/ConvoIdContext.tsx diff --git a/ts/contexts/ScrollToLoadedMessage.tsx b/ts/contexts/ScrollToLoadedMessage.tsx new file mode 100644 index 000000000..3f1e9b80d --- /dev/null +++ b/ts/contexts/ScrollToLoadedMessage.tsx @@ -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); +} diff --git a/ts/contexts/isDetailViewContext.tsx b/ts/contexts/isDetailViewContext.tsx new file mode 100644 index 000000000..e2f31cf84 --- /dev/null +++ b/ts/contexts/isDetailViewContext.tsx @@ -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(false); + +export function useIsDetailMessageView() { + return useContext(IsDetailMessageViewContext); +} diff --git a/ts/contexts/isMessageVisibleContext.tsx b/ts/contexts/isMessageVisibleContext.tsx new file mode 100644 index 000000000..814f10167 --- /dev/null +++ b/ts/contexts/isMessageVisibleContext.tsx @@ -0,0 +1,7 @@ +import { createContext, useContext } from 'react'; + +export const IsMessageVisibleContext = createContext(false); + +export function useIsMessageVisible() { + return useContext(IsMessageVisibleContext); +} From cc4789cd0f766f19355994788a9022f4ea58a170 Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Tue, 26 Mar 2024 15:15:34 +1100 Subject: [PATCH 6/6] fix: small UI fixes --- stylesheets/_modal.scss | 1 - .../message/message-content/MessageContentWithStatus.tsx | 1 + .../message/message-item/MessageRequestResponse.tsx | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/stylesheets/_modal.scss b/stylesheets/_modal.scss index 8446da1a3..43c27d01d 100644 --- a/stylesheets/_modal.scss +++ b/stylesheets/_modal.scss @@ -210,7 +210,6 @@ display: flex; align-items: center; justify-content: center; - margin-right: -20px; // offsets the edit icon button so it's centered p { font-size: $session-font-md; diff --git a/ts/components/conversation/message/message-content/MessageContentWithStatus.tsx b/ts/components/conversation/message/message-content/MessageContentWithStatus.tsx index 79087629d..a49f084f1 100644 --- a/ts/components/conversation/message/message-content/MessageContentWithStatus.tsx +++ b/ts/components/conversation/message/message-content/MessageContentWithStatus.tsx @@ -51,6 +51,7 @@ const StyledMessageWithAuthor = styled.div` display: flex; flex-direction: column; min-width: 0; + gap: var(--margins-xs); `; export const MessageContentWithStatuses = (props: Props) => { diff --git a/ts/components/conversation/message/message-item/MessageRequestResponse.tsx b/ts/components/conversation/message/message-item/MessageRequestResponse.tsx index 4e84b1d7b..23076369c 100644 --- a/ts/components/conversation/message/message-item/MessageRequestResponse.tsx +++ b/ts/components/conversation/message/message-item/MessageRequestResponse.tsx @@ -39,7 +39,7 @@ export const MessageRequestResponse = (props: PropsForMessageRequestResponse) => id={`msg-${messageId}`} > - + );