split up load more messages from top or bottom
also split up just fetching the last messages from fetching based on unread/ lastTopMessageId
This commit is contained in:
parent
381cb77ad9
commit
12b00720f4
77
app/sql.js
77
app/sql.js
|
@ -71,6 +71,7 @@ module.exports = {
|
|||
getOutgoingWithoutExpiresAt,
|
||||
getNextExpiringMessage,
|
||||
getMessagesByConversation,
|
||||
getLastMessagesByConversation,
|
||||
getFirstUnreadMessageIdInConversation,
|
||||
hasConversationOutgoingMessage,
|
||||
trimMessages,
|
||||
|
@ -2230,27 +2231,83 @@ function getUnreadCountByConversation(conversationId) {
|
|||
|
||||
// Note: Sorting here is necessary for getting the last message (with limit 1)
|
||||
// be sure to update the sorting order to sort messages on redux too (sortMessages)
|
||||
const orderByClause =
|
||||
'ORDER BY serverTimestamp DESC, serverId DESC, sent_at DESC, received_at DESC';
|
||||
function getMessagesByConversation(conversationId, { messageId = null } = {}) {
|
||||
const absLimit = 30;
|
||||
// If messageId is given it means we are opening the conversation to that specific messageId,
|
||||
// or that we just scrolled to it by a quote click and needs to load around it.
|
||||
// If messageId is null, it means we are just opening the convo to the last unread message, or at the bottom
|
||||
const firstUnread = getFirstUnreadMessageIdInConversation(conversationId);
|
||||
|
||||
if (messageId || firstUnread) {
|
||||
const messageFound = getMessageById(messageId || firstUnread);
|
||||
|
||||
if (messageFound && messageFound.conversationId === conversationId) {
|
||||
console.warn('firstUnread', messageId, firstUnread, messageFound);
|
||||
|
||||
const rows = globalInstance
|
||||
.prepare(
|
||||
`WITH cte AS (
|
||||
SELECT id, json, row_number() OVER (${orderByClause}) as row_number
|
||||
FROM messages
|
||||
), current AS (
|
||||
SELECT row_number
|
||||
FROM cte
|
||||
WHERE id = $messageId
|
||||
)
|
||||
SELECT cte.*
|
||||
FROM cte, current
|
||||
WHERE ABS(cte.row_number - current.row_number) <= $limit
|
||||
ORDER BY cte.row_number;
|
||||
`
|
||||
)
|
||||
.all({
|
||||
conversationId,
|
||||
messageId: messageId || firstUnread,
|
||||
limit: absLimit,
|
||||
});
|
||||
console.warn('rows', rows);
|
||||
return map(rows, row => jsonToObject(row.json));
|
||||
}
|
||||
console.warn(
|
||||
`getMessagesByConversation: Could not find messageId ${messageId} in db with conversationId: ${conversationId}. Just fetching the convo as usual.`
|
||||
);
|
||||
}
|
||||
|
||||
function getMessagesByConversation(
|
||||
conversationId,
|
||||
{ limit = 100, receivedAt = Number.MAX_VALUE, type = '%' } = {}
|
||||
) {
|
||||
const rows = globalInstance
|
||||
.prepare(
|
||||
`
|
||||
SELECT json FROM ${MESSAGES_TABLE} WHERE
|
||||
conversationId = $conversationId AND
|
||||
received_at < $received_at AND
|
||||
type LIKE $type
|
||||
ORDER BY serverTimestamp DESC, serverId DESC, sent_at DESC, received_at DESC
|
||||
conversationId = $conversationId
|
||||
${orderByClause}
|
||||
LIMIT $limit;
|
||||
`
|
||||
)
|
||||
.all({
|
||||
conversationId,
|
||||
limit: 2 * absLimit,
|
||||
});
|
||||
return map(rows, row => jsonToObject(row.json));
|
||||
}
|
||||
|
||||
function getLastMessagesByConversation(conversationId, limit) {
|
||||
if (!isNumber(limit)) {
|
||||
throw new Error('limit must be a number');
|
||||
}
|
||||
|
||||
const rows = globalInstance
|
||||
.prepare(
|
||||
`
|
||||
SELECT json FROM ${MESSAGES_TABLE} WHERE
|
||||
conversationId = $conversationId
|
||||
${orderByClause}
|
||||
LIMIT $limit;
|
||||
`
|
||||
)
|
||||
.all({
|
||||
conversationId,
|
||||
received_at: receivedAt,
|
||||
limit,
|
||||
type,
|
||||
});
|
||||
return map(rows, row => jsonToObject(row.json));
|
||||
}
|
||||
|
|
|
@ -87,7 +87,7 @@ export class SessionConversation extends React.Component<Props, State> {
|
|||
};
|
||||
this.messageContainerRef = React.createRef();
|
||||
this.dragCounter = 0;
|
||||
this.updateMemberList = _.debounce(this.updateMemberListBouncy.bind(this), 1000);
|
||||
this.updateMemberList = _.debounce(this.updateMemberListBouncy.bind(this), 10000);
|
||||
|
||||
autoBind(this);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React from 'react';
|
||||
import React, { useLayoutEffect } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
// tslint:disable-next-line: no-submodule-imports
|
||||
import useKey from 'react-use/lib/useKey';
|
||||
|
@ -9,7 +9,10 @@ import {
|
|||
PropsForGroupInvitation,
|
||||
PropsForGroupUpdate,
|
||||
} from '../../state/ducks/conversations';
|
||||
import { getSortedMessagesTypesOfSelectedConversation } from '../../state/selectors/conversations';
|
||||
import {
|
||||
getOldTopMessageId,
|
||||
getSortedMessagesTypesOfSelectedConversation,
|
||||
} from '../../state/selectors/conversations';
|
||||
import { GroupUpdateMessage } from './message/message-item/GroupUpdateMessage';
|
||||
import { DataExtractionNotification } from './message/message-item/DataExtractionNotification';
|
||||
import { MessageDateBreak } from './message/message-item/DateBreak';
|
||||
|
@ -26,12 +29,32 @@ function isNotTextboxEvent(e: KeyboardEvent) {
|
|||
|
||||
export const SessionMessagesList = (props: {
|
||||
scrollToQuoteMessage: (options: QuoteClickOptions) => Promise<void>;
|
||||
scrollAfterLoadMore: (
|
||||
messageIdToScrollTo: string,
|
||||
block: ScrollLogicalPosition | undefined
|
||||
) => void;
|
||||
onPageUpPressed: () => void;
|
||||
onPageDownPressed: () => void;
|
||||
onHomePressed: () => void;
|
||||
onEndPressed: () => void;
|
||||
}) => {
|
||||
const messagesProps = useSelector(getSortedMessagesTypesOfSelectedConversation);
|
||||
const oldTopMessageId = useSelector(getOldTopMessageId);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const newTopMessageId = messagesProps.length
|
||||
? messagesProps[messagesProps.length - 1].message.props.messageId
|
||||
: undefined;
|
||||
console.warn('useLayoutEffect ', {
|
||||
oldTopMessageId,
|
||||
newTopMessageId,
|
||||
length: messagesProps.length,
|
||||
});
|
||||
|
||||
if (oldTopMessageId !== newTopMessageId && oldTopMessageId && newTopMessageId) {
|
||||
props.scrollAfterLoadMore(oldTopMessageId, 'center');
|
||||
}
|
||||
});
|
||||
|
||||
useKey('PageUp', () => {
|
||||
props.onPageUpPressed();
|
||||
|
|
|
@ -95,56 +95,59 @@ class SessionMessagesListContainerInner extends React.Component<Props> {
|
|||
|
||||
public componentDidUpdate(
|
||||
prevProps: Props,
|
||||
_prevState: any,
|
||||
snapShot: { fakeScrollTop: number; realScrollTop: number; scrollHeight: number }
|
||||
_prevState: any
|
||||
// snapShot: {
|
||||
// fakeScrollTop: number;
|
||||
// realScrollTop: number;
|
||||
// scrollHeight: number;
|
||||
// oldTopMessageId?: string;
|
||||
// }
|
||||
) {
|
||||
// this was hard to write, it should be hard to read
|
||||
// just make sure you don't remove that as a bug in chrome makes the column-reverse do bad things
|
||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=1189195&q=column-reverse&can=2#makechanges
|
||||
const currentRef = this.props.messageContainerRef.current;
|
||||
// const { oldTopMessageId } = snapShot;
|
||||
// console.warn('didupdate with oldTopMessageId', oldTopMessageId);
|
||||
// // If you want to mess with this, be my guest.
|
||||
// // just make sure you don't remove that as a bug in chrome makes the column-reverse do bad things
|
||||
// // https://bugs.chromium.org/p/chromium/issues/detail?id=1189195&q=column-reverse&can=2#makechanges
|
||||
const isSameConvo = prevProps.conversationKey === this.props.conversationKey;
|
||||
const prevMsgLength = prevProps.messagesProps.length;
|
||||
const newMsgLength = this.props.messagesProps.length;
|
||||
|
||||
const prevFirstMesssageId = prevProps.messagesProps[0]?.propsForMessage.id;
|
||||
const newFirstMesssageId = this.props.messagesProps[0]?.propsForMessage.id;
|
||||
const messageAddedWasMoreRecentOne = prevFirstMesssageId !== newFirstMesssageId;
|
||||
|
||||
if (isSameConvo && snapShot?.realScrollTop && prevMsgLength !== newMsgLength && currentRef) {
|
||||
if (messageAddedWasMoreRecentOne) {
|
||||
if (snapShot.scrollHeight - snapShot.realScrollTop < 50) {
|
||||
// consider that we were scrolled to bottom
|
||||
currentRef.scrollTop = 0;
|
||||
} else {
|
||||
currentRef.scrollTop = -(currentRef.scrollHeight - snapShot.realScrollTop);
|
||||
}
|
||||
} else {
|
||||
currentRef.scrollTop = snapShot.fakeScrollTop;
|
||||
}
|
||||
}
|
||||
if (!isSameConvo || (prevMsgLength === 0 && newMsgLength !== 0)) {
|
||||
// if (isSameConvo && oldTopMessageId) {
|
||||
// this.scrollToMessage(oldTopMessageId, 'center');
|
||||
// // if (messageAddedWasMoreRecentOne) {
|
||||
// // if (snapShot.scrollHeight - snapShot.realScrollTop < 50) {
|
||||
// // // consider that we were scrolled to bottom
|
||||
// // currentRef.scrollTop = 0;
|
||||
// // } else {
|
||||
// // currentRef.scrollTop = -(currentRef.scrollHeight - snapShot.realScrollTop);
|
||||
// // }
|
||||
// // } else {
|
||||
// // currentRef.scrollTop = snapShot.fakeScrollTop;
|
||||
// // }
|
||||
// }
|
||||
if (!isSameConvo) {
|
||||
console.info('Not same convo, resetting scrolling posiiton');
|
||||
this.setupTimeoutResetQuotedHighlightedMessage(this.props.animateQuotedMessageId);
|
||||
|
||||
// displayed conversation changed. We have a bit of cleaning to do here
|
||||
this.initialMessageLoadingPosition();
|
||||
}
|
||||
}
|
||||
|
||||
public getSnapshotBeforeUpdate() {
|
||||
const messageContainer = this.props.messageContainerRef.current;
|
||||
|
||||
const scrollTop = messageContainer?.scrollTop || undefined;
|
||||
const scrollHeight = messageContainer?.scrollHeight || undefined;
|
||||
|
||||
// as we use column-reverse for displaying message list
|
||||
// the top is < 0
|
||||
// tslint:disable-next-line: restrict-plus-operands
|
||||
const realScrollTop = scrollHeight && scrollTop ? scrollHeight + scrollTop : undefined;
|
||||
return {
|
||||
realScrollTop,
|
||||
fakeScrollTop: scrollTop,
|
||||
scrollHeight: scrollHeight,
|
||||
};
|
||||
// const messagePropsBeforeUpdate = this.props.messagesProps;
|
||||
// const oldTopMessageId = messagePropsBeforeUpdate.length
|
||||
// ? messagePropsBeforeUpdate[messagePropsBeforeUpdate.length - 1].propsForMessage.id
|
||||
// : undefined;
|
||||
// console.warn('oldTopMessageId', oldTopMessageId);
|
||||
// const messageContainer = this.props.messageContainerRef.current;
|
||||
// const scrollTop = messageContainer?.scrollTop || undefined;
|
||||
// const scrollHeight = messageContainer?.scrollHeight || undefined;
|
||||
// // as we use column-reverse for displaying message list
|
||||
// // the top is < 0
|
||||
// const realScrollTop = scrollHeight && scrollTop ? scrollHeight + scrollTop : undefined;
|
||||
// return {
|
||||
// realScrollTop,
|
||||
// fakeScrollTop: scrollTop,
|
||||
// scrollHeight: scrollHeight,
|
||||
// oldTopMessageId,
|
||||
// };
|
||||
}
|
||||
|
||||
public render() {
|
||||
|
@ -180,6 +183,7 @@ class SessionMessagesListContainerInner extends React.Component<Props> {
|
|||
|
||||
<SessionMessagesList
|
||||
scrollToQuoteMessage={this.scrollToQuoteMessage}
|
||||
scrollAfterLoadMore={this.scrollToMessage}
|
||||
onPageDownPressed={this.scrollPgDown}
|
||||
onPageUpPressed={this.scrollPgUp}
|
||||
onHomePressed={this.scrollTop}
|
||||
|
@ -207,6 +211,8 @@ class SessionMessagesListContainerInner extends React.Component<Props> {
|
|||
return;
|
||||
}
|
||||
|
||||
console.warn('firstUnreadOnOpen', firstUnreadOnOpen);
|
||||
|
||||
if (
|
||||
(conversation.unreadCount && conversation.unreadCount <= 0) ||
|
||||
firstUnreadOnOpen === undefined
|
||||
|
@ -218,6 +224,7 @@ class SessionMessagesListContainerInner extends React.Component<Props> {
|
|||
const firstUnreadIndex = messagesProps.findIndex(
|
||||
m => m.propsForMessage.id === firstUnreadOnOpen
|
||||
);
|
||||
console.warn('firstUnreadIndex', firstUnreadIndex);
|
||||
|
||||
if (firstUnreadIndex === -1) {
|
||||
// the first unread message is not in the 30 most recent messages
|
||||
|
@ -260,8 +267,10 @@ class SessionMessagesListContainerInner extends React.Component<Props> {
|
|||
}
|
||||
}
|
||||
|
||||
private scrollToMessage(messageId: string, block: 'center' | 'end' | 'nearest' | 'start') {
|
||||
private scrollToMessage(messageId: string, block: ScrollLogicalPosition | undefined) {
|
||||
const messageElementDom = document.getElementById(`msg-${messageId}`);
|
||||
console.warn('scrollToMessage', messageElementDom);
|
||||
debugger;
|
||||
messageElementDom?.scrollIntoView({
|
||||
behavior: 'auto',
|
||||
block,
|
||||
|
|
|
@ -37,6 +37,7 @@ async function getMediaGalleryProps(
|
|||
documents: Array<MediaItemType>;
|
||||
media: Array<MediaItemType>;
|
||||
}> {
|
||||
console.warn('getMediaGalleryProps');
|
||||
// We fetch more documents than media as they don’t require to be loaded
|
||||
// into memory right away. Revisit this once we have infinite scrolling:
|
||||
const rawMedia = await getMessagesWithVisualMediaAttachments(conversationId, {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { getMessageById, getMessagesByConversation } from '../../../../data/data';
|
||||
import { getLastMessagesByConversation, getMessageById } from '../../../../data/data';
|
||||
import { getConversationController } from '../../../../session/conversations';
|
||||
import { AttachmentDownloads } from '../../../../session/utils';
|
||||
import { updateConfirmModal } from '../../../../state/ducks/modalDialog';
|
||||
|
@ -42,9 +42,7 @@ export const ClickToTrustSender = (props: { messageId: string }) => {
|
|||
onClickOk: async () => {
|
||||
convo.set({ isTrustedForAttachmentDownload: true });
|
||||
await convo.commit();
|
||||
const messagesInConvo = await getMessagesByConversation(convo.id, {
|
||||
limit: 100,
|
||||
});
|
||||
const messagesInConvo = await getLastMessagesByConversation(convo.id, 100, false);
|
||||
|
||||
await Promise.all(
|
||||
messagesInConvo.map(async message => {
|
||||
|
|
|
@ -12,7 +12,6 @@ import { messageExpired } from '../../../../state/ducks/conversations';
|
|||
import {
|
||||
getGenericReadableMessageSelectorProps,
|
||||
getIsMessageSelected,
|
||||
getQuotedMessageToAnimate,
|
||||
isMessageSelectionMode,
|
||||
} from '../../../../state/selectors/conversations';
|
||||
import { getIncrement } from '../../../../util/timer';
|
||||
|
|
|
@ -3,15 +3,14 @@ import React, { useCallback } from 'react';
|
|||
import { InView } from 'react-intersection-observer';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { getMessageById } from '../../../../data/data';
|
||||
import { Constants } from '../../../../session';
|
||||
import { getConversationController } from '../../../../session/conversations';
|
||||
import {
|
||||
fetchMessagesForConversation,
|
||||
fetchTopMessagesForConversation,
|
||||
markConversationFullyRead,
|
||||
showScrollToBottomButton,
|
||||
} from '../../../../state/ducks/conversations';
|
||||
import {
|
||||
areMoreMessagesBeingFetched,
|
||||
areMoreTopMessagesBeingFetched,
|
||||
getHaveDoneFirstScroll,
|
||||
getLoadedMessagesLength,
|
||||
getMostRecentMessageId,
|
||||
|
@ -29,13 +28,12 @@ type ReadableMessageProps = {
|
|||
onContextMenu?: (e: React.MouseEvent<HTMLElement>) => void;
|
||||
};
|
||||
|
||||
const debouncedTriggerLoadMore = _.debounce(
|
||||
(loadedMessagesLength: number, selectedConversationKey: string | undefined) => {
|
||||
const numMessages = loadedMessagesLength + Constants.CONVERSATION.DEFAULT_MESSAGE_FETCH_COUNT;
|
||||
const debouncedTriggerLoadMoreTop = _.debounce(
|
||||
(selectedConversationKey: string, oldestMessageId: string) => {
|
||||
(window.inboxStore?.dispatch as any)(
|
||||
fetchMessagesForConversation({
|
||||
conversationKey: selectedConversationKey as string,
|
||||
count: numMessages,
|
||||
fetchTopMessagesForConversation({
|
||||
conversationKey: selectedConversationKey,
|
||||
oldTopMessageId: oldestMessageId,
|
||||
})
|
||||
);
|
||||
},
|
||||
|
@ -53,7 +51,7 @@ export const ReadableMessage = (props: ReadableMessageProps) => {
|
|||
const haveDoneFirstScroll = useSelector(getHaveDoneFirstScroll);
|
||||
const mostRecentMessageId = useSelector(getMostRecentMessageId);
|
||||
const oldestMessageId = useSelector(getOldestMessageId);
|
||||
const fetchingMore = useSelector(areMoreMessagesBeingFetched);
|
||||
const fetchingMore = useSelector(areMoreTopMessagesBeingFetched);
|
||||
const shouldMarkReadWhenVisible = isUnread;
|
||||
|
||||
const onVisible = useCallback(
|
||||
|
@ -83,8 +81,14 @@ export const ReadableMessage = (props: ReadableMessageProps) => {
|
|||
}
|
||||
}
|
||||
|
||||
if (inView === true && isAppFocused && oldestMessageId === messageId && !fetchingMore) {
|
||||
debouncedTriggerLoadMore(loadedMessagesLength, selectedConversationKey);
|
||||
if (
|
||||
inView === true &&
|
||||
isAppFocused &&
|
||||
oldestMessageId === messageId &&
|
||||
!fetchingMore &&
|
||||
selectedConversationKey
|
||||
) {
|
||||
debouncedTriggerLoadMoreTop(selectedConversationKey, oldestMessageId);
|
||||
}
|
||||
|
||||
// this part is just handling the marking of the message as read if needed
|
||||
|
@ -115,7 +119,6 @@ export const ReadableMessage = (props: ReadableMessageProps) => {
|
|||
receivedAt,
|
||||
shouldMarkReadWhenVisible,
|
||||
messageId,
|
||||
debouncedTriggerLoadMore,
|
||||
]
|
||||
);
|
||||
|
||||
|
|
|
@ -3,11 +3,7 @@ import React from 'react';
|
|||
import { useDispatch, useSelector } from 'react-redux';
|
||||
// tslint:disable-next-line: no-submodule-imports
|
||||
import useUpdate from 'react-use/lib/useUpdate';
|
||||
import {
|
||||
createOrUpdateItem,
|
||||
fillWithTestData,
|
||||
hasLinkPreviewPopupBeenDisplayed,
|
||||
} from '../../../data/data';
|
||||
import { createOrUpdateItem, hasLinkPreviewPopupBeenDisplayed } from '../../../data/data';
|
||||
import { ToastUtils } from '../../../session/utils';
|
||||
import { updateConfirmModal } from '../../../state/ducks/modalDialog';
|
||||
import { toggleAudioAutoplay } from '../../../state/ducks/userConfig';
|
||||
|
|
|
@ -125,6 +125,7 @@ const channelsToMake = {
|
|||
getOutgoingWithoutExpiresAt,
|
||||
getNextExpiringMessage,
|
||||
getMessagesByConversation,
|
||||
getLastMessagesByConversation,
|
||||
getFirstUnreadMessageIdInConversation,
|
||||
hasConversationOutgoingMessage,
|
||||
getSeenMessagesByHashList,
|
||||
|
@ -753,18 +754,45 @@ export async function getUnreadCountByConversation(conversationId: string): Prom
|
|||
|
||||
export async function getMessagesByConversation(
|
||||
conversationId: string,
|
||||
{ limit = 100, receivedAt = Number.MAX_VALUE, type = '%', skipTimerInit = false }
|
||||
{
|
||||
skipTimerInit = false,
|
||||
messageId = null,
|
||||
}: { limit?: number; skipTimerInit?: false; messageId: string | null }
|
||||
): Promise<MessageCollection> {
|
||||
const messages = await channels.getMessagesByConversation(conversationId, {
|
||||
limit,
|
||||
receivedAt,
|
||||
type,
|
||||
messageId,
|
||||
});
|
||||
if (skipTimerInit) {
|
||||
for (const message of messages) {
|
||||
message.skipTimerInit = skipTimerInit;
|
||||
}
|
||||
}
|
||||
console.warn('messages length got: ', messages.length);
|
||||
return new MessageCollection(messages);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function should only be used when you don't want to render the messages.
|
||||
* It just grabs the last messages of a conversation.
|
||||
*
|
||||
* To be used when you want for instance to remove messages from a conversations, in order.
|
||||
* Or to trigger downloads of a attachments from a just approved contact (clicktotrustSender)
|
||||
* @param conversationId the conversationId to fetch messages from
|
||||
* @param limit the maximum number of messages to return
|
||||
* @param skipTimerInit see MessageModel.skipTimerInit
|
||||
* @returns the fetched messageModels
|
||||
*/
|
||||
export async function getLastMessagesByConversation(
|
||||
conversationId: string,
|
||||
limit: number,
|
||||
skipTimerInit: boolean
|
||||
): Promise<MessageCollection> {
|
||||
const messages = await channels.getLastMessagesByConversation(conversationId, limit);
|
||||
if (skipTimerInit) {
|
||||
for (const message of messages) {
|
||||
message.skipTimerInit = skipTimerInit;
|
||||
}
|
||||
}
|
||||
return new MessageCollection(messages);
|
||||
}
|
||||
|
||||
|
@ -795,12 +823,10 @@ export async function getSeenMessagesByHashList(hashes: Array<string>): Promise<
|
|||
export async function removeAllMessagesInConversation(conversationId: string): Promise<void> {
|
||||
let messages;
|
||||
do {
|
||||
// Yes, we really want the await in the loop. We're deleting 100 at a
|
||||
// Yes, we really want the await in the loop. We're deleting 500 at a
|
||||
// time so we don't use too much memory.
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
messages = await getMessagesByConversation(conversationId, {
|
||||
limit: 500,
|
||||
});
|
||||
messages = await getLastMessagesByConversation(conversationId, 500, false);
|
||||
if (!messages.length) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import { MessageModel } from './message';
|
|||
import { MessageAttributesOptionals, MessageModelType } from './messageType';
|
||||
import autoBind from 'auto-bind';
|
||||
import {
|
||||
getMessagesByConversation,
|
||||
getLastMessagesByConversation,
|
||||
getUnreadByConversation,
|
||||
getUnreadCountByConversation,
|
||||
removeMessage as dataRemoveMessage,
|
||||
|
@ -787,10 +787,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
|
|||
if (!this.get('active_at')) {
|
||||
return;
|
||||
}
|
||||
const messages = await getMessagesByConversation(this.id, {
|
||||
limit: 1,
|
||||
skipTimerInit: true,
|
||||
});
|
||||
const messages = await getLastMessagesByConversation(this.id, 1, true);
|
||||
const lastMessageModel = messages.at(0);
|
||||
const lastMessageJSON = lastMessageModel ? lastMessageModel.toJSON() : null;
|
||||
const lastMessageStatusModel = lastMessageModel
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { Constants } from '../../session';
|
||||
import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||
import { getConversationController } from '../../session/conversations';
|
||||
import { getFirstUnreadMessageIdInConversation, getMessagesByConversation } from '../../data/data';
|
||||
|
@ -273,7 +272,8 @@ export type ConversationsStateType = {
|
|||
selectedMessageIds: Array<string>;
|
||||
lightBox?: LightBoxOptions;
|
||||
quotedMessage?: ReplyingToMessageProps;
|
||||
areMoreMessagesBeingFetched: boolean;
|
||||
areMoreTopMessagesBeingFetched: boolean;
|
||||
oldTopMessageId: string | null;
|
||||
haveDoneFirstScroll: boolean;
|
||||
|
||||
showScrollButton: boolean;
|
||||
|
@ -287,28 +287,23 @@ export type MentionsMembersType = Array<{
|
|||
authorProfileName: string;
|
||||
}>;
|
||||
|
||||
async function getMessages(
|
||||
conversationKey: string,
|
||||
numMessagesToFetch: number
|
||||
): Promise<Array<MessageModelPropsWithoutConvoProps>> {
|
||||
async function getMessages({
|
||||
conversationKey,
|
||||
messageId,
|
||||
}: {
|
||||
conversationKey: string;
|
||||
messageId: string | null;
|
||||
}): Promise<Array<MessageModelPropsWithoutConvoProps>> {
|
||||
const conversation = getConversationController().get(conversationKey);
|
||||
if (!conversation) {
|
||||
// no valid conversation, early return
|
||||
window?.log?.error('Failed to get convo on reducer.');
|
||||
return [];
|
||||
}
|
||||
let msgCount = numMessagesToFetch;
|
||||
msgCount =
|
||||
msgCount > Constants.CONVERSATION.MAX_MESSAGE_FETCH_COUNT
|
||||
? Constants.CONVERSATION.MAX_MESSAGE_FETCH_COUNT
|
||||
: msgCount;
|
||||
|
||||
if (msgCount < Constants.CONVERSATION.DEFAULT_MESSAGE_FETCH_COUNT) {
|
||||
msgCount = Constants.CONVERSATION.DEFAULT_MESSAGE_FETCH_COUNT;
|
||||
}
|
||||
console.warn('getMessages with messageId', messageId);
|
||||
|
||||
const messageSet = await getMessagesByConversation(conversationKey, {
|
||||
limit: msgCount,
|
||||
messageId,
|
||||
});
|
||||
|
||||
const messageProps: Array<MessageModelPropsWithoutConvoProps> = messageSet.models.map(m =>
|
||||
|
@ -325,24 +320,27 @@ export type SortedMessageModelProps = MessageModelPropsWithoutConvoProps & {
|
|||
type FetchedMessageResults = {
|
||||
conversationKey: string;
|
||||
messagesProps: Array<MessageModelPropsWithoutConvoProps>;
|
||||
oldTopMessageId: string | null;
|
||||
};
|
||||
|
||||
export const fetchMessagesForConversation = createAsyncThunk(
|
||||
export const fetchTopMessagesForConversation = createAsyncThunk(
|
||||
'messages/fetchByConversationKey',
|
||||
async ({
|
||||
conversationKey,
|
||||
count,
|
||||
oldTopMessageId,
|
||||
}: {
|
||||
conversationKey: string;
|
||||
count: number;
|
||||
oldTopMessageId: string | null;
|
||||
}): Promise<FetchedMessageResults> => {
|
||||
console.warn('fetchTopMessagesForConversation oldTop:', oldTopMessageId);
|
||||
const beforeTimestamp = Date.now();
|
||||
// tslint:disable-next-line: no-console
|
||||
perfStart('fetchMessagesForConversation');
|
||||
const messagesProps = await getMessages(conversationKey, count);
|
||||
perfStart('fetchTopMessagesForConversation');
|
||||
const messagesProps = await getMessages({
|
||||
conversationKey,
|
||||
messageId: oldTopMessageId,
|
||||
});
|
||||
const afterTimestamp = Date.now();
|
||||
// tslint:disable-next-line: no-console
|
||||
perfEnd('fetchMessagesForConversation', 'fetchMessagesForConversation');
|
||||
perfEnd('fetchTopMessagesForConversation', 'fetchTopMessagesForConversation');
|
||||
|
||||
const time = afterTimestamp - beforeTimestamp;
|
||||
window?.log?.info(`Loading ${messagesProps.length} messages took ${time}ms to load.`);
|
||||
|
@ -350,6 +348,7 @@ export const fetchMessagesForConversation = createAsyncThunk(
|
|||
return {
|
||||
conversationKey,
|
||||
messagesProps,
|
||||
oldTopMessageId,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
@ -363,11 +362,12 @@ export function getEmptyConversationState(): ConversationsStateType {
|
|||
messageDetailProps: undefined,
|
||||
showRightPanel: false,
|
||||
selectedMessageIds: [],
|
||||
areMoreMessagesBeingFetched: false,
|
||||
areMoreTopMessagesBeingFetched: false,
|
||||
showScrollButton: false,
|
||||
mentionMembers: [],
|
||||
firstUnreadMessageId: undefined,
|
||||
haveDoneFirstScroll: false,
|
||||
oldTopMessageId: null,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -697,7 +697,7 @@ const conversationsSlice = createSlice({
|
|||
conversationLookup: state.conversationLookup,
|
||||
|
||||
selectedConversation: action.payload.id,
|
||||
areMoreMessagesBeingFetched: false,
|
||||
areMoreTopMessagesBeingFetched: false,
|
||||
messages: action.payload.initialMessages,
|
||||
showRightPanel: false,
|
||||
selectedMessageIds: [],
|
||||
|
@ -708,6 +708,7 @@ const conversationsSlice = createSlice({
|
|||
nextMessageToPlay: undefined,
|
||||
showScrollButton: false,
|
||||
animateQuotedMessageId: undefined,
|
||||
oldTopMessageId: null,
|
||||
mentionMembers: [],
|
||||
firstUnreadMessageId: action.payload.firstUnreadIdOnOpen,
|
||||
|
||||
|
@ -762,26 +763,28 @@ const conversationsSlice = createSlice({
|
|||
extraReducers: (builder: any) => {
|
||||
// Add reducers for additional action types here, and handle loading state as needed
|
||||
builder.addCase(
|
||||
fetchMessagesForConversation.fulfilled,
|
||||
fetchTopMessagesForConversation.fulfilled,
|
||||
(state: ConversationsStateType, action: PayloadAction<FetchedMessageResults>) => {
|
||||
// this is called once the messages are loaded from the db for the currently selected conversation
|
||||
const { messagesProps, conversationKey } = action.payload;
|
||||
const { messagesProps, conversationKey, oldTopMessageId } = action.payload;
|
||||
// double check that this update is for the shown convo
|
||||
if (conversationKey === state.selectedConversation) {
|
||||
console.warn('fullfilled', oldTopMessageId);
|
||||
return {
|
||||
...state,
|
||||
oldTopMessageId,
|
||||
messages: messagesProps,
|
||||
areMoreMessagesBeingFetched: false,
|
||||
areMoreTopMessagesBeingFetched: false,
|
||||
};
|
||||
}
|
||||
return state;
|
||||
}
|
||||
);
|
||||
builder.addCase(fetchMessagesForConversation.pending, (state: ConversationsStateType) => {
|
||||
state.areMoreMessagesBeingFetched = true;
|
||||
builder.addCase(fetchTopMessagesForConversation.pending, (state: ConversationsStateType) => {
|
||||
state.areMoreTopMessagesBeingFetched = true;
|
||||
});
|
||||
builder.addCase(fetchMessagesForConversation.rejected, (state: ConversationsStateType) => {
|
||||
state.areMoreMessagesBeingFetched = false;
|
||||
builder.addCase(fetchTopMessagesForConversation.rejected, (state: ConversationsStateType) => {
|
||||
state.areMoreTopMessagesBeingFetched = false;
|
||||
});
|
||||
},
|
||||
});
|
||||
|
@ -855,7 +858,10 @@ export async function openConversationWithMessages(args: {
|
|||
// preload 30 messages
|
||||
perfStart('getMessages');
|
||||
|
||||
const initialMessages = await getMessages(conversationKey, 30);
|
||||
const initialMessages = await getMessages({
|
||||
conversationKey,
|
||||
messageId: null,
|
||||
});
|
||||
perfEnd('getMessages', 'getMessages');
|
||||
|
||||
window.inboxStore?.dispatch(
|
||||
|
|
|
@ -590,9 +590,9 @@ export const getQuotedMessage = createSelector(
|
|||
(state: ConversationsStateType): ReplyingToMessageProps | undefined => state.quotedMessage
|
||||
);
|
||||
|
||||
export const areMoreMessagesBeingFetched = createSelector(
|
||||
export const areMoreTopMessagesBeingFetched = createSelector(
|
||||
getConversations,
|
||||
(state: ConversationsStateType): boolean => state.areMoreMessagesBeingFetched || false
|
||||
(state: ConversationsStateType): boolean => state.areMoreTopMessagesBeingFetched || false
|
||||
);
|
||||
|
||||
export const getHaveDoneFirstScroll = createSelector(
|
||||
|
@ -1116,3 +1116,8 @@ export const getGenericReadableMessageSelectorProps = createSelector(
|
|||
return msgProps;
|
||||
}
|
||||
);
|
||||
|
||||
export const getOldTopMessageId = createSelector(
|
||||
getConversations,
|
||||
(state: ConversationsStateType): string | null => state.oldTopMessageId || null
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue