preload messages when switching conversation

pull/1804/head
Audric Ackermann 4 years ago
parent e5bbfc8c1e
commit 12d09bc896
No known key found for this signature in database
GPG Key ID: 999F434D76324AD4

@ -344,13 +344,7 @@
Whisper.Notifications.on('click', async (id, messageId) => { Whisper.Notifications.on('click', async (id, messageId) => {
window.showWindow(); window.showWindow();
if (id) { if (id) {
const firstUnreadIdOnOpen = await window.Signal.Data.getFirstUnreadMessageIdInConversation( await window.openConversationWithMessages({ id, messageId });
id
);
window.inboxStore.dispatch(
window.actionsCreators.openConversationExternal({ id, messageId, firstUnreadIdOnOpen })
);
} else { } else {
appView.openInbox({ appView.openInbox({
initialLoadComplete, initialLoadComplete,

@ -17,16 +17,15 @@ import { useTheme } from 'styled-components';
import { PubKey } from '../session/types'; import { PubKey } from '../session/types';
import { import {
LastMessageType, LastMessageType,
openConversationExternal, openConversationWithMessages,
ReduxConversationType, ReduxConversationType,
} from '../state/ducks/conversations'; } from '../state/ducks/conversations';
import _ from 'underscore'; import _ from 'underscore';
import { useMembersAvatars } from '../hooks/useMembersAvatar'; import { useMembersAvatars } from '../hooks/useMembersAvatar';
import { SessionIcon, SessionIconSize, SessionIconType } from './session/icon'; import { SessionIcon, SessionIconSize, SessionIconType } from './session/icon';
import { useDispatch, useSelector } from 'react-redux'; import { 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 {}
@ -238,18 +237,21 @@ const ConversationListItem = (props: Props) => {
const membersAvatar = useMembersAvatars(props); const membersAvatar = useMembersAvatars(props);
const dispatch = useDispatch(); const openConvo = useCallback(
async (e: any) => {
const openConvo = useCallback(async () => { // mousedown is invoked sooner than onClick, but for both right and left click
const firstUnreadIdOnOpen = await getFirstUnreadMessageIdInConversation(conversationId); if (e.button === 0) {
dispatch(openConversationExternal({ id: conversationId, firstUnreadIdOnOpen })); await openConversationWithMessages({ conversationKey: conversationId });
}, [conversationId]); }
},
[conversationId]
);
return ( return (
<div key={key}> <div key={key}>
<div <div
role="button" role="button"
onClick={openConvo} onMouseDown={openConvo}
onContextMenu={(e: any) => { onContextMenu={(e: any) => {
contextMenu.show({ contextMenu.show({
id: triggerId, id: triggerId,

@ -1,17 +1,9 @@
import React from 'react'; import React from 'react';
import classNames from 'classnames';
import { Avatar, AvatarSize } from './Avatar'; import { Avatar, AvatarSize } from './Avatar';
import { MessageBodyHighlight } from './MessageBodyHighlight';
import { Timestamp } from './conversation/Timestamp';
import { ContactName } from './conversation/ContactName'; import { ContactName } from './conversation/ContactName';
import { import { FindAndFormatContactType, PropsForSearchResults } from '../state/ducks/conversations';
FindAndFormatContactType,
openConversationExternal,
PropsForSearchResults,
} from '../state/ducks/conversations';
import { useDispatch } from 'react-redux';
type PropsHousekeeping = { type PropsHousekeeping = {
isSelected?: boolean; isSelected?: boolean;
@ -81,44 +73,44 @@ const AvatarItem = (props: { from: FindAndFormatContactType }) => {
/> />
); );
}; };
export const MessageSearchResult = (props: Props) => { // export const MessageSearchResult = (props: Props) => {
const { from, id: messageId, isSelected, conversationId, receivedAt, snippet, to } = props; // const { from, id: messageId, isSelected, conversationId, receivedAt, snippet, to } = props;
const dispatch = useDispatch(); // const dispatch = useDispatch();
if (!from || !to) { // if (!from || !to) {
return null; // return null;
} // }
return ( // return (
<div // <div
role="button" // role="button"
onClick={() => { // onClick={() => {
dispatch( // dispatch(
openConversationExternal({ // openConversationExternal({
id: conversationId, // id: conversationId,
messageId, // messageId,
firstUnreadIdOnOpen: undefined, // firstUnreadIdOnOpen: undefined,
}) // })
); // );
}} // }}
className={classNames( // className={classNames(
'module-message-search-result', // 'module-message-search-result',
isSelected ? 'module-message-search-result--is-selected' : null // isSelected ? 'module-message-search-result--is-selected' : null
)} // )}
> // >
<AvatarItem from={from} /> // <AvatarItem from={from} />
<div className="module-message-search-result__text"> // <div className="module-message-search-result__text">
<div className="module-message-search-result__header"> // <div className="module-message-search-result__header">
<From from={from} to={to} /> // <From from={from} to={to} />
<div className="module-message-search-result__header__timestamp"> // <div className="module-message-search-result__header__timestamp">
<Timestamp timestamp={receivedAt} /> // <Timestamp timestamp={receivedAt} />
</div> // </div>
</div> // </div>
<div className="module-message-search-result__body"> // <div className="module-message-search-result__body">
<MessageBodyHighlight text={snippet || ''} /> // <MessageBodyHighlight text={snippet || ''} />
</div> // </div>
</div> // </div>
</div> // </div>
); // );
}; // };

@ -4,7 +4,7 @@ import {
ConversationListItemProps, ConversationListItemProps,
MemoConversationListItemWithDetails, MemoConversationListItemWithDetails,
} from './ConversationListItem'; } from './ConversationListItem';
import { MessageSearchResult } from './MessageSearchResult'; // import { MessageSearchResult } from './MessageSearchResult';
export type SearchResultsProps = { export type SearchResultsProps = {
contacts: Array<ConversationListItemProps>; contacts: Array<ConversationListItemProps>;

@ -8,7 +8,7 @@ import { ConversationTypeEnum } from '../models/conversation';
import { SessionWrapperModal } from './session/SessionWrapperModal'; import { SessionWrapperModal } from './session/SessionWrapperModal';
import { SpacerMD } from './basic/Text'; import { SpacerMD } from './basic/Text';
import { updateUserDetailsModal } from '../state/ducks/modalDialog'; import { updateUserDetailsModal } from '../state/ducks/modalDialog';
import { openConversationExternal } from '../state/ducks/conversations'; import { openConversationWithMessages } 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'; import { getFirstUnreadMessageIdInConversation } from '../data/data';
@ -34,12 +34,8 @@ 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, firstUnreadIdOnOpen })
);
await openConversationWithMessages({ conversationKey: conversation.id });
closeDialog(); closeDialog();
} }

@ -46,6 +46,7 @@ import {
getQuotedMessageToAnimate, getQuotedMessageToAnimate,
getSelectedConversationKey, getSelectedConversationKey,
getSelectedMessageIds, getSelectedMessageIds,
haveDoneFirstScroll,
} from '../../state/selectors/conversations'; } from '../../state/selectors/conversations';
import { import {
fetchMessagesForConversation, fetchMessagesForConversation,
@ -84,6 +85,7 @@ type Props = MessageRenderingProps & {
areMoreMessagesBeingFetched: boolean; areMoreMessagesBeingFetched: boolean;
loadedMessagesLength: number; loadedMessagesLength: number;
selectedConversationKey: string | undefined; selectedConversationKey: string | undefined;
haveDoneFirstScroll: boolean;
}; };
function attachmentIsAttachmentTypeWithPath(attac: any): attac is AttachmentTypeWithPath { function attachmentIsAttachmentTypeWithPath(attac: any): attac is AttachmentTypeWithPath {
@ -609,6 +611,7 @@ class MessageInner extends React.PureComponent<Props, State> {
} }
// tslint:disable-next-line: cyclomatic-complexity // tslint:disable-next-line: cyclomatic-complexity
// tslint:disable-next-line: max-func-body-length
public render() { public render() {
const { const {
direction, direction,
@ -646,6 +649,12 @@ class MessageInner extends React.PureComponent<Props, State> {
} }
const onVisible = async (inView: boolean | Object) => { const onVisible = async (inView: boolean | Object) => {
// when the view first loads, it needs to scroll to the unread messages.
// we need to disable the inview on the first loading
if (!this.props.haveDoneFirstScroll) {
console.warn('waiting for first scroll');
return;
}
// we are the bottom message // we are the bottom message
if (this.props.mostRecentMessageId === messageId) { if (this.props.mostRecentMessageId === messageId) {
if (inView === true) { if (inView === true) {
@ -933,6 +942,7 @@ const mapStateToProps = (state: StateType) => {
areMoreMessagesBeingFetched: areMoreMessagesBeingFetched(state), areMoreMessagesBeingFetched: areMoreMessagesBeingFetched(state),
selectedConversationKey: getSelectedConversationKey(state), selectedConversationKey: getSelectedConversationKey(state),
loadedMessagesLength: getLoadedMessagesLength(state), loadedMessagesLength: getLoadedMessagesLength(state),
haveDoneFirstScroll: haveDoneFirstScroll(state),
}; };
}; };

@ -5,7 +5,10 @@ import {
ConversationListItemProps, ConversationListItemProps,
MemoConversationListItemWithDetails, MemoConversationListItemWithDetails,
} from '../ConversationListItem'; } from '../ConversationListItem';
import { openConversationExternal, ReduxConversationType } from '../../state/ducks/conversations'; import {
openConversationWithMessages,
ReduxConversationType,
} from '../../state/ducks/conversations';
import { SearchResults, SearchResultsProps } from '../SearchResults'; import { SearchResults, SearchResultsProps } from '../SearchResults';
import { SessionSearchInput } from './SessionSearchInput'; import { SessionSearchInput } from './SessionSearchInput';
import _, { debounce } from 'lodash'; import _, { debounce } from 'lodash';
@ -320,11 +323,8 @@ export class LeftPaneMessageSection extends React.Component<Props, State> {
pubkeyorOns, pubkeyorOns,
ConversationTypeEnum.PRIVATE ConversationTypeEnum.PRIVATE
); );
const firstUnreadIdOnOpen = await getFirstUnreadMessageIdInConversation(pubkeyorOns);
window.inboxStore?.dispatch( await openConversationWithMessages({ conversationKey: pubkeyorOns });
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
@ -345,11 +345,8 @@ export class LeftPaneMessageSection extends React.Component<Props, State> {
ConversationTypeEnum.PRIVATE ConversationTypeEnum.PRIVATE
); );
const firstUnreadIdOnOpen = await getFirstUnreadMessageIdInConversation(resolvedSessionID); await openConversationWithMessages({ conversationKey: 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);

@ -5,7 +5,11 @@ import { ConversationModel } from '../../models/conversation';
import { getConversationController } from '../../session/conversations'; import { getConversationController } from '../../session/conversations';
import { UserUtils } from '../../session/utils'; import { UserUtils } from '../../session/utils';
import { createStore } from '../../state/createStore'; import { createStore } from '../../state/createStore';
import { actions as conversationActions } from '../../state/ducks/conversations'; import {
actions as conversationActions,
getEmptyConversationState,
openConversationWithMessages,
} from '../../state/ducks/conversations';
import { initialDefaultRoomState } from '../../state/ducks/defaultRooms'; import { initialDefaultRoomState } from '../../state/ducks/defaultRooms';
import { initialModalState } from '../../state/ducks/modalDialog'; import { initialModalState } from '../../state/ducks/modalDialog';
import { initialOnionPathState } from '../../state/ducks/onion'; import { initialOnionPathState } from '../../state/ducks/onion';
@ -99,20 +103,8 @@ export class SessionInboxView extends React.Component<any, State> {
const initialState: StateType = { const initialState: StateType = {
conversations: { conversations: {
...getEmptyConversationState(),
conversationLookup: makeLookup(fullFilledConversations, 'id'), conversationLookup: makeLookup(fullFilledConversations, 'id'),
messages: [],
showRightPanel: false,
messageDetailProps: undefined,
selectedMessageIds: [],
selectedConversation: undefined,
areMoreMessagesBeingFetched: false,
showScrollButton: false,
animateQuotedMessageId: undefined,
lightBox: undefined,
nextMessageToPlay: undefined,
quotedMessage: undefined,
mentionMembers: [],
firstUnreadMessageId: undefined,
}, },
user: { user: {
ourNumber: UserUtils.getOurPubKeyStrFromCache(), ourNumber: UserUtils.getOurPubKeyStrFromCache(),
@ -134,7 +126,7 @@ export class SessionInboxView extends React.Component<any, State> {
// Enables our redux store to be updated by backbone events in the outside world // Enables our redux store to be updated by backbone events in the outside world
const { messageExpired } = bindActionCreators(conversationActions, this.store.dispatch); const { messageExpired } = bindActionCreators(conversationActions, this.store.dispatch);
window.actionsCreators = conversationActions; window.openConversationWithMessages = openConversationWithMessages;
// messageExpired is currently inboked fropm js. So we link it to Redux that way // messageExpired is currently inboked fropm js. So we link it to Redux that way
window.Whisper.events.on('messageExpired', messageExpired); window.Whisper.events.on('messageExpired', messageExpired);

@ -12,6 +12,7 @@ import {
setNextMessageToPlay, setNextMessageToPlay,
showScrollToBottomButton, showScrollToBottomButton,
SortedMessageModelProps, SortedMessageModelProps,
updateHaveDoneFirstScroll,
} from '../../../state/ducks/conversations'; } from '../../../state/ducks/conversations';
import { ToastUtils } from '../../../session/utils'; import { ToastUtils } from '../../../session/utils';
import { TypingBubble } from '../../conversation/TypingBubble'; import { TypingBubble } from '../../conversation/TypingBubble';
@ -48,14 +49,11 @@ type Props = SessionMessageListProps & {
}; };
class SessionMessagesListContainerInner extends React.Component<Props> { class SessionMessagesListContainerInner extends React.Component<Props> {
private ignoreScrollEvents: boolean;
private timeoutResetQuotedScroll: NodeJS.Timeout | null = null; private timeoutResetQuotedScroll: NodeJS.Timeout | null = null;
public constructor(props: Props) { public constructor(props: Props) {
super(props); super(props);
autoBind(this); autoBind(this);
this.ignoreScrollEvents = true;
} }
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -63,8 +61,7 @@ class SessionMessagesListContainerInner extends React.Component<Props> {
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
public componentDidMount() { public componentDidMount() {
// Pause thread to wait for rendering to complete this.initialMessageLoadingPosition();
setTimeout(this.initialMessageLoadingPosition, 0);
} }
public componentWillUnmount() { public componentWillUnmount() {
@ -87,9 +84,7 @@ class SessionMessagesListContainerInner extends React.Component<Props> {
this.setupTimeoutResetQuotedHighlightedMessage(this.props.animateQuotedMessageId); this.setupTimeoutResetQuotedHighlightedMessage(this.props.animateQuotedMessageId);
// 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.initialMessageLoadingPosition(); this.initialMessageLoadingPosition();
this.ignoreScrollEvents = false;
} 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
if (isSameConvo && messageLengthChanged) { if (isSameConvo && messageLengthChanged) {
@ -97,8 +92,6 @@ class SessionMessagesListContainerInner extends React.Component<Props> {
// Adjust scroll so these new items don't push the old ones out of view. // Adjust scroll so these new items don't push the old ones out of view.
// (snapshot here is the value returned from getSnapshotBeforeUpdate) // (snapshot here is the value returned from getSnapshotBeforeUpdate)
if (prevProps.messagesProps.length && snapshot !== null) { if (prevProps.messagesProps.length && snapshot !== null) {
this.ignoreScrollEvents = true;
const list = this.props.messageContainerRef.current; const list = this.props.messageContainerRef.current;
// if we added a message at the top, keep position from the bottom. // if we added a message at the top, keep position from the bottom.
@ -106,23 +99,28 @@ class SessionMessagesListContainerInner extends React.Component<Props> {
prevProps.messagesProps[0].propsForMessage.id === prevProps.messagesProps[0].propsForMessage.id ===
this.props.messagesProps[0].propsForMessage.id this.props.messagesProps[0].propsForMessage.id
) { ) {
// list.scrollTop = list.scrollHeight - (snapshot.scrollHeight - snapshot.scrollTop); list.scrollTop = list.scrollHeight - (snapshot.scrollHeight - snapshot.scrollTop);
} else { } else {
// if we added a message at the bottom, keep position from the bottom. // if we added a message at the bottom, keep position from the bottom.
// list.scrollTop = snapshot.scrollTop; list.scrollTop = snapshot.scrollTop;
} }
this.ignoreScrollEvents = false;
} }
} }
} }
} }
public getSnapshotBeforeUpdate(prevProps: Props) { public getSnapshotBeforeUpdate(prevProps: Props) {
// getSnapshotBeforeUpdate is kind of pain to do in react hooks, so better keep the message list as a
// class component for now
// Are we adding new items to the list? // Are we adding new items to the list?
// Capture the scroll position so we can adjust scroll later. // Capture the scroll position so we can adjust scroll later.
if (prevProps.messagesProps.length < this.props.messagesProps.length) { if (prevProps.messagesProps.length < this.props.messagesProps.length) {
const list = this.props.messageContainerRef.current; const list = this.props.messageContainerRef.current;
console.warn('getSnapshotBeforeUpdate ', {
scrollHeight: list.scrollHeight,
scrollTop: list.scrollTop,
});
return { scrollHeight: list.scrollHeight, scrollTop: list.scrollTop }; return { scrollHeight: list.scrollHeight, scrollTop: list.scrollTop };
} }
return null; return null;
@ -166,33 +164,6 @@ class SessionMessagesListContainerInner extends React.Component<Props> {
); );
} }
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// ~~~~~~~~~~~~~ MESSAGE HANDLING ~~~~~~~~~~~~~
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
private updateReadMessages(forceIsOnBottom = false) {
const { messagesProps, conversationKey } = this.props;
if (!messagesProps || messagesProps.length === 0 || !conversationKey) {
return;
}
const conversation = getConversationController().getOrThrow(conversationKey);
if (conversation.isBlocked()) {
return;
}
if (this.ignoreScrollEvents) {
return;
}
if ((forceIsOnBottom || this.getScrollOffsetBottomPx() === 0) && isElectronWindowFocused()) {
void conversation.markRead(Date.now()).then(() => {
window.inboxStore?.dispatch(markConversationFullyRead(conversationKey));
});
}
}
/** /**
* Sets the targeted index for the next * Sets the targeted index for the next
* @param index index of message that just completed * @param index index of message that just completed
@ -246,11 +217,7 @@ class SessionMessagesListContainerInner extends React.Component<Props> {
this.scrollToMessage(messagesProps[middle].propsForMessage.id); this.scrollToMessage(messagesProps[middle].propsForMessage.id);
} }
} }
// window.inboxStore?.dispatch(updateHaveDoneFirstScroll());
if (this.ignoreScrollEvents && messagesProps.length > 0) {
this.ignoreScrollEvents = false;
this.updateReadMessages();
}
} }
/** /**
@ -274,18 +241,12 @@ class SessionMessagesListContainerInner extends React.Component<Props> {
} }
} }
private scrollToMessage(messageId: string, scrollIsQuote: boolean = false) { private scrollToMessage(messageId: string) {
const messageElementDom = document.getElementById(messageId); const messageElementDom = document.getElementById(`inview-${messageId}`);
messageElementDom?.scrollIntoView({ messageElementDom?.scrollIntoView({
behavior: 'auto', behavior: 'auto',
block: 'center', block: 'center',
}); });
// we consider that a `scrollIsQuote` set to true, means it's a quoted message, so highlight this message on the UI
if (scrollIsQuote) {
window.inboxStore?.dispatch(quotedMessageToAnimate(messageId));
this.setupTimeoutResetQuotedHighlightedMessage(messageId);
}
} }
private scrollToBottom() { private scrollToBottom() {
@ -343,21 +304,10 @@ class SessionMessagesListContainerInner extends React.Component<Props> {
} }
const databaseId = targetMessage.propsForMessage.id; const databaseId = targetMessage.propsForMessage.id;
this.scrollToMessage(databaseId, true); this.scrollToMessage(databaseId);
} // Highlight this message on the UI
window.inboxStore?.dispatch(quotedMessageToAnimate(databaseId));
// basically the offset in px from the bottom of the view (most recent message) this.setupTimeoutResetQuotedHighlightedMessage(databaseId);
private getScrollOffsetBottomPx() {
const messageContainer = this.props.messageContainerRef?.current;
if (!messageContainer) {
return Number.MAX_VALUE;
}
const scrollTop = messageContainer.scrollTop;
const scrollHeight = messageContainer.scrollHeight;
const clientHeight = messageContainer.clientHeight;
return scrollHeight - scrollTop - clientHeight;
} }
} }

@ -1180,7 +1180,6 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
} }
private dispatchMessageUpdate() { private dispatchMessageUpdate() {
trotthledAllMessagesDispatch(); trotthledAllMessagesDispatch();
console.warn('adding dispatch for:', this.id);
updatesToDispatch.set(this.id, this.getProps()); updatesToDispatch.set(this.id, this.getProps());
} }
@ -1190,7 +1189,6 @@ const trotthledAllMessagesDispatch = _.throttle(() => {
if (updatesToDispatch.size === 0) { if (updatesToDispatch.size === 0) {
return; return;
} }
console.warn('TRIGGERING ALL DISPATCH');
window.inboxStore?.dispatch(messagesChanged([...updatesToDispatch.values()])); window.inboxStore?.dispatch(messagesChanged([...updatesToDispatch.values()]));
updatesToDispatch.clear(); updatesToDispatch.clear();
}, 1000); }, 1000);

@ -31,7 +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 { openConversationExternal } from '../state/ducks/conversations'; import { openConversationWithMessages } 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';
@ -952,9 +952,7 @@ export async function createClosedGroup(groupName: string, members: Array<string
await forceSyncConfigurationNowIfNeeded(); await forceSyncConfigurationNowIfNeeded();
window.inboxStore?.dispatch( await openConversationWithMessages({ conversationKey: groupPublicKey });
openConversationExternal({ id: groupPublicKey, firstUnreadIdOnOpen: undefined })
);
} }
/** /**

@ -498,7 +498,6 @@ const trotthledAllMessagesAddedDispatch = _.throttle(() => {
if (updatesToDispatch.size === 0) { if (updatesToDispatch.size === 0) {
return; return;
} }
console.warn('TRIGGERING ALL ADDED DISPATCH');
window.inboxStore?.dispatch(messagesAdded([...updatesToDispatch.values()])); window.inboxStore?.dispatch(messagesAdded([...updatesToDispatch.values()]));
updatesToDispatch.clear(); updatesToDispatch.clear();
}, 1000); }, 1000);

@ -3,7 +3,7 @@ import _, { omit } from 'lodash';
import { Constants } from '../../session'; import { Constants } from '../../session';
import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit'; import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit';
import { getConversationController } from '../../session/conversations'; import { getConversationController } from '../../session/conversations';
import { getMessagesByConversation } from '../../data/data'; import { getFirstUnreadMessageIdInConversation, getMessagesByConversation } from '../../data/data';
import { import {
ConversationNotificationSettingType, ConversationNotificationSettingType,
ConversationTypeEnum, ConversationTypeEnum,
@ -255,6 +255,7 @@ export type ConversationsStateType = {
lightBox?: LightBoxOptions; lightBox?: LightBoxOptions;
quotedMessage?: ReplyingToMessageProps; quotedMessage?: ReplyingToMessageProps;
areMoreMessagesBeingFetched: boolean; areMoreMessagesBeingFetched: boolean;
haveDoneFirstScroll: boolean;
showScrollButton: boolean; showScrollButton: boolean;
animateQuotedMessageId?: string; animateQuotedMessageId?: string;
@ -334,7 +335,7 @@ export const fetchMessagesForConversation = createAsyncThunk(
// Reducer // Reducer
function getEmptyState(): ConversationsStateType { export function getEmptyConversationState(): ConversationsStateType {
return { return {
conversationLookup: {}, conversationLookup: {},
messages: [], messages: [],
@ -345,6 +346,7 @@ function getEmptyState(): ConversationsStateType {
showScrollButton: false, showScrollButton: false,
mentionMembers: [], mentionMembers: [],
firstUnreadMessageId: undefined, firstUnreadMessageId: undefined,
haveDoneFirstScroll: false,
}; };
} }
@ -464,7 +466,7 @@ function handleConversationReset(state: ConversationsStateType, action: PayloadA
const conversationsSlice = createSlice({ const conversationsSlice = createSlice({
name: 'conversations', name: 'conversations',
initialState: getEmptyState(), initialState: getEmptyConversationState(),
reducers: { reducers: {
showMessageDetailsView( showMessageDetailsView(
state: ConversationsStateType, state: ConversationsStateType,
@ -569,7 +571,7 @@ const conversationsSlice = createSlice({
}, },
removeAllConversations() { removeAllConversations() {
return getEmptyState(); return getEmptyConversationState();
}, },
messageAdded( messageAdded(
@ -648,6 +650,7 @@ const conversationsSlice = createSlice({
action: PayloadAction<{ action: PayloadAction<{
id: string; id: string;
firstUnreadIdOnOpen: string | undefined; firstUnreadIdOnOpen: string | undefined;
initialMessages: Array<MessageModelProps>;
messageId?: string; messageId?: string;
}> }>
) { ) {
@ -659,7 +662,7 @@ const conversationsSlice = createSlice({
conversationLookup: state.conversationLookup, conversationLookup: state.conversationLookup,
selectedConversation: action.payload.id, selectedConversation: action.payload.id,
areMoreMessagesBeingFetched: false, areMoreMessagesBeingFetched: false,
messages: [], messages: action.payload.initialMessages,
showRightPanel: false, showRightPanel: false,
selectedMessageIds: [], selectedMessageIds: [],
lightBox: undefined, lightBox: undefined,
@ -671,8 +674,14 @@ const conversationsSlice = createSlice({
animateQuotedMessageId: undefined, animateQuotedMessageId: undefined,
mentionMembers: [], mentionMembers: [],
firstUnreadMessageId: action.payload.firstUnreadIdOnOpen, firstUnreadMessageId: action.payload.firstUnreadIdOnOpen,
haveDoneFirstScroll: false,
}; };
}, },
updateHaveDoneFirstScroll(state: ConversationsStateType) {
state.haveDoneFirstScroll = true;
return state;
},
showLightBox( showLightBox(
state: ConversationsStateType, state: ConversationsStateType,
action: PayloadAction<LightBoxOptions | undefined> action: PayloadAction<LightBoxOptions | undefined>
@ -753,7 +762,7 @@ export const {
conversationReset, conversationReset,
messageChanged, messageChanged,
messagesChanged, messagesChanged,
openConversationExternal, updateHaveDoneFirstScroll,
markConversationFullyRead, markConversationFullyRead,
// layout stuff // layout stuff
showMessageDetailsView, showMessageDetailsView,
@ -770,3 +779,22 @@ export const {
setNextMessageToPlay, setNextMessageToPlay,
updateMentionsMembers, updateMentionsMembers,
} = actions; } = actions;
export async function openConversationWithMessages(args: {
conversationKey: string;
messageId?: string;
}) {
const { conversationKey, messageId } = args;
const firstUnreadIdOnOpen = await getFirstUnreadMessageIdInConversation(conversationKey);
const initialMessages = await getMessages(conversationKey, 30);
window.inboxStore?.dispatch(
actions.openConversationExternal({
id: conversationKey,
firstUnreadIdOnOpen,
messageId,
initialMessages,
})
);
}

@ -320,6 +320,11 @@ export const areMoreMessagesBeingFetched = createSelector(
(state: ConversationsStateType): boolean => state.areMoreMessagesBeingFetched || false (state: ConversationsStateType): boolean => state.areMoreMessagesBeingFetched || false
); );
export const haveDoneFirstScroll = createSelector(
getConversations,
(state: ConversationsStateType): boolean => state.haveDoneFirstScroll
);
export const getShowScrollButton = createSelector( export const getShowScrollButton = createSelector(
getConversations, getConversations,
(state: ConversationsStateType): boolean => state.showScrollButton || false (state: ConversationsStateType): boolean => state.showScrollButton || false

5
ts/window.d.ts vendored

@ -73,7 +73,10 @@ declare global {
autoOrientImage: any; autoOrientImage: any;
contextMenuShown: boolean; contextMenuShown: boolean;
inboxStore?: Store; inboxStore?: Store;
actionsCreators: any; openConversationWithMessages: (args: {
conversationKey: string;
messageId?: string | undefined;
}) => Promise<void>;
extension: { extension: {
expired: (boolean) => void; expired: (boolean) => void;
expiredStatus: () => boolean; expiredStatus: () => boolean;

Loading…
Cancel
Save