split conversation reducer for convo logic

also
- fix ordering of messages for open groups, when they are added
- fix the way the firstMessageOfSeries for just created clsoed groups
pull/1423/head
Audric Ackermann 5 years ago
parent 1aa165c261
commit 9048913332
No known key found for this signature in database
GPG Key ID: 999F434D76324AD4

@ -325,6 +325,7 @@ export class SessionMessagesList extends React.Component<Props, State> {
messageProps.i18n = window.i18n; messageProps.i18n = window.i18n;
messageProps.selected = selected; messageProps.selected = selected;
messageProps.firstMessageOfSeries = firstMessageOfSeries; messageProps.firstMessageOfSeries = firstMessageOfSeries;
messageProps.multiSelectMode = multiSelectMode; messageProps.multiSelectMode = multiSelectMode;
messageProps.onSelectMessage = this.props.selectMessage; messageProps.onSelectMessage = this.props.selectMessage;
messageProps.onDeleteMessage = this.props.deleteMessage; messageProps.onDeleteMessage = this.props.deleteMessage;

@ -118,13 +118,6 @@ export class ConversationController {
conversation.initialPromise = create(); conversation.initialPromise = create();
conversation.initialPromise.then(async () => { conversation.initialPromise.then(async () => {
if (!conversation.isPublic() && !conversation.isRss()) {
await Promise.all([
conversation.updateProfileAvatar(),
// NOTE: we request snodes updating the cache, but ignore the result
window.SnodePool.getSnodesFor(id),
]);
}
if (window.inboxStore) { if (window.inboxStore) {
conversation.on('change', this.updateReduxConvoChanged); conversation.on('change', this.updateReduxConvoChanged);
window.inboxStore.dispatch( window.inboxStore.dispatch(
@ -134,6 +127,13 @@ export class ConversationController {
) )
); );
} }
if (!conversation.isPublic() && !conversation.isRss()) {
await Promise.all([
conversation.updateProfileAvatar(),
// NOTE: we request snodes updating the cache, but ignore the result
window.SnodePool.getSnodesFor(id),
]);
}
}); });
return conversation; return conversation;
@ -164,7 +164,10 @@ export class ConversationController {
} }
public async getOrCreateAndWait(id: any, type: string) { public async getOrCreateAndWait(id: any, type: string) {
const initialPromise = this._initialPromise !== undefined ? this._initialPromise : Promise.resolve(); const initialPromise =
this._initialPromise !== undefined
? this._initialPromise
: Promise.resolve();
return initialPromise.then(() => { return initialPromise.then(() => {
if (!id) { if (!id) {
return Promise.reject( return Promise.reject(

@ -4,6 +4,7 @@ import { Constants } from '../../session';
import { createAsyncThunk } from '@reduxjs/toolkit'; import { createAsyncThunk } from '@reduxjs/toolkit';
import { MessageModel } from '../../../js/models/messages'; import { MessageModel } from '../../../js/models/messages';
import { ConversationController } from '../../session/conversations'; import { ConversationController } from '../../session/conversations';
import { StateType } from '../reducer';
// State // State
@ -122,11 +123,18 @@ async function getMessages(
// Set first member of series here. // Set first member of series here.
const messageModels = messageSet.models; const messageModels = messageSet.models;
const isPublic = conversation.isPublic();
const sortedMessage = sortMessages(messageModels, isPublic);
// no need to do that `firstMessageOfSeries` on a private chat // no need to do that `firstMessageOfSeries` on a private chat
if (conversation.isPrivate()) { if (conversation.isPrivate()) {
return messageModels; return sortedMessage;
} }
return updateFirstMessageOfSeries(sortedMessage);
}
const updateFirstMessageOfSeries = (messageModels: Array<any>) => {
// messages are got from the more recent to the oldest, so we need to check if // messages are got from the more recent to the oldest, so we need to check if
// the next messages in the list is still the same author. // the next messages in the list is still the same author.
// The message is the first of the series if the next message is not from the same author // The message is the first of the series if the next message is not from the same author
@ -138,13 +146,13 @@ async function getMessages(
i < messageModels.length - 1 i < messageModels.length - 1
? messageModels[i + 1].propsForMessage?.authorPhoneNumber ? messageModels[i + 1].propsForMessage?.authorPhoneNumber
: undefined; : undefined;
if (i > 0 && currentSender === nextSender) { if (i >= 0 && currentSender === nextSender) {
firstMessageOfSeries = false; firstMessageOfSeries = false;
} }
messageModels[i].firstMessageOfSeries = firstMessageOfSeries; messageModels[i].firstMessageOfSeries = firstMessageOfSeries;
} }
return messageModels; return messageModels;
} };
const fetchMessagesForConversation = createAsyncThunk( const fetchMessagesForConversation = createAsyncThunk(
'messages/fetchByConversationKey', 'messages/fetchByConversationKey',
@ -422,6 +430,138 @@ function getEmptyState(): ConversationsStateType {
}; };
} }
function sortMessages(
messages: Array<MessageTypeInConvo>,
isPublic: boolean
): Array<MessageTypeInConvo> {
// we order by serverTimestamp for public convos
if (isPublic) {
return messages.sort(
(a: any, b: any) =>
b.attributes.serverTimestamp - a.attributes.serverTimestamp
);
}
return messages.sort(
(a: any, b: any) => b.attributes.timestamp - a.attributes.timestamp
);
}
function handleMessageAdded(
state: ConversationsStateType,
action: MessageAddedActionType
) {
const { messages } = state;
const { conversationKey, messageModel } = action.payload;
if (conversationKey === state.selectedConversation) {
const addedMessage = _.pick(
messageModel as any,
toPickFromMessageModel
) as MessageTypeInConvo;
const messagesWithNewMessage = [...messages, addedMessage];
const convo = state.conversationLookup[state.selectedConversation];
const isPublic = convo?.isPublic || false;
if (convo) {
const sortedMessage = sortMessages(messagesWithNewMessage, isPublic);
const updatedWithFirstMessageOfSeries = updateFirstMessageOfSeries(
sortedMessage
);
return {
...state,
messages: updatedWithFirstMessageOfSeries,
};
}
}
return state;
}
function handleMessageChanged(
state: ConversationsStateType,
action: MessageChangedActionType
) {
const messageInStoreIndex = state?.messages?.findIndex(
m => m.id === action.payload.id
);
if (messageInStoreIndex >= 0) {
const changedMessage = _.pick(
action.payload as any,
toPickFromMessageModel
) as MessageTypeInConvo;
// we cannot edit the array directly, so slice the first part, insert our edited message, and slice the second part
const editedMessages = [
...state.messages.slice(0, messageInStoreIndex),
changedMessage,
...state.messages.slice(messageInStoreIndex + 1),
];
// reorder the messages depending on the timestamp (we might have an updated serverTimestamp now)
const updatedWithFirstMessageOfSeries = updateFirstMessageOfSeries(
editedMessages
);
return {
...state,
messages: updatedWithFirstMessageOfSeries,
};
}
return state;
}
function handleMessageExpiredOrDeleted(
state: ConversationsStateType,
action: MessageDeletedActionType | MessageExpiredActionType
) {
const { conversationKey, messageId } = action.payload;
if (conversationKey === state.selectedConversation) {
// search if we find this message id.
// we might have not loaded yet, so this case might not happen
const messageInStoreIndex = state?.messages.findIndex(
m => m.id === messageId
);
if (messageInStoreIndex >= 0) {
// we cannot edit the array directly, so slice the first part, and slice the second part,
// keeping the index removed out
const editedMessages = [
...state.messages.slice(0, messageInStoreIndex),
...state.messages.slice(messageInStoreIndex + 1),
];
const updatedWithFirstMessageOfSeries = updateFirstMessageOfSeries(
editedMessages
);
// FIXME two other thing we have to do:
// * update the last message text if the message deleted was the last one
// * update the unread count of the convo if the message was the one counted as an unread
return {
...state,
messages: updatedWithFirstMessageOfSeries,
};
}
return state;
}
return state;
}
function handleConversationReset(
state: ConversationsStateType,
action: ConversationResetActionType
) {
const { conversationKey } = action.payload;
if (conversationKey === state.selectedConversation) {
// just empty the list of messages
return {
...state,
messages: [],
};
}
return state;
}
// tslint:disable: cyclomatic-complexity // tslint:disable: cyclomatic-complexity
// tslint:disable: max-func-body-length // tslint:disable: max-func-body-length
export function reducer( export function reducer(
@ -492,6 +632,7 @@ export function reducer(
const { id } = payload; const { id } = payload;
const oldSelectedConversation = state.selectedConversation; const oldSelectedConversation = state.selectedConversation;
const newSelectedConversation = id; const newSelectedConversation = id;
if (newSelectedConversation !== oldSelectedConversation) { if (newSelectedConversation !== oldSelectedConversation) {
// empty the message list // empty the message list
return { return {
@ -505,6 +646,8 @@ export function reducer(
selectedConversation: id, selectedConversation: id,
}; };
} }
// this is called once the messages are loaded from the db for the currently selected conversation
if (action.type === fetchMessagesForConversation.fulfilled.type) { if (action.type === fetchMessagesForConversation.fulfilled.type) {
const { messages, conversationKey } = action.payload as any; const { messages, conversationKey } = action.payload as any;
// double check that this update is for the shown convo // double check that this update is for the shown convo
@ -521,102 +664,18 @@ export function reducer(
} }
if (action.type === 'MESSAGE_CHANGED') { if (action.type === 'MESSAGE_CHANGED') {
const messageInStoreIndex = state?.messages?.findIndex( return handleMessageChanged(state, action);
m => m.id === action.payload.id
);
if (messageInStoreIndex >= 0) {
const changedMessage = _.pick(
action.payload as any,
toPickFromMessageModel
) as MessageTypeInConvo;
// we cannot edit the array directly, so slice the first part, insert our edited message, and slice the second part
const editedMessages = [
...state.messages.slice(0, messageInStoreIndex),
changedMessage,
...state.messages.slice(messageInStoreIndex + 1),
];
return {
...state,
messages: editedMessages,
};
}
return state;
} }
if (action.type === 'MESSAGE_ADDED') { if (action.type === 'MESSAGE_ADDED') {
const { conversationKey, messageModel } = action.payload; return handleMessageAdded(state, action);
if (conversationKey === state.selectedConversation) {
const { messages } = state;
const addedMessage = _.pick(
messageModel as any,
toPickFromMessageModel
) as MessageTypeInConvo;
const messagesWithNewMessage = [...messages, addedMessage];
const convo = state.conversationLookup[state.selectedConversation];
const isPublic = convo?.isPublic;
if (convo && isPublic) {
return {
...state,
messages: messagesWithNewMessage.sort(
(a: any, b: any) =>
b.attributes.serverTimestamp - a.attributes.serverTimestamp
),
};
}
if (convo) {
return {
...state,
messages: messagesWithNewMessage.sort(
(a, b) => b.attributes.timestamp - a.attributes.timestamp
),
};
}
}
return state;
} }
if (action.type === 'MESSAGE_EXPIRED' || action.type === 'MESSAGE_DELETED') { if (action.type === 'MESSAGE_EXPIRED' || action.type === 'MESSAGE_DELETED') {
const { conversationKey, messageId } = action.payload; return handleMessageExpiredOrDeleted(state, action);
if (conversationKey === state.selectedConversation) {
// search if we find this message id.
// we might have not loaded yet, so this case might not happen
const messageInStoreIndex = state?.messages.findIndex(
m => m.id === messageId
);
if (messageInStoreIndex >= 0) {
// we cannot edit the array directly, so slice the first part, and slice the second part,
// keeping the index removed out
const editedMessages = [
...state.messages.slice(0, messageInStoreIndex),
...state.messages.slice(messageInStoreIndex + 1),
];
// FIXME two other thing we have to do:
// * update the last message text if the message deleted was the last one
// * update the unread count of the convo if the message was one one counted as an unread
return {
...state,
messages: editedMessages,
};
}
return state;
}
return state;
} }
if (action.type === 'CONVERSATION_RESET') { if (action.type === 'CONVERSATION_RESET') {
const { conversationKey } = action.payload; return handleConversationReset(state, action);
if (conversationKey === state.selectedConversation) {
// just empty the list of messages
return {
...state,
messages: [],
};
}
return state;
} }
return state; return state;

@ -9,7 +9,6 @@ const mapStateToProps = (state: StateType) => {
(conversationKey && (conversationKey &&
state.conversations.conversationLookup[conversationKey]) || state.conversations.conversationLookup[conversationKey]) ||
null; null;
return { return {
conversation, conversation,
conversationKey, conversationKey,

Loading…
Cancel
Save