session-desktop/ts/state/ducks/conversations.ts

809 lines
21 KiB
TypeScript
Raw Normal View History

2020-11-16 04:45:13 +01:00
import _, { omit } from 'lodash';
2019-01-14 22:49:58 +01:00
2020-11-16 04:45:13 +01:00
import { Constants } from '../../session';
import { createAsyncThunk } from '@reduxjs/toolkit';
import { getConversationController } from '../../session/conversations';
import { MessageModel } from '../../models/message';
2021-02-15 05:16:38 +01:00
import { getMessagesByConversation } from '../../data/data';
import {
ConversationNotificationSettingType,
ConversationTypeEnum,
} from '../../models/conversation';
import {
MessageDeliveryStatus,
MessageModelType,
PropsForDataExtractionNotification,
} from '../../models/messageType';
import { NotificationForConvoOption } from '../../components/conversation/ConversationHeader';
export type MessageModelProps = {
propsForMessage: PropsForMessage;
propsForSearchResult: PropsForSearchResults | null;
propsForGroupInvitation: PropsForGroupInvitation | null;
propsForTimerNotification: PropsForExpirationTimer | null;
propsForDataExtractionNotification: PropsForDataExtractionNotification | null;
propsForGroupNotification: PropsForGroupUpdate | null;
2019-01-14 22:49:58 +01:00
};
2020-11-16 04:45:13 +01:00
export type MessagePropsDetails = {};
2020-11-16 04:45:13 +01:00
2021-06-15 02:12:43 +02:00
export type LastMessageStatusType = MessageDeliveryStatus | null;
2021-07-05 03:23:47 +02:00
export type FindAndFormatContactType = {
phoneNumber: string;
avatarPath: string | null;
name: string | null;
profileName: string | null;
title: string | null;
isMe: boolean;
2021-07-05 03:23:47 +02:00
};
export type PropsForExpirationTimer = {
timespan: string;
2021-07-05 03:23:47 +02:00
disabled: boolean;
phoneNumber: string;
avatarPath: string | null;
name: string | null;
profileName: string | null;
title: string | null;
type: 'fromMe' | 'fromSync' | 'fromOther';
};
2021-07-05 03:23:47 +02:00
export type PropsForGroupUpdateGeneral = {
type: 'general';
};
export type PropsForGroupUpdateAdd = {
type: 'add';
contacts?: Array<FindAndFormatContactType>;
};
export type PropsForGroupUpdateKicked = {
type: 'kicked';
isMe: boolean;
contacts?: Array<FindAndFormatContactType>;
};
export type PropsForGroupUpdateRemove = {
type: 'remove';
isMe: boolean;
contacts?: Array<FindAndFormatContactType>;
};
export type PropsForGroupUpdateName = {
type: 'name';
newName: string;
};
export type PropsForGroupUpdateType =
2021-07-05 03:23:47 +02:00
| PropsForGroupUpdateGeneral
| PropsForGroupUpdateAdd
| PropsForGroupUpdateKicked
| PropsForGroupUpdateName
| PropsForGroupUpdateRemove;
export type PropsForGroupUpdateArray = Array<PropsForGroupUpdateType>;
2021-07-05 03:23:47 +02:00
export type PropsForGroupUpdate = {
changes: PropsForGroupUpdateArray;
};
export type PropsForGroupInvitation = {
serverName: string;
url: string;
direction: MessageModelType;
acceptUrl: string;
messageId: string;
};
export type PropsForSearchResults = {
from: FindAndFormatContactType;
to: FindAndFormatContactType;
id: string;
conversationId: string;
receivedAt: number | undefined;
snippet?: string; //not sure about the type of snippet
};
export type PropsForAttachment = {
id?: string;
contentType: string;
size: number;
width?: number;
height?: number;
url: string;
path?: string;
fileSize: string | null;
isVoiceMessage: boolean;
pending: boolean;
fileName: string;
screenshot: {
contentType: string;
width: number;
height: number;
url?: string;
path?: string;
} | null;
thumbnail: {
contentType: string;
width: number;
height: number;
url?: string;
path?: string;
} | null;
};
export type PropsForMessage = {
text: string | null;
id: string;
direction: MessageModelType;
timestamp: number | undefined;
receivedAt: number | undefined;
serverTimestamp: number | undefined;
2021-07-06 06:22:22 +02:00
serverId: number | undefined;
status: LastMessageStatusType;
authorName: string | null;
authorProfileName: string | null;
authorPhoneNumber: string;
conversationType: ConversationTypeEnum;
convoId: string;
attachments: Array<PropsForAttachment>;
previews: any;
quote: any;
authorAvatarPath: string | null;
isUnread: boolean;
expirationLength: number;
expirationTimestamp: number | null;
isPublic: boolean;
isOpenGroupV2: boolean;
isKickedFromGroup: boolean | undefined;
isTrustedForAttachmentDownload: boolean;
weAreAdmin: boolean;
isSenderAdmin: boolean;
isDeletable: boolean;
};
2021-07-05 03:23:47 +02:00
export type LastMessageType = {
status: LastMessageStatusType;
text: string | null;
};
2021-01-29 01:29:24 +01:00
export interface ConversationType {
2019-01-14 22:49:58 +01:00
id: string;
name?: string;
2021-01-29 01:29:24 +01:00
profileName?: string;
hasNickname: boolean;
2021-01-29 01:29:24 +01:00
2019-01-14 22:49:58 +01:00
activeAt?: number;
lastMessage?: LastMessageType;
2019-01-14 22:49:58 +01:00
phoneNumber: string;
type: ConversationTypeEnum;
2019-01-14 22:49:58 +01:00
isMe: boolean;
isPublic: boolean;
isGroup: boolean;
isPrivate: boolean;
weAreAdmin: boolean;
2019-01-14 22:49:58 +01:00
unreadCount: number;
mentionedUs: boolean;
2019-01-14 22:49:58 +01:00
isSelected: boolean;
expireTimer: number;
2021-01-29 01:29:24 +01:00
2019-01-14 22:49:58 +01:00
isTyping: boolean;
isBlocked: boolean;
isKickedFromGroup: boolean;
subscriberCount: number;
left: boolean;
avatarPath?: string; // absolute filepath to the avatar
2021-01-19 01:25:03 +01:00
groupAdmins?: Array<string>; // admins for closed groups and moderators for open groups
members: Array<string>; // members for closed groups only
currentNotificationSetting: ConversationNotificationSettingType;
notificationForConvo: Array<NotificationForConvoOption>;
2021-01-29 01:29:24 +01:00
}
2019-01-14 22:49:58 +01:00
export type ConversationLookupType = {
[key: string]: ConversationType;
};
export type ConversationsStateType = {
conversationLookup: ConversationLookupType;
selectedConversation?: string;
messages: Array<SortedMessageModelProps>;
2019-01-14 22:49:58 +01:00
};
2020-11-16 04:45:13 +01:00
async function getMessages(
conversationKey: string,
numMessages: number
): Promise<Array<SortedMessageModelProps>> {
const conversation = getConversationController().get(conversationKey);
2020-11-16 04:45:13 +01:00
if (!conversation) {
// no valid conversation, early return
window?.log?.error('Failed to get convo on reducer.');
2020-11-16 04:45:13 +01:00
return [];
}
2021-02-15 05:16:38 +01:00
const unreadCount = await conversation.getUnreadCount();
2020-11-16 04:45:13 +01:00
let msgCount =
2021-04-22 10:03:58 +02:00
numMessages || Number(Constants.CONVERSATION.DEFAULT_MESSAGE_FETCH_COUNT) + unreadCount;
2020-11-16 04:45:13 +01:00
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;
}
const messageSet = await getMessagesByConversation(conversationKey, {
limit: msgCount,
});
2020-11-16 04:45:13 +01:00
// Set first member of series here.
2021-07-05 09:06:34 +02:00
const messageModelsProps: Array<SortedMessageModelProps> = [];
messageSet.models.forEach(m => {
messageModelsProps.push({ ...m.getProps(), firstMessageOfSeries: true });
});
2020-11-16 04:45:13 +01:00
const isPublic = conversation.isPublic();
const sortedMessageProps = sortMessages(messageModelsProps, isPublic);
2020-11-16 04:45:13 +01:00
// no need to do that `firstMessageOfSeries` on a private chat
if (conversation.isPrivate()) {
return sortedMessageProps;
2020-11-16 04:45:13 +01:00
}
return updateFirstMessageOfSeries(sortedMessageProps);
}
2020-11-16 04:45:13 +01:00
export type SortedMessageModelProps = MessageModelProps & {
firstMessageOfSeries: boolean;
};
const updateFirstMessageOfSeries = (
messageModelsProps: Array<MessageModelProps>
): Array<SortedMessageModelProps> => {
2020-11-16 04:45:13 +01:00
// 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 message is the first of the series if the next message is not from the same author
const sortedMessageProps: Array<SortedMessageModelProps> = [];
for (let i = 0; i < messageModelsProps.length; i++) {
2020-11-16 04:45:13 +01:00
// Handle firstMessageOfSeries for conditional avatar rendering
let firstMessageOfSeries = true;
const currentSender = messageModelsProps[i].propsForMessage?.authorPhoneNumber;
2020-11-16 04:45:13 +01:00
const nextSender =
i < messageModelsProps.length - 1
? messageModelsProps[i + 1].propsForMessage?.authorPhoneNumber
2020-11-16 04:45:13 +01:00
: undefined;
if (i >= 0 && currentSender === nextSender) {
2020-11-16 04:45:13 +01:00
firstMessageOfSeries = false;
}
sortedMessageProps.push({ ...messageModelsProps[i], firstMessageOfSeries });
2020-11-16 04:45:13 +01:00
}
return sortedMessageProps;
};
type FetchedMessageResults = {
conversationKey: string;
messagesProps: Array<SortedMessageModelProps>;
};
2020-11-16 04:45:13 +01:00
const fetchMessagesForConversation = createAsyncThunk(
'messages/fetchByConversationKey',
async ({
conversationKey,
count,
}: {
conversationKey: string;
count: number;
}): Promise<FetchedMessageResults> => {
const beforeTimestamp = Date.now();
const messagesProps = await getMessages(conversationKey, count);
const afterTimestamp = Date.now();
const time = afterTimestamp - beforeTimestamp;
window?.log?.info(`Loading ${messagesProps.length} messages took ${time}ms to load.`);
2021-07-05 09:06:34 +02:00
const mapped = messagesProps.map(m => {
return {
...m,
firstMessageOfSeries: true,
};
});
2020-11-16 04:45:13 +01:00
return {
conversationKey,
2021-07-05 09:06:34 +02:00
messagesProps: mapped,
2020-11-16 04:45:13 +01:00
};
}
);
2019-01-14 22:49:58 +01:00
// Actions
type ConversationAddedActionType = {
type: 'CONVERSATION_ADDED';
payload: {
id: string;
data: ConversationType;
};
};
type ConversationChangedActionType = {
type: 'CONVERSATION_CHANGED';
payload: {
id: string;
data: ConversationType;
};
};
type ConversationRemovedActionType = {
type: 'CONVERSATION_REMOVED';
payload: {
id: string;
};
};
export type RemoveAllConversationsActionType = {
type: 'CONVERSATIONS_REMOVE_ALL';
payload: null;
};
export type MessageExpiredActionType = {
type: 'MESSAGE_EXPIRED';
payload: {
messageId: string;
conversationKey: string;
2019-01-14 22:49:58 +01:00
};
};
2020-11-16 04:45:13 +01:00
export type MessageChangedActionType = {
type: 'MESSAGE_CHANGED';
payload: MessageModelProps;
2020-11-16 04:45:13 +01:00
};
export type MessagesChangedActionType = {
type: 'MESSAGES_CHANGED';
payload: Array<MessageModelProps>;
};
2020-11-16 04:45:13 +01:00
export type MessageAddedActionType = {
type: 'MESSAGE_ADDED';
payload: {
conversationKey: string;
messageModelProps: MessageModelProps;
2020-11-16 04:45:13 +01:00
};
};
2020-11-17 03:30:24 +01:00
export type MessageDeletedActionType = {
type: 'MESSAGE_DELETED';
payload: {
conversationKey: string;
messageId: string;
};
};
export type ConversationResetActionType = {
type: 'CONVERSATION_RESET';
payload: {
conversationKey: string;
};
};
2019-01-14 22:49:58 +01:00
export type SelectedConversationChangedActionType = {
type: 'SELECTED_CONVERSATION_CHANGED';
payload: {
id: string;
messageId?: string;
};
};
2020-11-16 04:45:13 +01:00
export type FetchMessagesForConversationType = {
type: 'messages/fetchByConversationKey/fulfilled';
payload: {
conversationKey: string;
messages: Array<MessageModelProps>;
2020-11-16 04:45:13 +01:00
};
};
2019-01-14 22:49:58 +01:00
export type ConversationActionType =
| ConversationAddedActionType
| ConversationChangedActionType
| ConversationRemovedActionType
| ConversationResetActionType
2019-01-14 22:49:58 +01:00
| RemoveAllConversationsActionType
| MessageExpiredActionType
2020-11-16 04:45:13 +01:00
| MessageAddedActionType
2020-11-17 03:30:24 +01:00
| MessageDeletedActionType
2020-11-16 04:45:13 +01:00
| MessageChangedActionType
| MessagesChangedActionType
2019-03-12 01:20:16 +01:00
| SelectedConversationChangedActionType
2020-11-16 04:45:13 +01:00
| SelectedConversationChangedActionType
| FetchMessagesForConversationType;
2019-01-14 22:49:58 +01:00
// Action Creators
export const actions = {
conversationAdded,
conversationChanged,
conversationRemoved,
removeAllConversations,
messageExpired,
2020-11-16 04:45:13 +01:00
messageAdded,
2020-11-17 03:30:24 +01:00
messageDeleted,
conversationReset,
2020-11-16 04:45:13 +01:00
messageChanged,
messagesChanged,
2020-11-16 04:45:13 +01:00
fetchMessagesForConversation,
2019-01-14 22:49:58 +01:00
openConversationExternal,
};
2021-04-22 10:03:58 +02:00
function conversationAdded(id: string, data: ConversationType): ConversationAddedActionType {
2019-01-14 22:49:58 +01:00
return {
type: 'CONVERSATION_ADDED',
payload: {
id,
data,
},
};
}
2021-04-22 10:03:58 +02:00
function conversationChanged(id: string, data: ConversationType): ConversationChangedActionType {
2019-01-14 22:49:58 +01:00
return {
type: 'CONVERSATION_CHANGED',
payload: {
id,
data,
},
};
}
function conversationRemoved(id: string): ConversationRemovedActionType {
return {
type: 'CONVERSATION_REMOVED',
payload: {
id,
},
};
}
function removeAllConversations(): RemoveAllConversationsActionType {
return {
type: 'CONVERSATIONS_REMOVE_ALL',
payload: null,
};
}
2019-03-12 01:20:16 +01:00
function messageExpired({
conversationKey,
messageId,
}: {
conversationKey: string;
messageId: string;
}): MessageExpiredActionType {
2019-01-14 22:49:58 +01:00
return {
type: 'MESSAGE_EXPIRED',
payload: {
conversationKey,
messageId,
2019-01-14 22:49:58 +01:00
},
};
}
function messageChanged(messageModelProps: MessageModelProps): MessageChangedActionType {
2020-11-16 04:45:13 +01:00
return {
type: 'MESSAGE_CHANGED',
payload: messageModelProps,
2020-11-16 04:45:13 +01:00
};
}
function messagesChanged(messageModelsProps: Array<MessageModelProps>): MessagesChangedActionType {
return {
type: 'MESSAGES_CHANGED',
payload: messageModelsProps,
};
}
2020-11-16 04:45:13 +01:00
function messageAdded({
conversationKey,
messageModelProps,
2020-11-16 04:45:13 +01:00
}: {
conversationKey: string;
messageModelProps: MessageModelProps;
2020-11-16 04:45:13 +01:00
}): MessageAddedActionType {
return {
type: 'MESSAGE_ADDED',
payload: {
conversationKey,
messageModelProps,
2020-11-16 04:45:13 +01:00
},
};
}
2020-11-17 03:30:24 +01:00
function messageDeleted({
conversationKey,
messageId,
}: {
conversationKey: string;
messageId: string;
}): MessageDeletedActionType {
return {
type: 'MESSAGE_DELETED',
payload: {
conversationKey,
messageId,
},
};
}
export function conversationReset({
conversationKey,
}: {
conversationKey: string;
}): ConversationResetActionType {
return {
type: 'CONVERSATION_RESET',
payload: {
conversationKey,
},
};
}
2021-03-16 07:22:46 +01:00
export function openConversationExternal(
2019-01-14 22:49:58 +01:00
id: string,
messageId?: string
): SelectedConversationChangedActionType {
window?.log?.info(`openConversationExternal with convoId: ${id}; messageId: ${messageId}`);
2019-01-14 22:49:58 +01:00
return {
type: 'SELECTED_CONVERSATION_CHANGED',
payload: {
id,
messageId,
},
};
}
// Reducer
function getEmptyState(): ConversationsStateType {
return {
conversationLookup: {},
2020-11-16 04:45:13 +01:00
messages: [],
2019-01-14 22:49:58 +01:00
};
}
function sortMessages(
messages: Array<SortedMessageModelProps>,
isPublic: boolean
): Array<SortedMessageModelProps> {
// we order by serverTimestamp for public convos
// be sure to update the sorting order to fetch messages from the DB too at getMessagesByConversation
if (isPublic) {
2021-07-05 09:06:34 +02:00
return messages.sort((a, b) => {
return (b.propsForMessage.serverTimestamp || 0) - (a.propsForMessage.serverTimestamp || 0);
});
}
if (messages.some(n => !n.propsForMessage.timestamp && !n.propsForMessage.receivedAt)) {
throw new Error('Found some messages without any timestamp set');
}
// for non public convos, we order by sent_at or received_at timestamp.
// we assume that a message has either a sent_at or a received_at field set.
const messagesSorted = messages.sort(
2021-07-05 09:06:34 +02:00
(a, b) =>
(b.propsForMessage.timestamp || b.propsForMessage.receivedAt || 0) -
(a.propsForMessage.timestamp || a.propsForMessage.receivedAt || 0)
);
return messagesSorted;
}
2021-04-22 10:03:58 +02:00
function handleMessageAdded(state: ConversationsStateType, action: MessageAddedActionType) {
const { messages } = state;
const { conversationKey, messageModelProps: addedMessageProps } = action.payload;
if (conversationKey === state.selectedConversation) {
const messagesWithNewMessage = [
...messages,
{ ...addedMessageProps, firstMessageOfSeries: true },
];
const convo = state.conversationLookup[state.selectedConversation];
const isPublic = convo?.isPublic || false;
if (convo) {
const sortedMessage = sortMessages(messagesWithNewMessage, isPublic);
2021-04-22 10:03:58 +02:00
const updatedWithFirstMessageOfSeries = updateFirstMessageOfSeries(sortedMessage);
return {
...state,
messages: updatedWithFirstMessageOfSeries,
};
}
}
return state;
}
2021-04-22 10:03:58 +02:00
function handleMessageChanged(state: ConversationsStateType, action: MessageChangedActionType) {
const { payload } = action;
const messageInStoreIndex = state?.messages?.findIndex(
m => m.propsForMessage.id === payload.propsForMessage.id
);
if (messageInStoreIndex >= 0) {
const changedMessage = { ...payload, firstMessageOfSeries: true };
// 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),
];
const convo = state.conversationLookup[payload.propsForMessage.convoId];
const isPublic = convo?.isPublic || false;
// reorder the messages depending on the timestamp (we might have an updated serverTimestamp now)
const sortedMessage = sortMessages(editedMessages, isPublic);
2021-04-22 10:03:58 +02:00
const updatedWithFirstMessageOfSeries = updateFirstMessageOfSeries(sortedMessage);
return {
...state,
messages: updatedWithFirstMessageOfSeries,
};
}
return state;
}
2021-04-22 10:03:58 +02:00
function handleMessagesChanged(state: ConversationsStateType, action: MessagesChangedActionType) {
const { payload } = action;
payload.forEach(element => {
// tslint:disable-next-line: no-parameter-reassignment
state = handleMessageChanged(state, {
payload: element,
type: 'MESSAGE_CHANGED',
});
});
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.propsForMessage.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),
];
2021-04-22 10:03:58 +02:00
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;
}
2020-11-17 03:30:24 +01:00
// tslint:disable: cyclomatic-complexity
// tslint:disable: max-func-body-length
2019-01-14 22:49:58 +01:00
export function reducer(
state: ConversationsStateType = getEmptyState(),
2019-01-14 22:49:58 +01:00
action: ConversationActionType
): ConversationsStateType {
if (action.type === 'CONVERSATION_ADDED') {
const { payload } = action;
const { id, data } = payload;
const { conversationLookup } = state;
return {
...state,
conversationLookup: {
...conversationLookup,
[id]: data,
},
};
}
if (action.type === 'CONVERSATION_CHANGED') {
const { payload } = action;
const { id, data } = payload;
const { conversationLookup, selectedConversation } = state;
2019-03-12 01:20:16 +01:00
const existing = conversationLookup[id];
2019-01-14 22:49:58 +01:00
// In the change case we only modify the lookup if we already had that conversation
2019-03-12 01:20:16 +01:00
if (!existing) {
2019-01-14 22:49:58 +01:00
return state;
}
return {
...state,
2019-03-12 01:20:16 +01:00
selectedConversation,
2019-01-14 22:49:58 +01:00
conversationLookup: {
...conversationLookup,
[id]: data,
},
};
}
if (action.type === 'CONVERSATION_REMOVED') {
const { payload } = action;
const { id } = payload;
const { conversationLookup, selectedConversation } = state;
2019-01-14 22:49:58 +01:00
return {
...state,
conversationLookup: omit(conversationLookup, [id]),
2021-04-22 10:03:58 +02:00
selectedConversation: selectedConversation === id ? undefined : selectedConversation,
2019-01-14 22:49:58 +01:00
};
}
if (action.type === 'CONVERSATIONS_REMOVE_ALL') {
return getEmptyState();
}
2019-03-12 01:20:16 +01:00
if (action.type === 'SELECTED_CONVERSATION_CHANGED') {
const { payload } = action;
const { id } = payload;
const oldSelectedConversation = state.selectedConversation;
const newSelectedConversation = id;
if (newSelectedConversation !== oldSelectedConversation) {
// empty the message list
return {
...state,
messages: [],
selectedConversation: id,
};
}
2019-03-12 01:20:16 +01:00
return {
...state,
selectedConversation: id,
};
}
// this is called once the messages are loaded from the db for the currently selected conversation
2020-11-16 04:45:13 +01:00
if (action.type === fetchMessagesForConversation.fulfilled.type) {
const { messagesProps, conversationKey } = action.payload as FetchedMessageResults;
2020-11-16 04:45:13 +01:00
// double check that this update is for the shown convo
if (conversationKey === state.selectedConversation) {
return {
...state,
2021-07-05 09:06:34 +02:00
messages: messagesProps,
2020-11-16 04:45:13 +01:00
};
}
return state;
}
if (action.type === 'MESSAGE_CHANGED') {
return handleMessageChanged(state, action);
2020-11-16 04:45:13 +01:00
}
if (action.type === 'MESSAGES_CHANGED') {
return handleMessagesChanged(state, action);
}
2020-11-16 04:45:13 +01:00
if (action.type === 'MESSAGE_ADDED') {
return handleMessageAdded(state, action);
2020-11-16 04:45:13 +01:00
}
if (action.type === 'MESSAGE_EXPIRED' || action.type === 'MESSAGE_DELETED') {
return handleMessageExpiredOrDeleted(state, action);
2020-11-17 03:30:24 +01:00
}
if (action.type === 'CONVERSATION_RESET') {
return handleConversationReset(state, action);
}
2019-01-14 22:49:58 +01:00
return state;
}