fix: do not scroll back to unread banner when deleting msgs
Relates #2308
This commit is contained in:
parent
56ee7fe7ac
commit
b638733090
|
@ -1,4 +1,4 @@
|
|||
import React, { useContext, useLayoutEffect, useState } from 'react';
|
||||
import React, { useContext, useLayoutEffect } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import styled from 'styled-components';
|
||||
import { getQuotedMessageToAnimate } from '../../state/selectors/conversations';
|
||||
|
@ -35,19 +35,28 @@ const LastSeenText = styled.div`
|
|||
color: var(--color-last-seen-indicator);
|
||||
`;
|
||||
|
||||
export const SessionLastSeenIndicator = (props: { messageId: string }) => {
|
||||
export const SessionLastSeenIndicator = (props: {
|
||||
messageId: string;
|
||||
didScroll: boolean;
|
||||
setDidScroll: (scroll: boolean) => void;
|
||||
}) => {
|
||||
// if this unread-indicator is not unique it's going to cause issues
|
||||
const [didScroll, setDidScroll] = useState(false);
|
||||
const quotedMessageToAnimate = useSelector(getQuotedMessageToAnimate);
|
||||
|
||||
const scrollToLoadedMessage = useContext(ScrollToLoadedMessageContext);
|
||||
|
||||
// if this unread-indicator is rendered,
|
||||
// we want to scroll here only if the conversation was not opened to a specific message
|
||||
const { messageId, didScroll, setDidScroll } = props;
|
||||
|
||||
/**
|
||||
* If this unread-indicator is rendered, we want to scroll here only if:
|
||||
* 1. the conversation was not opened to a specific message (quoted message)
|
||||
* 2. we already scrolled to this unread banner once for this convo https://github.com/oxen-io/session-desktop/issues/2308
|
||||
*
|
||||
* To achieve 2. we store the didScroll state in the parent and track the last rendered conversation in it.
|
||||
*/
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (!quotedMessageToAnimate && !didScroll) {
|
||||
scrollToLoadedMessage(props.messageId, 'unread-indicator');
|
||||
scrollToLoadedMessage(messageId, 'unread-indicator');
|
||||
setDidScroll(true);
|
||||
} else if (quotedMessageToAnimate) {
|
||||
setDidScroll(true);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useLayoutEffect } from 'react';
|
||||
import React, { useLayoutEffect, useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
// tslint:disable-next-line: no-submodule-imports
|
||||
import useKey from 'react-use/lib/useKey';
|
||||
|
@ -15,6 +15,7 @@ import {
|
|||
import {
|
||||
getOldBottomMessageId,
|
||||
getOldTopMessageId,
|
||||
getSelectedConversationKey,
|
||||
getSortedMessagesTypesOfSelectedConversation,
|
||||
} from '../../state/selectors/conversations';
|
||||
import { GroupUpdateMessage } from './message/message-item/GroupUpdateMessage';
|
||||
|
@ -32,6 +33,8 @@ function isNotTextboxEvent(e: KeyboardEvent) {
|
|||
return (e?.target as any)?.type === undefined;
|
||||
}
|
||||
|
||||
let previousRenderedConvo: string | undefined;
|
||||
|
||||
export const SessionMessagesList = (props: {
|
||||
scrollAfterLoadMore: (
|
||||
messageIdToScrollTo: string,
|
||||
|
@ -43,6 +46,9 @@ export const SessionMessagesList = (props: {
|
|||
onEndPressed: () => void;
|
||||
}) => {
|
||||
const messagesProps = useSelector(getSortedMessagesTypesOfSelectedConversation);
|
||||
const convoKey = useSelector(getSelectedConversationKey);
|
||||
|
||||
const [didScroll, setDidScroll] = useState(false);
|
||||
const oldTopMessageId = useSelector(getOldTopMessageId);
|
||||
const oldBottomMessageId = useSelector(getOldBottomMessageId);
|
||||
|
||||
|
@ -84,12 +90,22 @@ export const SessionMessagesList = (props: {
|
|||
}
|
||||
});
|
||||
|
||||
if (didScroll && previousRenderedConvo !== convoKey) {
|
||||
setDidScroll(false);
|
||||
previousRenderedConvo = convoKey;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{messagesProps.map(messageProps => {
|
||||
const messageId = messageProps.message.props.messageId;
|
||||
const unreadIndicator = messageProps.showUnreadIndicator ? (
|
||||
<SessionLastSeenIndicator key={`unread-indicator-${messageId}`} messageId={messageId} />
|
||||
<SessionLastSeenIndicator
|
||||
key={'unread-indicator'}
|
||||
messageId={messageId}
|
||||
didScroll={didScroll}
|
||||
setDidScroll={setDidScroll}
|
||||
/>
|
||||
) : null;
|
||||
|
||||
const dateBreak =
|
||||
|
@ -100,24 +116,22 @@ export const SessionMessagesList = (props: {
|
|||
messageId={messageId}
|
||||
/>
|
||||
) : null;
|
||||
|
||||
const componentToMerge = [dateBreak, unreadIndicator];
|
||||
if (messageProps.message?.messageType === 'group-notification') {
|
||||
const msgProps = messageProps.message.props as PropsForGroupUpdate;
|
||||
return [<GroupUpdateMessage key={messageId} {...msgProps} />, dateBreak, unreadIndicator];
|
||||
return [<GroupUpdateMessage key={messageId} {...msgProps} />, ...componentToMerge];
|
||||
}
|
||||
|
||||
if (messageProps.message?.messageType === 'group-invitation') {
|
||||
const msgProps = messageProps.message.props as PropsForGroupInvitation;
|
||||
return [<GroupInvitation key={messageId} {...msgProps} />, dateBreak, unreadIndicator];
|
||||
return [<GroupInvitation key={messageId} {...msgProps} />, ...componentToMerge];
|
||||
}
|
||||
|
||||
if (messageProps.message?.messageType === 'message-request-response') {
|
||||
const msgProps = messageProps.message.props as PropsForMessageRequestResponse;
|
||||
|
||||
return [
|
||||
<MessageRequestResponse key={messageId} {...msgProps} />,
|
||||
dateBreak,
|
||||
unreadIndicator,
|
||||
];
|
||||
return [<MessageRequestResponse key={messageId} {...msgProps} />, ...componentToMerge];
|
||||
}
|
||||
|
||||
if (messageProps.message?.messageType === 'data-extraction') {
|
||||
|
@ -125,28 +139,27 @@ export const SessionMessagesList = (props: {
|
|||
|
||||
return [
|
||||
<DataExtractionNotification key={messageId} {...msgProps} />,
|
||||
dateBreak,
|
||||
unreadIndicator,
|
||||
...componentToMerge,
|
||||
];
|
||||
}
|
||||
|
||||
if (messageProps.message?.messageType === 'timer-notification') {
|
||||
const msgProps = messageProps.message.props as PropsForExpirationTimer;
|
||||
|
||||
return [<TimerNotification key={messageId} {...msgProps} />, dateBreak, unreadIndicator];
|
||||
return [<TimerNotification key={messageId} {...msgProps} />, ...componentToMerge];
|
||||
}
|
||||
|
||||
if (messageProps.message?.messageType === 'call-notification') {
|
||||
const msgProps = messageProps.message.props as PropsForCallNotification;
|
||||
|
||||
return [<CallNotification key={messageId} {...msgProps} />, dateBreak, unreadIndicator];
|
||||
return [<CallNotification key={messageId} {...msgProps} />, ...componentToMerge];
|
||||
}
|
||||
|
||||
if (!messageProps) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [<Message messageId={messageId} key={messageId} />, dateBreak, unreadIndicator];
|
||||
return [<Message messageId={messageId} key={messageId} />, ...componentToMerge];
|
||||
})}
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -366,6 +366,7 @@ type FetchedTopMessageResults = {
|
|||
conversationKey: string;
|
||||
messagesProps: Array<MessageModelPropsWithoutConvoProps>;
|
||||
oldTopMessageId: string | null;
|
||||
newMostRecentMessageIdInConversation: string | null;
|
||||
} | null;
|
||||
|
||||
export const fetchTopMessagesForConversation = createAsyncThunk(
|
||||
|
@ -379,6 +380,7 @@ export const fetchTopMessagesForConversation = createAsyncThunk(
|
|||
}): Promise<FetchedTopMessageResults> => {
|
||||
// no need to load more top if we are already at the top
|
||||
const oldestMessage = await Data.getOldestMessageInConversation(conversationKey);
|
||||
const mostRecentMessage = await Data.getLastMessageInConversation(conversationKey);
|
||||
|
||||
if (!oldestMessage || oldestMessage.id === oldTopMessageId) {
|
||||
window.log.info('fetchTopMessagesForConversation: we are already at the top');
|
||||
|
@ -393,6 +395,7 @@ export const fetchTopMessagesForConversation = createAsyncThunk(
|
|||
conversationKey,
|
||||
messagesProps,
|
||||
oldTopMessageId,
|
||||
newMostRecentMessageIdInConversation: mostRecentMessage?.id || null,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
@ -845,7 +848,12 @@ const conversationsSlice = createSlice({
|
|||
return { ...state, areMoreMessagesBeingFetched: false };
|
||||
}
|
||||
// this is called once the messages are loaded from the db for the currently selected conversation
|
||||
const { messagesProps, conversationKey, oldTopMessageId } = action.payload;
|
||||
const {
|
||||
messagesProps,
|
||||
conversationKey,
|
||||
oldTopMessageId,
|
||||
newMostRecentMessageIdInConversation,
|
||||
} = action.payload;
|
||||
// double check that this update is for the shown convo
|
||||
if (conversationKey === state.selectedConversation) {
|
||||
return {
|
||||
|
@ -853,6 +861,7 @@ const conversationsSlice = createSlice({
|
|||
oldTopMessageId,
|
||||
messages: messagesProps,
|
||||
areMoreMessagesBeingFetched: false,
|
||||
mostRecentMessageId: newMostRecentMessageIdInConversation,
|
||||
};
|
||||
}
|
||||
return state;
|
||||
|
|
|
@ -172,11 +172,12 @@ export const hasSelectedConversationIncomingMessages = createSelector(
|
|||
}
|
||||
);
|
||||
|
||||
const getFirstUnreadMessageId = createSelector(getConversations, (state: ConversationsStateType):
|
||||
| string
|
||||
| undefined => {
|
||||
return state.firstUnreadMessageId;
|
||||
});
|
||||
export const getFirstUnreadMessageId = createSelector(
|
||||
getConversations,
|
||||
(state: ConversationsStateType): string | undefined => {
|
||||
return state.firstUnreadMessageId;
|
||||
}
|
||||
);
|
||||
|
||||
export const getConversationHasUnread = createSelector(getFirstUnreadMessageId, unreadId => {
|
||||
return Boolean(unreadId);
|
||||
|
@ -215,10 +216,11 @@ export const getSortedMessagesTypesOfSelectedConversation = createSelector(
|
|||
? messageTimestamp
|
||||
: undefined;
|
||||
|
||||
const common = { showUnreadIndicator: isFirstUnread, showDateBreak };
|
||||
|
||||
if (msg.propsForDataExtractionNotification) {
|
||||
return {
|
||||
showUnreadIndicator: isFirstUnread,
|
||||
showDateBreak,
|
||||
...common,
|
||||
message: {
|
||||
messageType: 'data-extraction',
|
||||
props: { ...msg.propsForDataExtractionNotification, messageId: msg.propsForMessage.id },
|
||||
|
@ -228,8 +230,7 @@ export const getSortedMessagesTypesOfSelectedConversation = createSelector(
|
|||
|
||||
if (msg.propsForMessageRequestResponse) {
|
||||
return {
|
||||
showUnreadIndicator: isFirstUnread,
|
||||
showDateBreak,
|
||||
...common,
|
||||
message: {
|
||||
messageType: 'message-request-response',
|
||||
props: { ...msg.propsForMessageRequestResponse, messageId: msg.propsForMessage.id },
|
||||
|
@ -239,8 +240,7 @@ export const getSortedMessagesTypesOfSelectedConversation = createSelector(
|
|||
|
||||
if (msg.propsForGroupInvitation) {
|
||||
return {
|
||||
showUnreadIndicator: isFirstUnread,
|
||||
showDateBreak,
|
||||
...common,
|
||||
message: {
|
||||
messageType: 'group-invitation',
|
||||
props: { ...msg.propsForGroupInvitation, messageId: msg.propsForMessage.id },
|
||||
|
@ -250,8 +250,7 @@ export const getSortedMessagesTypesOfSelectedConversation = createSelector(
|
|||
|
||||
if (msg.propsForGroupUpdateMessage) {
|
||||
return {
|
||||
showUnreadIndicator: isFirstUnread,
|
||||
showDateBreak,
|
||||
...common,
|
||||
message: {
|
||||
messageType: 'group-notification',
|
||||
props: { ...msg.propsForGroupUpdateMessage, messageId: msg.propsForMessage.id },
|
||||
|
@ -261,8 +260,7 @@ export const getSortedMessagesTypesOfSelectedConversation = createSelector(
|
|||
|
||||
if (msg.propsForTimerNotification) {
|
||||
return {
|
||||
showUnreadIndicator: isFirstUnread,
|
||||
showDateBreak,
|
||||
...common,
|
||||
message: {
|
||||
messageType: 'timer-notification',
|
||||
props: { ...msg.propsForTimerNotification, messageId: msg.propsForMessage.id },
|
||||
|
@ -272,8 +270,7 @@ export const getSortedMessagesTypesOfSelectedConversation = createSelector(
|
|||
|
||||
if (msg.propsForCallNotification) {
|
||||
return {
|
||||
showUnreadIndicator: isFirstUnread,
|
||||
showDateBreak,
|
||||
...common,
|
||||
message: {
|
||||
messageType: 'call-notification',
|
||||
props: {
|
||||
|
|
Loading…
Reference in New Issue