fixup selected message in redux logic

This commit is contained in:
Audric Ackermann 2021-07-14 11:30:31 +10:00
parent 787e3a32d0
commit 4ca5a4f093
No known key found for this signature in database
GPG Key ID: 999F434D76324AD4
4 changed files with 60 additions and 71 deletions

View File

@ -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<Props, State> {
if (target.className === 'text-selectable' || window.contextMenuShown) {
return;
}
if (id) {
window.inboxStore?.dispatch(toggleSelectedMessageId(id));
}
}}
>
{this.renderAuthor()}

View File

@ -170,53 +170,10 @@ class SessionMessagesListInner extends React.Component<Props, State> {
);
}
private displayUnreadBannerIndex(messages: Array<SortedMessageModelProps>) {
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<Props, State> {
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 = (
<SessionLastSeenIndicator
count={displayUnreadBannerIndex + 1} // count is used for the 118n of the string
show={showUnreadIndicator}
key={`unread-indicator-${messageProps.propsForMessage.id}`}
/>
);
currentMessageIndex = currentMessageIndex + 1;
if (groupNotificationProps) {
return (
<React.Fragment key={messageProps.propsForMessage.id}>
<GroupNotification {...groupNotificationProps} />
<React.Fragment>
<GroupNotification
key={messageProps.propsForMessage.id}
{...groupNotificationProps}
/>
{unreadIndicator}
</React.Fragment>
);

View File

@ -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(

View File

@ -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<MessageModelProps>
const updateFirstMessageOfSeriesAndUnread = (
messageModelsProps: Array<SortedMessageModelProps>
): Array<SortedMessageModelProps> => {
// 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<SortedMessageModelProps> = [];
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<SortedMessageModelProps>;
};
const getFirstMessageUnreadIndex = (messages: Array<SortedMessageModelProps>) => {
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<FetchedMessageResults> => {
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;