fix unread banner position when first unread is visible

pull/1753/head
audric 4 years ago
parent c2b5ac68d6
commit c8f0150aaf

@ -341,11 +341,15 @@
window.setMediaPermissions(!value); window.setMediaPermissions(!value);
}; };
Whisper.Notifications.on('click', (id, messageId) => { Whisper.Notifications.on('click', async (id, messageId) => {
window.showWindow(); window.showWindow();
if (id) { if (id) {
const firstUnreadIdOnOpen = await window.Signal.Data.getFirstUnreadMessageIdInConversation(
id
);
window.inboxStore.dispatch( window.inboxStore.dispatch(
window.actionsCreators.openConversationExternal({ id, messageId }) window.actionsCreators.openConversationExternal({ id, messageId, firstUnreadIdOnOpen })
); );
} else { } else {
appView.openInbox({ appView.openInbox({

@ -1,4 +1,4 @@
import React from 'react'; import React, { useCallback } from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import { isEmpty } from 'lodash'; import { isEmpty } from 'lodash';
import { contextMenu } from 'react-contexify'; import { contextMenu } from 'react-contexify';
@ -26,6 +26,7 @@ import { SessionIcon, SessionIconSize, SessionIconType } from './session/icon';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import { SectionType } from '../state/ducks/section'; import { SectionType } from '../state/ducks/section';
import { getFocusedSection } from '../state/selectors/section'; import { getFocusedSection } from '../state/selectors/section';
import { getFirstUnreadMessageIdInConversation } from '../data/data';
// tslint:disable-next-line: no-empty-interface // tslint:disable-next-line: no-empty-interface
export interface ConversationListItemProps extends ReduxConversationType {} export interface ConversationListItemProps extends ReduxConversationType {}
@ -240,13 +241,16 @@ const ConversationListItem = (props: Props) => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const openConvo = useCallback(async () => {
const firstUnreadIdOnOpen = await getFirstUnreadMessageIdInConversation(conversationId);
dispatch(openConversationExternal({ id: conversationId, firstUnreadIdOnOpen }));
}, [conversationId]);
return ( return (
<div key={key}> <div key={key}>
<div <div
role="button" role="button"
onClick={() => { onClick={openConvo}
dispatch(openConversationExternal({ id: conversationId }));
}}
onContextMenu={(e: any) => { onContextMenu={(e: any) => {
contextMenu.show({ contextMenu.show({
id: triggerId, id: triggerId,

@ -94,7 +94,13 @@ export const MessageSearchResult = (props: Props) => {
<div <div
role="button" role="button"
onClick={() => { onClick={() => {
dispatch(openConversationExternal({ id: conversationId, messageId })); dispatch(
openConversationExternal({
id: conversationId,
messageId,
firstUnreadIdOnOpen: undefined,
})
);
}} }}
className={classNames( className={classNames(
'module-message-search-result', 'module-message-search-result',

@ -11,6 +11,7 @@ import { updateUserDetailsModal } from '../state/ducks/modalDialog';
import { openConversationExternal } from '../state/ducks/conversations'; import { openConversationExternal } from '../state/ducks/conversations';
// tslint:disable-next-line: no-submodule-imports // tslint:disable-next-line: no-submodule-imports
import useKey from 'react-use/lib/useKey'; import useKey from 'react-use/lib/useKey';
import { getFirstUnreadMessageIdInConversation } from '../data/data';
type Props = { type Props = {
conversationId: string; conversationId: string;
authorAvatarPath?: string; authorAvatarPath?: string;
@ -33,8 +34,11 @@ export const UserDetailsDialog = (props: Props) => {
convo.id, convo.id,
ConversationTypeEnum.PRIVATE ConversationTypeEnum.PRIVATE
); );
const firstUnreadIdOnOpen = await getFirstUnreadMessageIdInConversation(conversation.id);
window.inboxStore?.dispatch(openConversationExternal({ id: conversation.id })); window.inboxStore?.dispatch(
openConversationExternal({ id: conversation.id, firstUnreadIdOnOpen })
);
closeDialog(); closeDialog();
} }

@ -39,7 +39,10 @@ import { ClickToTrustSender } from './message/ClickToTrustSender';
import { getMessageById } from '../../data/data'; import { getMessageById } from '../../data/data';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { StateType } from '../../state/reducer'; import { StateType } from '../../state/reducer';
import { getSelectedMessageIds } from '../../state/selectors/conversations'; import {
getQuotedMessageToAnimate,
getSelectedMessageIds,
} from '../../state/selectors/conversations';
import { import {
messageExpired, messageExpired,
showLightBox, showLightBox,
@ -64,7 +67,10 @@ interface State {
const EXPIRATION_CHECK_MINIMUM = 2000; const EXPIRATION_CHECK_MINIMUM = 2000;
const EXPIRED_DELAY = 600; const EXPIRED_DELAY = 600;
type Props = MessageRegularProps & { selectedMessages: Array<string> }; type Props = MessageRegularProps & {
selectedMessages: Array<string>;
quotedMessageToAnimate: string | undefined;
};
const onClickAttachment = async (onClickProps: { const onClickAttachment = async (onClickProps: {
attachment: AttachmentTypeWithPath; attachment: AttachmentTypeWithPath;
@ -570,14 +576,7 @@ class MessageInner extends React.PureComponent<Props, State> {
// tslint:disable-next-line: cyclomatic-complexity // tslint:disable-next-line: cyclomatic-complexity
public render() { public render() {
const { const { direction, id, conversationType, isUnread, selectedMessages } = this.props;
direction,
id,
multiSelectMode,
conversationType,
isUnread,
selectedMessages,
} = this.props;
const { expired, expiring } = this.state; const { expired, expiring } = this.state;
if (expired) { if (expired) {
@ -601,7 +600,7 @@ class MessageInner extends React.PureComponent<Props, State> {
divClasses.push('public-chat-message-wrapper'); divClasses.push('public-chat-message-wrapper');
} }
if (this.props.isQuotedMessageToAnimate) { if (this.props.quotedMessageToAnimate === this.props.id) {
divClasses.push('flash-green-once'); divClasses.push('flash-green-once');
} }
@ -851,6 +850,7 @@ class MessageInner extends React.PureComponent<Props, State> {
const mapStateToProps = (state: StateType) => { const mapStateToProps = (state: StateType) => {
return { return {
selectedMessages: getSelectedMessageIds(state), selectedMessages: getSelectedMessageIds(state),
quotedMessageToAnimate: getQuotedMessageToAnimate(state),
}; };
}; };

@ -15,7 +15,7 @@ export const ReadableMessage = (props: ReadableMessageProps) => {
useFocus(onChange); useFocus(onChange);
return ( return (
<InView {...props} as="div" threshold={1} delay={200} triggerOnce={false}> <InView {...props} as="div" threshold={0.5} delay={20} triggerOnce={false}>
{props.children} {props.children}
</InView> </InView>
); );

@ -26,6 +26,7 @@ import autoBind from 'auto-bind';
import { onsNameRegex } from '../../session/snode_api/SNodeAPI'; import { onsNameRegex } from '../../session/snode_api/SNodeAPI';
import { SNodeAPI } from '../../session/snode_api'; import { SNodeAPI } from '../../session/snode_api';
import { clearSearch, search, updateSearchTerm } from '../../state/ducks/search'; import { clearSearch, search, updateSearchTerm } from '../../state/ducks/search';
import { getFirstUnreadMessageIdInConversation } from '../../data/data';
export interface Props { export interface Props {
searchTerm: string; searchTerm: string;
@ -319,7 +320,11 @@ export class LeftPaneMessageSection extends React.Component<Props, State> {
pubkeyorOns, pubkeyorOns,
ConversationTypeEnum.PRIVATE ConversationTypeEnum.PRIVATE
); );
window.inboxStore?.dispatch(openConversationExternal({ id: pubkeyorOns })); const firstUnreadIdOnOpen = await getFirstUnreadMessageIdInConversation(pubkeyorOns);
window.inboxStore?.dispatch(
openConversationExternal({ id: pubkeyorOns, firstUnreadIdOnOpen })
);
this.handleToggleOverlay(undefined); this.handleToggleOverlay(undefined);
} else { } else {
// this might be an ONS, validate the regex first // this might be an ONS, validate the regex first
@ -339,7 +344,12 @@ export class LeftPaneMessageSection extends React.Component<Props, State> {
resolvedSessionID, resolvedSessionID,
ConversationTypeEnum.PRIVATE ConversationTypeEnum.PRIVATE
); );
window.inboxStore?.dispatch(openConversationExternal({ id: resolvedSessionID }));
const firstUnreadIdOnOpen = await getFirstUnreadMessageIdInConversation(resolvedSessionID);
window.inboxStore?.dispatch(
openConversationExternal({ id: resolvedSessionID, firstUnreadIdOnOpen })
);
this.handleToggleOverlay(undefined); this.handleToggleOverlay(undefined);
} catch (e) { } catch (e) {
window?.log?.warn('failed to resolve ons name', pubkeyorOns, e); window?.log?.warn('failed to resolve ons name', pubkeyorOns, e);

@ -30,7 +30,7 @@ import {
PropsForDataExtractionNotification, PropsForDataExtractionNotification,
QuoteClickOptions, QuoteClickOptions,
} from '../../../models/messageType'; } from '../../../models/messageType';
import { getFirstUnreadMessageIdInConversation, getMessagesBySentAt } from '../../../data/data'; import { getMessagesBySentAt } from '../../../data/data';
import autoBind from 'auto-bind'; import autoBind from 'auto-bind';
import { ConversationTypeEnum } from '../../../models/conversation'; import { ConversationTypeEnum } from '../../../models/conversation';
import { DataExtractionNotification } from '../../conversation/DataExtractionNotification'; import { DataExtractionNotification } from '../../conversation/DataExtractionNotification';
@ -44,9 +44,9 @@ import {
getSelectedConversationKey, getSelectedConversationKey,
getShowScrollButton, getShowScrollButton,
isMessageSelectionMode, isMessageSelectionMode,
getFirstUnreadMessageIndex,
areMoreMessagesBeingFetched, areMoreMessagesBeingFetched,
isFirstUnreadMessageIdAbove, isFirstUnreadMessageIdAbove,
getFirstUnreadMessageId,
} from '../../../state/selectors/conversations'; } from '../../../state/selectors/conversations';
import { isElectronWindowFocused } from '../../../session/utils/WindowUtils'; import { isElectronWindowFocused } from '../../../session/utils/WindowUtils';
import useInterval from 'react-use/lib/useInterval'; import useInterval from 'react-use/lib/useInterval';
@ -65,8 +65,9 @@ type Props = SessionMessageListProps & {
areMoreMessagesBeingFetched: boolean; areMoreMessagesBeingFetched: boolean;
}; };
const UnreadIndicator = (props: { messageId: string; show: boolean }) => { const UnreadIndicator = (props: { messageId: string }) => {
if (!props.show) { const isFirstUnreadOnOpen = useSelector(getFirstUnreadMessageId);
if (!isFirstUnreadOnOpen || isFirstUnreadOnOpen !== props.messageId) {
return null; return null;
} }
return <SessionLastSeenIndicator key={`unread-indicator-${props.messageId}`} />; return <SessionLastSeenIndicator key={`unread-indicator-${props.messageId}`} />;
@ -75,12 +76,11 @@ const UnreadIndicator = (props: { messageId: string; show: boolean }) => {
const GroupUpdateItem = (props: { const GroupUpdateItem = (props: {
messageId: string; messageId: string;
groupNotificationProps: PropsForGroupUpdate; groupNotificationProps: PropsForGroupUpdate;
showUnreadIndicator: boolean;
}) => { }) => {
return ( return (
<React.Fragment key={props.messageId}> <React.Fragment key={props.messageId}>
<GroupNotification key={props.messageId} {...props.groupNotificationProps} /> <GroupNotification key={props.messageId} {...props.groupNotificationProps} />
<UnreadIndicator messageId={props.messageId} show={props.showUnreadIndicator} /> <UnreadIndicator messageId={props.messageId} />
</React.Fragment> </React.Fragment>
); );
}; };
@ -88,13 +88,12 @@ const GroupUpdateItem = (props: {
const GroupInvitationItem = (props: { const GroupInvitationItem = (props: {
messageId: string; messageId: string;
propsForGroupInvitation: PropsForGroupInvitation; propsForGroupInvitation: PropsForGroupInvitation;
showUnreadIndicator: boolean;
}) => { }) => {
return ( return (
<React.Fragment key={props.messageId}> <React.Fragment key={props.messageId}>
<GroupInvitation key={props.messageId} {...props.propsForGroupInvitation} /> <GroupInvitation key={props.messageId} {...props.propsForGroupInvitation} />
<UnreadIndicator messageId={props.messageId} show={props.showUnreadIndicator} /> <UnreadIndicator messageId={props.messageId} />
</React.Fragment> </React.Fragment>
); );
}; };
@ -102,7 +101,6 @@ const GroupInvitationItem = (props: {
const DataExtractionNotificationItem = (props: { const DataExtractionNotificationItem = (props: {
messageId: string; messageId: string;
propsForDataExtractionNotification: PropsForDataExtractionNotification; propsForDataExtractionNotification: PropsForDataExtractionNotification;
showUnreadIndicator: boolean;
}) => { }) => {
return ( return (
<React.Fragment key={props.messageId}> <React.Fragment key={props.messageId}>
@ -111,7 +109,7 @@ const DataExtractionNotificationItem = (props: {
{...props.propsForDataExtractionNotification} {...props.propsForDataExtractionNotification}
/> />
<UnreadIndicator messageId={props.messageId} show={props.showUnreadIndicator} /> <UnreadIndicator messageId={props.messageId} />
</React.Fragment> </React.Fragment>
); );
}; };
@ -119,13 +117,12 @@ const DataExtractionNotificationItem = (props: {
const TimerNotificationItem = (props: { const TimerNotificationItem = (props: {
messageId: string; messageId: string;
timerProps: PropsForExpirationTimer; timerProps: PropsForExpirationTimer;
showUnreadIndicator: boolean;
}) => { }) => {
return ( return (
<React.Fragment key={props.messageId}> <React.Fragment key={props.messageId}>
<TimerNotification key={props.messageId} {...props.timerProps} /> <TimerNotification key={props.messageId} {...props.timerProps} />
<UnreadIndicator messageId={props.messageId} show={props.showUnreadIndicator} /> <UnreadIndicator messageId={props.messageId} />
</React.Fragment> </React.Fragment>
); );
}; };
@ -134,12 +131,10 @@ const GenericMessageItem = (props: {
messageId: string; messageId: string;
messageProps: SortedMessageModelProps; messageProps: SortedMessageModelProps;
playableMessageIndex?: number; playableMessageIndex?: number;
showUnreadIndicator: boolean;
scrollToQuoteMessage: (options: QuoteClickOptions) => Promise<void>; scrollToQuoteMessage: (options: QuoteClickOptions) => Promise<void>;
playNextMessage?: (value: number) => void; playNextMessage?: (value: number) => void;
}) => { }) => {
const multiSelectMode = useSelector(isMessageSelectionMode); const multiSelectMode = useSelector(isMessageSelectionMode);
const quotedMessageToAnimate = useSelector(getQuotedMessageToAnimate);
const nextMessageToPlay = useSelector(getNextMessageToPlayIndex); const nextMessageToPlay = useSelector(getNextMessageToPlayIndex);
const messageId = props.messageId; const messageId = props.messageId;
@ -152,7 +147,6 @@ const GenericMessageItem = (props: {
...props.messageProps.propsForMessage, ...props.messageProps.propsForMessage,
firstMessageOfSeries: props.messageProps.firstMessageOfSeries, firstMessageOfSeries: props.messageProps.firstMessageOfSeries,
multiSelectMode, multiSelectMode,
isQuotedMessageToAnimate: messageId === quotedMessageToAnimate,
nextMessageToPlay, nextMessageToPlay,
playNextMessage: props.playNextMessage, playNextMessage: props.playNextMessage,
onQuoteClick, onQuoteClick,
@ -166,7 +160,7 @@ const GenericMessageItem = (props: {
multiSelectMode={multiSelectMode} multiSelectMode={multiSelectMode}
key={messageId} key={messageId}
/> />
<UnreadIndicator messageId={props.messageId} show={props.showUnreadIndicator} /> <UnreadIndicator messageId={props.messageId} />
</React.Fragment> </React.Fragment>
); );
}; };
@ -176,7 +170,6 @@ const MessageList = (props: {
playNextMessage?: (value: number) => void; playNextMessage?: (value: number) => void;
}) => { }) => {
const messagesProps = useSelector(getSortedMessagesOfSelectedConversation); const messagesProps = useSelector(getSortedMessagesOfSelectedConversation);
const firstUnreadMessageIndex = useSelector(getFirstUnreadMessageIndex);
const isAbove = useSelector(isFirstUnreadMessageIdAbove); const isAbove = useSelector(isFirstUnreadMessageIdAbove);
console.warn('isAbove', isAbove); console.warn('isAbove', isAbove);
@ -191,19 +184,12 @@ const MessageList = (props: {
const groupNotificationProps = messageProps.propsForGroupNotification; const groupNotificationProps = messageProps.propsForGroupNotification;
// IF we found the first unread message
// AND we are not scrolled all the way to the bottom
// THEN, show the unread banner for the current message
const showUnreadIndicator =
Boolean(firstUnreadMessageIndex) && firstUnreadMessageIndex === index;
if (groupNotificationProps) { if (groupNotificationProps) {
return ( return (
<GroupUpdateItem <GroupUpdateItem
key={messageProps.propsForMessage.id} key={messageProps.propsForMessage.id}
groupNotificationProps={groupNotificationProps} groupNotificationProps={groupNotificationProps}
messageId={messageProps.propsForMessage.id} messageId={messageProps.propsForMessage.id}
showUnreadIndicator={showUnreadIndicator}
/> />
); );
} }
@ -214,7 +200,6 @@ const MessageList = (props: {
key={messageProps.propsForMessage.id} key={messageProps.propsForMessage.id}
propsForGroupInvitation={propsForGroupInvitation} propsForGroupInvitation={propsForGroupInvitation}
messageId={messageProps.propsForMessage.id} messageId={messageProps.propsForMessage.id}
showUnreadIndicator={showUnreadIndicator}
/> />
); );
} }
@ -225,7 +210,6 @@ const MessageList = (props: {
key={messageProps.propsForMessage.id} key={messageProps.propsForMessage.id}
propsForDataExtractionNotification={propsForDataExtractionNotification} propsForDataExtractionNotification={propsForDataExtractionNotification}
messageId={messageProps.propsForMessage.id} messageId={messageProps.propsForMessage.id}
showUnreadIndicator={showUnreadIndicator}
/> />
); );
} }
@ -236,7 +220,6 @@ const MessageList = (props: {
key={messageProps.propsForMessage.id} key={messageProps.propsForMessage.id}
timerProps={timerProps} timerProps={timerProps}
messageId={messageProps.propsForMessage.id} messageId={messageProps.propsForMessage.id}
showUnreadIndicator={showUnreadIndicator}
/> />
); );
} }
@ -255,7 +238,6 @@ const MessageList = (props: {
playableMessageIndex={playableMessageIndex} playableMessageIndex={playableMessageIndex}
messageId={messageProps.propsForMessage.id} messageId={messageProps.propsForMessage.id}
messageProps={messageProps} messageProps={messageProps}
showUnreadIndicator={showUnreadIndicator}
scrollToQuoteMessage={props.scrollToQuoteMessage} scrollToQuoteMessage={props.scrollToQuoteMessage}
playNextMessage={props.playNextMessage} playNextMessage={props.playNextMessage}
/> />
@ -266,7 +248,6 @@ const MessageList = (props: {
}; };
class SessionMessagesListInner extends React.Component<Props> { class SessionMessagesListInner extends React.Component<Props> {
private scrollOffsetBottomPx: number = Number.MAX_VALUE;
private ignoreScrollEvents: boolean; private ignoreScrollEvents: boolean;
private timeoutResetQuotedScroll: NodeJS.Timeout | null = null; private timeoutResetQuotedScroll: NodeJS.Timeout | null = null;
@ -301,7 +282,7 @@ class SessionMessagesListInner extends React.Component<Props> {
) { ) {
// displayed conversation changed. We have a bit of cleaning to do here // displayed conversation changed. We have a bit of cleaning to do here
this.ignoreScrollEvents = true; this.ignoreScrollEvents = true;
this.setupTimeoutResetQuotedHighlightedMessage(true); this.setupTimeoutResetQuotedHighlightedMessage(this.props.animateQuotedMessageId);
this.initialMessageLoadingPosition(); this.initialMessageLoadingPosition();
} else { } else {
// if we got new message for this convo, and we are scrolled to bottom // if we got new message for this convo, and we are scrolled to bottom
@ -355,7 +336,7 @@ class SessionMessagesListInner extends React.Component<Props> {
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// ~~~~~~~~~~~~~ MESSAGE HANDLING ~~~~~~~~~~~~~ // ~~~~~~~~~~~~~ MESSAGE HANDLING ~~~~~~~~~~~~~
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
private updateReadMessages() { private updateReadMessages(forceIsOnBottom = false) {
const { messagesProps, conversationKey } = this.props; const { messagesProps, conversationKey } = this.props;
if (!messagesProps || messagesProps.length === 0 || !conversationKey) { if (!messagesProps || messagesProps.length === 0 || !conversationKey) {
@ -372,7 +353,7 @@ class SessionMessagesListInner extends React.Component<Props> {
return; return;
} }
if (this.getScrollOffsetBottomPx() === 0 && isElectronWindowFocused()) { if ((forceIsOnBottom || this.getScrollOffsetBottomPx() === 0) && isElectronWindowFocused()) {
void conversation.markRead(messagesProps[0].propsForMessage.receivedAt || 0); void conversation.markRead(messagesProps[0].propsForMessage.receivedAt || 0);
} }
} }
@ -450,10 +431,10 @@ class SessionMessagesListInner extends React.Component<Props> {
window.inboxStore?.dispatch(showScrollToBottomButton(showScrollButton)); window.inboxStore?.dispatch(showScrollToBottomButton(showScrollButton));
// trigger markRead if we hit the bottom // trigger markRead if we hit the bottom
const isScrolledToBottom = bottomOfBottomMessage >= containerBottom - 5; const isScrolledToBottom = bottomOfBottomMessage <= containerBottom - 5;
if (isScrolledToBottom) { if (isScrolledToBottom) {
// Mark messages read // Mark messages read
this.updateReadMessages(); this.updateReadMessages(true);
} }
} }
@ -522,19 +503,15 @@ class SessionMessagesListInner extends React.Component<Props> {
* So we need to reset the state of of the highlighted message so when the users clicks again, * So we need to reset the state of of the highlighted message so when the users clicks again,
* the highlight is shown once again * the highlight is shown once again
*/ */
private setupTimeoutResetQuotedHighlightedMessage(clearOnly = false) { private setupTimeoutResetQuotedHighlightedMessage(messageId: string | undefined) {
if (this.timeoutResetQuotedScroll) { if (this.timeoutResetQuotedScroll) {
clearTimeout(this.timeoutResetQuotedScroll); clearTimeout(this.timeoutResetQuotedScroll);
} }
// only clear the timeout, do not schedule once again
if (clearOnly) {
return;
}
if (this.props.animateQuotedMessageId !== undefined) { if (messageId !== undefined) {
this.timeoutResetQuotedScroll = global.setTimeout(() => { this.timeoutResetQuotedScroll = global.setTimeout(() => {
window.inboxStore?.dispatch(quotedMessageToAnimate(undefined)); window.inboxStore?.dispatch(quotedMessageToAnimate(undefined));
}, 3000); }, 2000); // should match .flash-green-once
} }
} }
@ -548,7 +525,7 @@ class SessionMessagesListInner extends React.Component<Props> {
// we consider that a `smooth` set to true, means it's a quoted message, so highlight this message on the UI // we consider that a `smooth` set to true, means it's a quoted message, so highlight this message on the UI
if (smooth) { if (smooth) {
window.inboxStore?.dispatch(quotedMessageToAnimate(messageId)); window.inboxStore?.dispatch(quotedMessageToAnimate(messageId));
this.setupTimeoutResetQuotedHighlightedMessage(); this.setupTimeoutResetQuotedHighlightedMessage(messageId);
} }
const messageContainer = this.props.messageContainerRef.current; const messageContainer = this.props.messageContainerRef.current;

@ -1087,8 +1087,17 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
public async markRead(readAt: number) { public async markRead(readAt: number) {
this.markReadNoCommit(readAt); this.markReadNoCommit(readAt);
await this.commit(); await this.commit();
const convo = this.getConversation();
if (convo) {
const beforeUnread = convo.get('unreadCount');
const unreadCount = await convo.getUnreadCount();
if (beforeUnread !== unreadCount) {
convo.set({ unreadCount });
await convo.commit();
}
}
} }
public markReadNoCommit(readAt: number) { public markReadNoCommit(readAt: number) {

@ -244,7 +244,6 @@ export interface MessageRegularProps {
multiSelectMode: boolean; multiSelectMode: boolean;
firstMessageOfSeries: boolean; firstMessageOfSeries: boolean;
isUnread: boolean; isUnread: boolean;
isQuotedMessageToAnimate?: boolean;
isTrustedForAttachmentDownload: boolean; isTrustedForAttachmentDownload: boolean;
onQuoteClick?: (options: QuoteClickOptions) => Promise<void>; onQuoteClick?: (options: QuoteClickOptions) => Promise<void>;

@ -31,10 +31,7 @@ import { forceSyncConfigurationNowIfNeeded } from '../session/utils/syncUtils';
import { getMessageController } from '../session/messages'; import { getMessageController } from '../session/messages';
import { ClosedGroupEncryptionPairReplyMessage } from '../session/messages/outgoing/controlMessage/group/ClosedGroupEncryptionPairReplyMessage'; import { ClosedGroupEncryptionPairReplyMessage } from '../session/messages/outgoing/controlMessage/group/ClosedGroupEncryptionPairReplyMessage';
import { queueAllCachedFromSource } from './receiver'; import { queueAllCachedFromSource } from './receiver';
import { import { openConversationExternal } from '../state/ducks/conversations';
actions as conversationActions,
openConversationExternal,
} from '../state/ducks/conversations';
import { getSwarmPollingInstance } from '../session/snode_api'; import { getSwarmPollingInstance } from '../session/snode_api';
import { MessageModel } from '../models/message'; import { MessageModel } from '../models/message';
@ -955,7 +952,9 @@ export async function createClosedGroup(groupName: string, members: Array<string
await forceSyncConfigurationNowIfNeeded(); await forceSyncConfigurationNowIfNeeded();
window.inboxStore?.dispatch(openConversationExternal({ id: groupPublicKey })); window.inboxStore?.dispatch(
openConversationExternal({ id: groupPublicKey, firstUnreadIdOnOpen: undefined })
);
} }
/** /**

@ -292,7 +292,6 @@ export type SortedMessageModelProps = MessageModelProps & {
type FetchedMessageResults = { type FetchedMessageResults = {
conversationKey: string; conversationKey: string;
messagesProps: Array<MessageModelProps>; messagesProps: Array<MessageModelProps>;
firstUnreadMessageId: string | undefined;
}; };
export const fetchMessagesForConversation = createAsyncThunk( export const fetchMessagesForConversation = createAsyncThunk(
@ -307,8 +306,6 @@ export const fetchMessagesForConversation = createAsyncThunk(
const beforeTimestamp = Date.now(); const beforeTimestamp = Date.now();
console.time('fetchMessagesForConversation'); console.time('fetchMessagesForConversation');
const messagesProps = await getMessages(conversationKey, count); const messagesProps = await getMessages(conversationKey, count);
const firstUnreadMessageId = await getFirstUnreadMessageIdInConversation(conversationKey);
const afterTimestamp = Date.now(); const afterTimestamp = Date.now();
console.timeEnd('fetchMessagesForConversation'); console.timeEnd('fetchMessagesForConversation');
@ -318,7 +315,6 @@ export const fetchMessagesForConversation = createAsyncThunk(
return { return {
conversationKey, conversationKey,
messagesProps, messagesProps,
firstUnreadMessageId,
}; };
} }
); );
@ -594,12 +590,14 @@ const conversationsSlice = createSlice({
state: ConversationsStateType, state: ConversationsStateType,
action: PayloadAction<{ action: PayloadAction<{
id: string; id: string;
firstUnreadIdOnOpen: string | undefined;
messageId?: string; messageId?: string;
}> }>
) { ) {
if (state.selectedConversation === action.payload.id) { if (state.selectedConversation === action.payload.id) {
return state; return state;
} }
return { return {
conversationLookup: state.conversationLookup, conversationLookup: state.conversationLookup,
selectedConversation: action.payload.id, selectedConversation: action.payload.id,
@ -615,7 +613,7 @@ const conversationsSlice = createSlice({
showScrollButton: false, showScrollButton: false,
animateQuotedMessageId: undefined, animateQuotedMessageId: undefined,
mentionMembers: [], mentionMembers: [],
firstUnreadMessageId: undefined, firstUnreadMessageId: action.payload.firstUnreadIdOnOpen,
}; };
}, },
showLightBox( showLightBox(
@ -662,14 +660,13 @@ const conversationsSlice = createSlice({
fetchMessagesForConversation.fulfilled, fetchMessagesForConversation.fulfilled,
(state: ConversationsStateType, action: PayloadAction<FetchedMessageResults>) => { (state: ConversationsStateType, action: PayloadAction<FetchedMessageResults>) => {
// this is called once the messages are loaded from the db for the currently selected conversation // this is called once the messages are loaded from the db for the currently selected conversation
const { messagesProps, conversationKey, firstUnreadMessageId } = action.payload; const { messagesProps, conversationKey } = action.payload;
// double check that this update is for the shown convo // double check that this update is for the shown convo
if (conversationKey === state.selectedConversation) { if (conversationKey === state.selectedConversation) {
return { return {
...state, ...state,
messages: messagesProps, messages: messagesProps,
areMoreMessagesBeingFetched: false, areMoreMessagesBeingFetched: false,
firstUnreadMessageId,
}; };
} }
return state; return state;

@ -5,11 +5,7 @@ import { cleanSearchTerm } from '../../util/cleanSearchTerm';
import { searchConversations, searchMessages } from '../../../ts/data/data'; import { searchConversations, searchMessages } from '../../../ts/data/data';
import { makeLookup } from '../../util/makeLookup'; import { makeLookup } from '../../util/makeLookup';
import { import { PropsForSearchResults, ReduxConversationType } from './conversations';
openConversationExternal,
PropsForSearchResults,
ReduxConversationType,
} from './conversations';
import { PubKey } from '../../session/types'; import { PubKey } from '../../session/types';
import { MessageModel } from '../../models/message'; import { MessageModel } from '../../models/message';
import { MessageModelType } from '../../models/messageType'; import { MessageModelType } from '../../models/messageType';

@ -402,14 +402,6 @@ function sortMessages(
return messagesSorted; return messagesSorted;
} }
export const getFirstUnreadMessageIndex = createSelector(
getSortedMessagesOfSelectedConversation,
(messageModelsProps: Array<MessageModelProps>): number | undefined => {
const firstUnreadIndex = getFirstMessageUnreadIndex(messageModelsProps);
return firstUnreadIndex;
}
);
function getFirstMessageUnreadIndex(messages: Array<MessageModelProps>) { function getFirstMessageUnreadIndex(messages: Array<MessageModelProps>) {
if (!messages || messages.length === 0) { if (!messages || messages.length === 0) {
return -1; return -1;
@ -442,7 +434,6 @@ function getFirstMessageUnreadIndex(messages: Array<MessageModelProps>) {
export const getFirstUnreadMessageId = createSelector( export const getFirstUnreadMessageId = createSelector(
getConversations, getConversations,
(state: ConversationsStateType): string | undefined => { (state: ConversationsStateType): string | undefined => {
console.warn('getFirstUnreadMessageId', state.firstUnreadMessageId);
return state.firstUnreadMessageId; return state.firstUnreadMessageId;
} }
); );

Loading…
Cancel
Save