From 4ca5a4f09394d2d4297428eaff47c8ea1f6b4599 Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Wed, 14 Jul 2021 11:30:31 +1000 Subject: [PATCH] fixup selected message in redux logic --- ts/components/conversation/Message.tsx | 6 +- .../conversation/SessionMessagesList.tsx | 59 +++--------------- ts/components/session/icon/SessionIcon.tsx | 4 -- ts/state/ducks/conversations.ts | 62 ++++++++++++++++--- 4 files changed, 60 insertions(+), 71 deletions(-) diff --git a/ts/components/conversation/Message.tsx b/ts/components/conversation/Message.tsx index 8f6613207..a1e5e3e6a 100644 --- a/ts/components/conversation/Message.tsx +++ b/ts/components/conversation/Message.tsx @@ -47,7 +47,7 @@ import { getSelectedMessage } from '../../state/selectors/search'; import { connect } from 'react-redux'; import { StateType } from '../../state/reducer'; import { getSelectedMessageIds } from '../../state/selectors/conversations'; -import { showMessageDetailsView } from '../../state/ducks/conversations'; +import { showMessageDetailsView, toggleSelectedMessageId } from '../../state/ducks/conversations'; // Same as MIN_WIDTH in ImageGrid.tsx const MINIMUM_LINK_PREVIEW_IMAGE_WIDTH = 200; @@ -813,10 +813,6 @@ class MessageInner extends React.PureComponent { if (target.className === 'text-selectable' || window.contextMenuShown) { return; } - - if (id) { - window.inboxStore?.dispatch(toggleSelectedMessageId(id)); - } }} > {this.renderAuthor()} diff --git a/ts/components/session/conversation/SessionMessagesList.tsx b/ts/components/session/conversation/SessionMessagesList.tsx index 6043972e1..cdda6f551 100644 --- a/ts/components/session/conversation/SessionMessagesList.tsx +++ b/ts/components/session/conversation/SessionMessagesList.tsx @@ -170,53 +170,10 @@ class SessionMessagesListInner extends React.Component { ); } - private displayUnreadBannerIndex(messages: Array) { - const { conversation } = this.props; - if (!conversation) { - return -1; - } - if (conversation.unreadCount === 0) { - return -1; - } - // conversation.unreadCount is the number of messages we incoming we did not read yet. - // also, unreacCount is updated only when the conversation is marked as read. - // So we can have an unreadCount for the conversation not correct based on the real number of unread messages. - // some of the messages we have in "messages" are ones we sent ourself (or from another device). - // those messages should not be counted to display the unread banner. - - let findFirstUnreadIndex = -1; - let incomingMessagesSoFar = 0; - const { unreadCount } = conversation; - - // Basically, count the number of incoming messages from the most recent one. - for (let index = 0; index <= messages.length - 1; index++) { - const message = messages[index]; - if (message.propsForMessage.direction === 'incoming') { - incomingMessagesSoFar++; - // message.attributes.unread is !== undefined if the message is unread. - if ( - message.propsForMessage.isUnread !== undefined && - incomingMessagesSoFar >= unreadCount - ) { - findFirstUnreadIndex = index; - break; - } - } - } - - // - if (findFirstUnreadIndex === -1 && conversation.unreadCount >= 0) { - return conversation.unreadCount - 1; - } - return findFirstUnreadIndex; - } - private renderMessages() { const { selectedMessages, messagesProps } = this.props; const multiSelectMode = Boolean(selectedMessages.length); - let currentMessageIndex = 0; let playableMessageIndex = 0; - const displayUnreadBannerIndex = this.displayUnreadBannerIndex(messagesProps); return ( <> @@ -228,27 +185,25 @@ class SessionMessagesListInner extends React.Component { const groupNotificationProps = messageProps.propsForGroupNotification; - // IF there are some unread messages - // AND we found the last read message + // IF we found the last read message // AND we are not scrolled all the way to the bottom // THEN, show the unread banner for the current message const showUnreadIndicator = - displayUnreadBannerIndex >= 0 && - currentMessageIndex === displayUnreadBannerIndex && - this.getScrollOffsetBottomPx() !== 0; + Boolean(messageProps.firstUnread) && this.getScrollOffsetBottomPx() !== 0; const unreadIndicator = ( ); - currentMessageIndex = currentMessageIndex + 1; if (groupNotificationProps) { return ( - - + + {unreadIndicator} ); diff --git a/ts/components/session/icon/SessionIcon.tsx b/ts/components/session/icon/SessionIcon.tsx index 1e473d1bb..e97ab2363 100644 --- a/ts/components/session/icon/SessionIcon.tsx +++ b/ts/components/session/icon/SessionIcon.tsx @@ -104,10 +104,6 @@ const animation = (props: { return css``; } - if (props.glowDuration === 10) { - console.warn('scake', props); - } - if (props.glowDuration !== undefined && props.glowStartDelay !== undefined && props.iconColor) { return css` ${glow( diff --git a/ts/state/ducks/conversations.ts b/ts/state/ducks/conversations.ts index 368029324..8106b7385 100644 --- a/ts/state/ducks/conversations.ts +++ b/ts/state/ducks/conversations.ts @@ -275,23 +275,27 @@ async function getMessages( if (conversation.isPrivate()) { return sortedMessageProps; } - return updateFirstMessageOfSeries(sortedMessageProps); + return updateFirstMessageOfSeriesAndUnread(sortedMessageProps); } export type SortedMessageModelProps = MessageModelProps & { firstMessageOfSeries: boolean; + firstUnread?: boolean; }; -const updateFirstMessageOfSeries = ( - messageModelsProps: Array +const updateFirstMessageOfSeriesAndUnread = ( + messageModelsProps: Array ): Array => { // messages are got from the more recent to the oldest, so we need to check if // the next messages in the list is still the same author. // The message is the first of the series if the next message is not from the same author const sortedMessageProps: Array = []; + const firstUnreadIndex = getFirstMessageUnreadIndex(messageModelsProps); + for (let i = 0; i < messageModelsProps.length; i++) { // Handle firstMessageOfSeries for conditional avatar rendering let firstMessageOfSeries = true; + let firstUnread = false; const currentSender = messageModelsProps[i].propsForMessage?.authorPhoneNumber; const nextSender = i < messageModelsProps.length - 1 @@ -300,8 +304,11 @@ const updateFirstMessageOfSeries = ( if (i >= 0 && currentSender === nextSender) { firstMessageOfSeries = false; } + if (i === firstUnreadIndex) { + firstUnread = true; + } - sortedMessageProps.push({ ...messageModelsProps[i], firstMessageOfSeries }); + sortedMessageProps.push({ ...messageModelsProps[i], firstMessageOfSeries, firstUnread }); } return sortedMessageProps; }; @@ -311,6 +318,27 @@ type FetchedMessageResults = { messagesProps: Array; }; +const getFirstMessageUnreadIndex = (messages: Array) => { + if (!messages || messages.length === 0) { + return -1; + } + + // iterate over the incoming messages from the oldest one. the first one with isUnread !== undefined is our first unread + for (let index = messages.length - 1; index > 0; index--) { + const message = messages[index]; + if ( + message.propsForMessage.direction === 'incoming' && + message.propsForMessage.isUnread === true + ) { + console.warn('message.propsForMessage', message.propsForMessage); + + return index; + } + } + + return -1; +}; + export const fetchMessagesForConversation = createAsyncThunk( 'messages/fetchByConversationKey', async ({ @@ -322,15 +350,25 @@ export const fetchMessagesForConversation = createAsyncThunk( }): Promise => { const beforeTimestamp = Date.now(); const messagesProps = await getMessages(conversationKey, count); + const firstUnreadIndex = getFirstMessageUnreadIndex(messagesProps); const afterTimestamp = Date.now(); const time = afterTimestamp - beforeTimestamp; window?.log?.info(`Loading ${messagesProps.length} messages took ${time}ms to load.`); - const mapped = messagesProps.map(m => { + const mapped = messagesProps.map((m, index) => { + if (index === firstUnreadIndex) { + console.warn('fullfuled firstUnreadIndex', firstUnreadIndex); + return { + ...m, + firstMessageOfSeries: true, + firstUnread: true, + }; + } return { ...m, firstMessageOfSeries: true, + firstUnread: false, }; }); return { @@ -397,7 +435,7 @@ function handleMessageAdded( if (convo) { const sortedMessage = sortMessages(messagesWithNewMessage, isPublic); - const updatedWithFirstMessageOfSeries = updateFirstMessageOfSeries(sortedMessage); + const updatedWithFirstMessageOfSeries = updateFirstMessageOfSeriesAndUnread(sortedMessage); return { ...state, @@ -425,7 +463,7 @@ function handleMessageChanged(state: ConversationsStateType, payload: MessageMod const isPublic = convo?.isPublic || false; // reorder the messages depending on the timestamp (we might have an updated serverTimestamp now) const sortedMessage = sortMessages(editedMessages, isPublic); - const updatedWithFirstMessageOfSeries = updateFirstMessageOfSeries(sortedMessage); + const updatedWithFirstMessageOfSeries = updateFirstMessageOfSeriesAndUnread(sortedMessage); return { ...state, @@ -465,7 +503,7 @@ function handleMessageExpiredOrDeleted( ...state.messages.slice(messageInStoreIndex + 1), ]; - const updatedWithFirstMessageOfSeries = updateFirstMessageOfSeries(editedMessages); + const updatedWithFirstMessageOfSeries = updateFirstMessageOfSeriesAndUnread(editedMessages); // FIXME two other thing we have to do: // * update the last message text if the message deleted was the last one @@ -534,9 +572,12 @@ const conversationsSlice = createSlice({ const index = state.selectedMessageIds.findIndex(id => id === action.payload); if (index === -1) { - return { ...state, selectedMessageIds: [...state.selectedMessageIds, action.payload] }; + state.selectedMessageIds = [...state.selectedMessageIds, action.payload]; + } else { + state.selectedMessageIds.splice(index, 1); } - return { ...state, selectedMessageIds: state.selectedMessageIds.splice(index, 1) }; + + return state; }, resetSelectedMessageIds(state: ConversationsStateType) { return { ...state, selectedMessageIds: [] }; @@ -710,4 +751,5 @@ export const { closeRightPanel, addMessageIdToSelection, resetSelectedMessageIds, + toggleSelectedMessageId, } = actions;