diff --git a/ts/components/conversation/message/message-content/MessageContentWithStatus.tsx b/ts/components/conversation/message/message-content/MessageContentWithStatus.tsx
index 8876b4b93..1406516a6 100644
--- a/ts/components/conversation/message/message-content/MessageContentWithStatus.tsx
+++ b/ts/components/conversation/message/message-content/MessageContentWithStatus.tsx
@@ -95,25 +95,32 @@ export const MessageContentWithStatuses = (props: Props) => {
}
};
- const { messageId, ctxMenuID, isDetailView, dataTestId, enableReactions } = props;
+ const { messageId, ctxMenuID, isDetailView = false, dataTestId, enableReactions } = props;
const [popupReaction, setPopupReaction] = useState('');
if (!contentProps) {
return null;
}
- const { conversationType, direction, isDeleted, isGroup } = contentProps;
+ const { conversationType, direction: _direction, isDeleted, isGroup } = contentProps;
+ // NOTE we want messages on the left in the message detail view regardless of direction
+ const direction = isDetailView ? 'incoming' : _direction;
const isIncoming = direction === 'incoming';
const isPrivate = conversationType === 'private';
- const hideAvatar = isPrivate || direction === 'outgoing';
+ const hideAvatar = isPrivate || direction === 'outgoing' || isDetailView;
const handleMessageReaction = async (emoji: string) => {
await Reactions.sendMessageReaction(messageId, emoji);
};
const handlePopupClick = () => {
- dispatch(updateReactListModal({ reaction: popupReaction, messageId }));
+ dispatch(
+ updateReactListModal({
+ reaction: popupReaction,
+ messageId,
+ })
+ );
};
return (
@@ -138,15 +145,17 @@ export const MessageContentWithStatuses = (props: Props) => {
dataTestId="msg-status-incoming"
messageId={messageId}
isCorrectSide={isIncoming}
+ isDetailView={isDetailView}
/>
-
+ {!isDetailView && }
{!isDeleted && (
{
setPopupReaction={setPopupReaction}
onPopupClick={handlePopupClick}
noAvatar={hideAvatar}
+ isDetailView={isDetailView}
/>
)}
diff --git a/ts/components/conversation/message/message-content/MessageReactions.tsx b/ts/components/conversation/message/message-content/MessageReactions.tsx
index b7567d70e..223c55188 100644
--- a/ts/components/conversation/message/message-content/MessageReactions.tsx
+++ b/ts/components/conversation/message/message-content/MessageReactions.tsx
@@ -3,6 +3,7 @@ import React, { ReactElement, useEffect, useState } from 'react';
import styled from 'styled-components';
import { useMessageReactsPropsById } from '../../../../hooks/useParamSelector';
import { MessageRenderingProps } from '../../../../models/messageType';
+import { REACT_LIMIT } from '../../../../session/constants';
import { useSelectedIsGroup } from '../../../../state/selectors/selectedConversation';
import { SortedReactionList } from '../../../../types/Reaction';
import { nativeEmojiData } from '../../../../util/emoji';
@@ -10,7 +11,6 @@ import { Flex } from '../../../basic/Flex';
import { SessionIcon } from '../../../icon';
import { Reaction, ReactionProps } from '../reactions/Reaction';
import { StyledPopupContainer } from '../reactions/ReactionPopup';
-import { REACT_LIMIT } from '../../../../session/constants';
export const popupXDefault = -81;
export const popupYDefault = -90;
@@ -147,6 +147,7 @@ type Props = {
inModal?: boolean;
onSelected?: (emoji: string) => boolean;
noAvatar: boolean;
+ isDetailView?: boolean;
};
export const MessageReactions = (props: Props) => {
@@ -160,6 +161,7 @@ export const MessageReactions = (props: Props) => {
inModal = false,
onSelected,
noAvatar,
+ isDetailView,
} = props;
const [reactions, setReactions] = useState([]);
@@ -202,10 +204,10 @@ export const MessageReactions = (props: Props) => {
inGroup,
handlePopupX: setPopupX,
handlePopupY: setPopupY,
- onClick,
+ onClick: !isDetailView ? onClick : undefined,
popupReaction,
onSelected,
- handlePopupReaction: setPopupReaction,
+ handlePopupReaction: !isDetailView ? setPopupReaction : undefined,
handlePopupClick: onPopupClick,
};
diff --git a/ts/components/conversation/message/message-content/MessageStatus.tsx b/ts/components/conversation/message/message-content/MessageStatus.tsx
index 9ff95f502..27dc7d86d 100644
--- a/ts/components/conversation/message/message-content/MessageStatus.tsx
+++ b/ts/components/conversation/message/message-content/MessageStatus.tsx
@@ -1,10 +1,11 @@
import React from 'react';
import { MessageRenderingProps } from '../../../../models/messageType';
-import { OutgoingMessageStatus } from './OutgoingMessageStatus';
import { useMessageDirection, useMessageStatus } from '../../../../state/selectors';
+import { OutgoingMessageStatus } from './OutgoingMessageStatus';
type Props = {
isCorrectSide: boolean;
+ isDetailView: boolean;
messageId: string;
dataTestId?: string;
};
@@ -12,15 +13,15 @@ type Props = {
export type MessageStatusSelectorProps = Pick;
export const MessageStatus = (props: Props) => {
- const { isCorrectSide, dataTestId } = props;
+ const { messageId, isCorrectSide, isDetailView, dataTestId } = props;
const direction = useMessageDirection(props.messageId);
const status = useMessageStatus(props.messageId);
- if (!props.messageId) {
+ if (!messageId) {
return null;
}
- if (!isCorrectSide) {
+ if (!isCorrectSide || !isDetailView) {
return null;
}
const isIncoming = direction === 'incoming';
@@ -30,5 +31,5 @@ export const MessageStatus = (props: Props) => {
return null;
}
- return ;
+ return ;
};
diff --git a/ts/components/conversation/message/message-content/OutgoingMessageStatus.tsx b/ts/components/conversation/message/message-content/OutgoingMessageStatus.tsx
index c71d4097e..da03566c6 100644
--- a/ts/components/conversation/message/message-content/OutgoingMessageStatus.tsx
+++ b/ts/components/conversation/message/message-content/OutgoingMessageStatus.tsx
@@ -1,8 +1,9 @@
-import { ipcRenderer } from 'electron';
import React from 'react';
+import { useDispatch } from 'react-redux';
import styled from 'styled-components';
import { LastMessageStatusType } from '../../../../state/ducks/conversations';
import { SessionIcon } from '../../../icon';
+import { showMessageInfoOverlay } from './MessageContextMenu';
const MessageStatusSendingContainer = styled.div`
display: inline-block;
@@ -38,16 +39,24 @@ const MessageStatusRead = ({ dataTestId }: { dataTestId?: string }) => {
);
};
-const MessageStatusError = ({ dataTestId }: { dataTestId?: string }) => {
- const showDebugLog = () => {
- ipcRenderer.send('show-debug-log');
- };
+const MessageStatusError = ({
+ messageId,
+ dataTestId,
+}: {
+ messageId?: string;
+ dataTestId?: string;
+}) => {
+ const dispatch = useDispatch();
return (
{
+ if (messageId) {
+ void showMessageInfoOverlay({ messageId, dispatch });
+ }
+ }}
title={window.i18n('sendFailed')}
>
@@ -57,9 +66,10 @@ const MessageStatusError = ({ dataTestId }: { dataTestId?: string }) => {
export const OutgoingMessageStatus = (props: {
status: LastMessageStatusType | null;
+ messageId?: string;
dataTestId?: string;
}) => {
- const { status, dataTestId } = props;
+ const { status, messageId, dataTestId } = props;
switch (status) {
case 'sending':
return ;
@@ -68,7 +78,7 @@ export const OutgoingMessageStatus = (props: {
case 'read':
return ;
case 'error':
- return ;
+ return ;
default:
return null;
}
diff --git a/ts/components/conversation/message/message-item/GenericReadableMessage.tsx b/ts/components/conversation/message/message-item/GenericReadableMessage.tsx
index 847ea0c10..80cee4094 100644
--- a/ts/components/conversation/message/message-item/GenericReadableMessage.tsx
+++ b/ts/components/conversation/message/message-item/GenericReadableMessage.tsx
@@ -41,11 +41,13 @@ const highlightedMessageAnimation = keyframes`
const StyledReadableMessage = styled.div<{
selected: boolean;
isRightClicked: boolean;
+ isDetailView?: boolean;
}>`
display: flex;
align-items: center;
width: 100%;
letter-spacing: 0.03rem;
+ padding: ${props => (props.isDetailView ? '0' : 'var(--margins-xs) var(--margins-lg) 0')};
&.message-highlighted {
animation: ${highlightedMessageAnimation} 1s ease-in-out;
@@ -151,6 +153,7 @@ export const GenericReadableMessage = (props: Props) => {
return (
`
+const StyledReaction = styled.button<{
+ selected: boolean;
+ inModal: boolean;
+ showCount: boolean;
+ hasOnClick?: boolean;
+}>`
display: flex;
justify-content: ${props => (props.showCount ? 'flex-start' : 'center')};
align-items: center;
@@ -29,6 +37,8 @@ const StyledReaction = styled.button<{ selected: boolean; inModal: boolean; show
span {
width: 100%;
}
+
+ ${props => !props.hasOnClick && 'cursor: not-allowed;'}
`;
const StyledReactionContainer = styled.div<{
@@ -46,7 +56,7 @@ export type ReactionProps = {
inGroup: boolean;
handlePopupX: (x: number) => void;
handlePopupY: (y: number) => void;
- onClick: (emoji: string) => void;
+ onClick?: (emoji: string) => void;
popupReaction?: string;
onSelected?: (emoji: string) => boolean;
handlePopupReaction?: (emoji: string) => void;
@@ -69,6 +79,7 @@ export const Reaction = (props: ReactionProps): ReactElement => {
handlePopupClick,
} = props;
+ const rightOverlayMode = useSelector(getRightOverlayMode);
const isMessageSelection = useIsMessageSelectionMode();
const reactionsMap = (reactions && Object.fromEntries(reactions)) || {};
const senders = reactionsMap[emoji]?.senders || [];
@@ -76,7 +87,7 @@ export const Reaction = (props: ReactionProps): ReactElement => {
const showCount = count !== undefined && (count > 1 || inGroup);
const reactionRef = useRef(null);
- const { docX, elW } = useMouse(reactionRef);
+ const { docX: _docX, elW } = useMouse(reactionRef);
const gutterWidth = 380; // TODOLATER make this a variable which can be shared in CSS and JS
const tooltipMidPoint = POPUP_WIDTH / 2; // px
@@ -96,7 +107,9 @@ export const Reaction = (props: ReactionProps): ReactElement => {
const handleReactionClick = () => {
if (!isMessageSelection) {
- onClick(emoji);
+ if (onClick) {
+ onClick(emoji);
+ }
}
};
@@ -107,12 +120,28 @@ export const Reaction = (props: ReactionProps): ReactElement => {
selected={selected()}
inModal={inModal}
onClick={handleReactionClick}
+ hasOnClick={Boolean(onClick)}
onMouseEnter={() => {
if (inGroup && !isMessageSelection) {
- const { innerWidth: windowWidth } = window;
+ const { innerWidth } = window;
+ let windowWidth = innerWidth;
+
+ let docX = _docX;
+ // if the right panel is open we may need to show a reaction tooltip relative to it
+ if (rightOverlayMode && rightOverlayMode.type === 'message_info') {
+ const rightPanelWidth = Number(THEME_GLOBALS['--right-panel-width'].split('px')[0]);
+
+ // we need to check that the reaction we are hovering over is inside of the right panel and not in the messages list
+ if (docX > windowWidth - rightPanelWidth) {
+ // make the values relative to the right panel
+ docX = docX - windowWidth + rightPanelWidth;
+ windowWidth = rightPanelWidth;
+ }
+ }
+
if (handlePopupReaction) {
// overflow on far right means we shift left
- if (docX + elW + tooltipMidPoint > windowWidth) {
+ if (docX + elW + tooltipMidPoint > innerWidth) {
handlePopupX(Math.abs(popupXDefault) * 1.5 * -1);
setTooltipPosition('right');
// overflow onto conversations means we lock to the right
diff --git a/ts/state/selectors/section.ts b/ts/state/selectors/section.ts
index 07cc2f042..f87d540e5 100644
--- a/ts/state/selectors/section.ts
+++ b/ts/state/selectors/section.ts
@@ -1,7 +1,7 @@
import { createSelector } from '@reduxjs/toolkit';
import { SessionSettingCategory } from '../../components/settings/SessionSettings';
-import { OverlayMode, SectionStateType, SectionType } from '../ducks/section';
+import { OverlayMode, RightOverlayMode, SectionStateType, SectionType } from '../ducks/section';
import { StateType } from '../reducer';
export const getSection = (state: StateType): SectionStateType => state.section;
@@ -31,6 +31,10 @@ export const getOverlayMode = createSelector(
(state: SectionStateType): OverlayMode | undefined => state.overlayMode
);
+export const getRightOverlayMode = (state: StateType): RightOverlayMode | undefined => {
+ return state.section.rightOverlayMode;
+};
+
export const getIsMessageRequestOverlayShown = (state: StateType) => {
const focusedSection = getFocusedSection(state);
const overlayMode = getOverlayMode(state);
diff --git a/ts/themes/globals.tsx b/ts/themes/globals.tsx
index a5d1d1339..6727a8171 100644
--- a/ts/themes/globals.tsx
+++ b/ts/themes/globals.tsx
@@ -36,6 +36,7 @@ export type ThemeGlobals = {
'--main-view-header-height': string;
'--composition-container-height': string;
'--search-input-height': string;
+ '--toggle-width': string;
/* Animations */
'--default-duration': string;
@@ -83,6 +84,12 @@ export type ThemeGlobals = {
/* Also used for FileDropZone */
/* Used for Quote References Not Found */
'--message-link-preview-background-color': string;
+
+ /* Right Panel */
+ '--right-panel-width': string;
+ '--right-panel-height': string;
+ '--right-panel-attachment-width': string;
+ '--right-panel-attachment-height': string;
};
// These are only set once in the global style (at root).
@@ -115,6 +122,7 @@ export const THEME_GLOBALS: ThemeGlobals = {
'--main-view-header-height': '68px',
'--composition-container-height': '60px',
'--search-input-height': '34px',
+ '--toggle-width': '51px',
'--default-duration': '0.25s',
@@ -149,6 +157,11 @@ export const THEME_GLOBALS: ThemeGlobals = {
'--avatar-border-color': 'var(--transparent-color)',
'--message-link-preview-background-color': `rgba(${hexColorToRGB(COLORS.BLACK)}, 0.06)`,
+
+ '--right-panel-width': '420px',
+ '--right-panel-height': '100%',
+ '--right-panel-attachment-width': '350px',
+ '--right-panel-attachment-height': '350px',
};
// These should only be needed for the global style (at root).