store messages currently loaded in convo on redux
parent
221f264de6
commit
e45ce43e01
@ -1,95 +0,0 @@
|
||||
/* global Whisper: false */
|
||||
|
||||
// eslint-disable-next-line func-names
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
window.Whisper = window.Whisper || {};
|
||||
|
||||
Whisper.MessageView = Whisper.View.extend({
|
||||
tagName: 'li',
|
||||
id() {
|
||||
return this.model.id;
|
||||
},
|
||||
initialize() {
|
||||
this.listenTo(this.model, 'change', this.onChange);
|
||||
this.listenTo(this.model, 'destroy', this.onDestroy);
|
||||
this.listenTo(this.model, 'unload', this.onUnload);
|
||||
this.listenTo(this.model, 'expired', this.onExpired);
|
||||
},
|
||||
onChange() {
|
||||
this.addId();
|
||||
},
|
||||
addId() {
|
||||
// The ID is important for other items inserting themselves into the DOM. Because
|
||||
// of ReactWrapperView and this view, there are two layers of DOM elements
|
||||
// between the parent and the elements returned by the React component, so this is
|
||||
// necessary.
|
||||
const { id } = this.model;
|
||||
this.$el.attr('id', id);
|
||||
},
|
||||
onExpired() {
|
||||
setTimeout(() => this.onUnload(), 1000);
|
||||
},
|
||||
onUnload() {
|
||||
if (this.childView) {
|
||||
this.childView.remove();
|
||||
}
|
||||
|
||||
this.remove();
|
||||
},
|
||||
onDestroy() {
|
||||
this.onUnload();
|
||||
},
|
||||
getRenderInfo() {
|
||||
return null;
|
||||
},
|
||||
render() {
|
||||
this.addId();
|
||||
|
||||
if (this.childView) {
|
||||
this.childView.remove();
|
||||
this.childView = null;
|
||||
}
|
||||
|
||||
const { Component, props } = this.getRenderInfo();
|
||||
this.childView = new Whisper.ReactWrapperView({
|
||||
className: 'message-wrapper',
|
||||
Component,
|
||||
props,
|
||||
});
|
||||
|
||||
const update = () => {
|
||||
const info = this.getRenderInfo();
|
||||
this.childView.update(info.props);
|
||||
};
|
||||
|
||||
this.listenTo(this.model, 'change', update);
|
||||
this.listenTo(this.model, 'expired', update);
|
||||
|
||||
const applicableConversationChanges =
|
||||
'change:color change:name change:number change:profileName change:profileAvatar';
|
||||
|
||||
this.conversation = this.model.getConversation();
|
||||
this.listenTo(this.conversation, applicableConversationChanges, update);
|
||||
|
||||
this.fromContact = this.model.getIncomingContact();
|
||||
if (this.fromContact) {
|
||||
this.listenTo(this.fromContact, applicableConversationChanges, update);
|
||||
}
|
||||
|
||||
this.quotedContact = this.model.getQuoteContact();
|
||||
if (this.quotedContact) {
|
||||
this.listenTo(
|
||||
this.quotedContact,
|
||||
applicableConversationChanges,
|
||||
update
|
||||
);
|
||||
}
|
||||
|
||||
this.$el.append(this.childView.el);
|
||||
|
||||
return this;
|
||||
},
|
||||
});
|
||||
})();
|
@ -1,3 +1,116 @@
|
||||
export const reducer = (state: any, _action: any) => {
|
||||
return state;
|
||||
import { Constants } from '../../session';
|
||||
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
|
||||
import _ from 'lodash';
|
||||
import { MessageType } from './conversations';
|
||||
|
||||
export type MessagesStateType = Array<MessageType>;
|
||||
|
||||
export async function getMessages(
|
||||
conversationKey: string,
|
||||
numMessages: number
|
||||
) : Promise<MessagesStateType> {
|
||||
const conversation = window.ConversationController.get(conversationKey);
|
||||
if (!conversation) {
|
||||
// no valid conversation, early return
|
||||
window.log.error('Failed to get convo on reducer.');
|
||||
return [];
|
||||
}
|
||||
const unreadCount = await conversation.getUnreadCount();
|
||||
let msgCount =
|
||||
numMessages ||
|
||||
Number(Constants.CONVERSATION.DEFAULT_MESSAGE_FETCH_COUNT) + unreadCount;
|
||||
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 window.Signal.Data.getMessagesByConversation(
|
||||
conversationKey,
|
||||
{ limit: msgCount, MessageCollection: window.Whisper.MessageCollection }
|
||||
);
|
||||
|
||||
// Set first member of series here.
|
||||
const messageModels = messageSet.models;
|
||||
|
||||
const messages = [];
|
||||
// no need to do that `firstMessageOfSeries` on a private chat
|
||||
if (conversation.isPrivate()) {
|
||||
return messageSet.models;
|
||||
}
|
||||
|
||||
// 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 authori
|
||||
for (let i = 0; i < messageModels.length; i++) {
|
||||
// Handle firstMessageOfSeries for conditional avatar rendering
|
||||
let firstMessageOfSeries = true;
|
||||
const currentSender = messageModels[i].propsForMessage?.authorPhoneNumber;
|
||||
const nextSender =
|
||||
i < messageModels.length - 1
|
||||
? messageModels[i + 1].propsForMessage?.authorPhoneNumber
|
||||
: undefined;
|
||||
if (i > 0 && currentSender === nextSender) {
|
||||
firstMessageOfSeries = false;
|
||||
}
|
||||
messages.push({ ...messageModels[i], firstMessageOfSeries });
|
||||
}
|
||||
return messages;
|
||||
}
|
||||
|
||||
// ACTIONS
|
||||
const fetchMessagesForConversation = createAsyncThunk(
|
||||
'messages/fetchByConversationKey',
|
||||
async ({
|
||||
conversationKey,
|
||||
count,
|
||||
}: {
|
||||
conversationKey: string;
|
||||
count: number;
|
||||
}) => {
|
||||
return getMessages(conversationKey, count);
|
||||
}
|
||||
);
|
||||
|
||||
const toOmitFromMessageModel = [
|
||||
'cid',
|
||||
'collection',
|
||||
// 'changing',
|
||||
// 'previousAttributes',
|
||||
'_events',
|
||||
'_listeningTo',
|
||||
];
|
||||
|
||||
const messageSlice = createSlice({
|
||||
name: 'messages',
|
||||
initialState: [] as MessagesStateType,
|
||||
reducers: {
|
||||
messageChanged(state, action){
|
||||
console.log('message changed ', state, action)
|
||||
const messageInStoreIndex = state.findIndex(m => m.id === action.payload.id);
|
||||
if (messageInStoreIndex >= 0) {
|
||||
state[messageInStoreIndex] = _.omit(action.payload, toOmitFromMessageModel)
|
||||
;
|
||||
}
|
||||
return state;
|
||||
},
|
||||
},
|
||||
extraReducers: {
|
||||
// Add reducers for additional action types here, and handle loading state as needed
|
||||
[fetchMessagesForConversation.fulfilled.type]: (state, action) => {
|
||||
const lightMessages = action.payload.map((m: any) =>
|
||||
_.omit(m, toOmitFromMessageModel)
|
||||
);
|
||||
return lightMessages;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const actions = {
|
||||
...messageSlice.actions,
|
||||
fetchMessagesForConversation,
|
||||
};
|
||||
export const reducer = messageSlice.reducer;
|
||||
|
Loading…
Reference in New Issue