From 1689bc617ead957289998b31094cb75a15c4a99a Mon Sep 17 00:00:00 2001 From: Vincent Date: Mon, 24 Feb 2020 14:11:54 +1100 Subject: [PATCH] Message loading with redux --- js/conversation_controller.js | 39 +++++++++--- js/modules/data.js | 3 + js/views/inbox_view.js | 63 +------------------ ts/components/LeftPane.tsx | 34 +++++----- ts/components/session/SessionConversation.tsx | 41 ++++++++---- ts/global.d.ts | 1 + ts/state/ducks/conversations.ts | 11 +++- ts/state/smart/SessionConversation.tsx | 7 ++- 8 files changed, 96 insertions(+), 103 deletions(-) diff --git a/js/conversation_controller.js b/js/conversation_controller.js index 6976e6732..85cc9368a 100644 --- a/js/conversation_controller.js +++ b/js/conversation_controller.js @@ -65,20 +65,39 @@ return conversation; } - window.getMessagesByKey = async key => { - const conversation = window.getConversationByKey(key); - - // Grab messages and push to conv object. - if (!conversation.messageCollection.models.length){ - await conversation.fetchMessages(); - } - - const messagesModel = conversation.messageCollection.models; - const messages = messagesModel.map(conv => conv.attributes); + window.getMessagesByKey = async (key, loadLive = false) => { + // loadLive gets messages live, not from the database which can lag behind. + + let messages = []; + let messageSet; + + // if (loadLive){ + messageSet = await window.Signal.Data.getMessagesByConversation( + key, + { limit: 100, MessageCollection: Whisper.MessageCollection } + ); + // } else { + // const conversation = window.getConversationByKey(key); + // // Grab messages and push to conv object. + // await conversation.fetchMessages(); + // messageSet = conversation.messageCollection; + // } + + messages = messageSet.models.map(conv => conv.attributes); return messages; } + window.getLastMessageByKey = async key => { + const messageSet = await window.Signal.Data.getMessagesByConversation( + key, + { limit: 1, MessageCollection: Whisper.MessageCollection } + ); + + const message = messageSet.models.map(conv => conv.attributes)[0]; + return message; + } + window.ConversationController = { get(id) { if (!this._initialFetchComplete) { diff --git a/js/modules/data.js b/js/modules/data.js index 729f8c60e..d3bdb7b19 100644 --- a/js/modules/data.js +++ b/js/modules/data.js @@ -770,6 +770,9 @@ async function updateConversation(id, data, { Conversation }) { throw new Error(`Conversation ${id} does not exist!`); } + console.log(`[vince][update] Updating conversation ${id}`); + console.log(`[vince][update] New data:`, data); + const merged = _.merge({}, existing.attributes, data); // Merging is a really bad idea and not what we want here, e.g. diff --git a/js/views/inbox_view.js b/js/views/inbox_view.js index cc6e4c3c4..e0379c961 100644 --- a/js/views/inbox_view.js +++ b/js/views/inbox_view.js @@ -53,74 +53,15 @@ reject: onCancel, }); }, - setupSessionConversation(conversationId) { + setupSessionConversation() { // Here we set up a full redux store with initial state for our Conversation Root - const convoCollection = getConversations(); - const conversations = convoCollection.map( - conversation => conversation.cachedProps - ); - - const initialState = { - conversations: { - conversationLookup: Signal.Util.makeLookup(conversations, 'id'), - }, - user: { - regionCode: window.storage.get('regionCode'), - ourNumber: - window.storage.get('primaryDevicePubKey') || - textsecure.storage.user.getNumber(), - isSecondaryDevice: !!window.storage.get('isSecondaryDevice'), - i18n: window.i18n, - }, - }; - - const store = Signal.State.createStore(initialState); - window.conversationStore = store; this.sessionConversationView = new Whisper.ReactWrapperView({ - JSX: Signal.State.Roots.createSessionConversation(store), + JSX: Signal.State.Roots.createSessionConversation(window.inboxStore), className: 'conversation-item', }); - // Enables our redux store to be updated by backbone events in the outside world - const { - conversationAdded, - conversationChanged, - conversationRemoved, - removeAllConversations, - messageExpired, - openConversationExternal, - } = Signal.State.bindActionCreators( - Signal.State.Ducks.conversations.actions, - store.dispatch - ); - const { userChanged } = Signal.State.bindActionCreators( - Signal.State.Ducks.user.actions, - store.dispatch - ); - - this.openConversationAction = openConversationExternal; - - this.listenTo(convoCollection, 'remove', conversation => { - const { id } = conversation || {}; - conversationRemoved(id); - }); - this.listenTo(convoCollection, 'add', conversation => { - const { id, cachedProps } = conversation || {}; - conversationAdded(id, cachedProps); - }); - this.listenTo(convoCollection, 'change', conversation => { - const { id, cachedProps } = conversation || {}; - conversationChanged(id, cachedProps); - }); - this.listenTo(convoCollection, 'reset', removeAllConversations); - - Whisper.events.on('messageExpired', messageExpired); - Whisper.events.on('userChanged', userChanged); - // Add sessionConversation to the DOM - // Don't worry - this isn't fetching messages on every re-render. It's pulling - // from Redux $('#main-view .conversation-stack').html(''); $('#main-view .conversation-stack').append(this.sessionConversationView.el); }, diff --git a/ts/components/LeftPane.tsx b/ts/components/LeftPane.tsx index f65cce3e4..1b05e8c8c 100644 --- a/ts/components/LeftPane.tsx +++ b/ts/components/LeftPane.tsx @@ -28,23 +28,23 @@ interface State { } interface Props { - // conversations: Array; - // friends: Array; - // sentFriendsRequest: Array; - // receivedFriendsRequest: Array; - // unreadMessageCount: number; - // receivedFriendRequestCount: number; - // searchResults?: SearchResultsProps; - // searchTerm: string; - // isSecondaryDevice: boolean; - - // openConversationInternal: (id: string, messageId?: string) => void; - // updateSearchTerm: (searchTerm: string) => void; - // search: (query: string, options: SearchOptions) => void; - // clearSearch: () => void; + conversations: Array; + friends: Array; + sentFriendsRequest: Array; + receivedFriendsRequest: Array; + unreadMessageCount: number; + receivedFriendRequestCount: number; + searchResults?: SearchResultsProps; + searchTerm: string; + isSecondaryDevice: boolean; + + openConversationInternal: (id: string, messageId?: string) => void; + updateSearchTerm: (searchTerm: string) => void; + search: (query: string, options: SearchOptions) => void; + clearSearch: () => void; } -export class LeftPane extends React.Component { +export class LeftPane extends React.Component { public state = { selectedSection: SectionType.Message, }; @@ -52,9 +52,6 @@ export class LeftPane extends React.Component { public constructor(props: any) { super(props); this.handleSectionSelected = this.handleSectionSelected.bind(this); - - console.log(`[vince] These are my props:`, props); - } // this static function is set here to be used by all subsections (message, contacts,...) to render their headers @@ -83,6 +80,7 @@ export class LeftPane extends React.Component { } public render(): JSX.Element { + return (
{ constructor(props: any) { super(props); - const conversationKey = window.inboxStore.getState().conversations.selectedConversation; + const conversationKey = this.props.conversations.selectedConversation; this.state = { sendingProgess: 0, @@ -31,33 +32,51 @@ export class SessionConversation extends React.Component { conversationKey, messages: [], }; + + } + + public async componentWillUpdate () { + console.log(`[vince][update] State:`, this.state); + console.log(`[vince][update] Props:`, this.props); } - async componentWillMount() { - const {conversationKey} = this.state; + public async componentWillReceiveProps() { + const { conversationKey, messages } = this.state; + const conversation = this.props.conversations.conversationLookup[conversationKey]; + + // Check if another message came through + const shouldLoad = !messages.length || (conversation.lastUpdated > messages[messages.length - 1]?.received_at); + + console.log(`[vince][update] conversation:`, conversation); + console.log(`[vince][update] conversation.lastupdated: `, conversation.lastUpdated) + console.log(`[vince][update] last message received at: `, messages[messages.length - 1]?.received_at) + console.log(`[vince][update] Should Update: `, shouldLoad) + console.log(`[vince][update] called ComponentWillRevieceProps. Messages: `, this.state.messages) - if (conversationKey){ + if (conversationKey && shouldLoad){ this.setState({ - messages: await window.getMessagesByKey(conversationKey) + messages: await window.getMessagesByKey(conversationKey, true) }); } + } render() { - + console.log('[vince] SessionConversation was just rerendered!'); + console.log(`[vince] These are SessionConversation props: `, this.props); + // const headerProps = this.props.getHeaderProps; const { conversationKey } = this.state; const loadingMessages = this.state.messages.length === 0; + console.log(`[vince] My conversation key is: `, conversationKey); + // TMEPORARY SOLUTION TO GETTING CONVERSATION UNTIL // SessionConversationStack is created // Get conversation by Key (NOT cid) - const conversation = window.getConversationByKey(conversationKey); - const conversationType = conversation.attributes.type; - - console.log(`[vince] Conversation key: `, conversationKey); - console.log(`[vince] Conversation:`, conversation); + const conversation = this.props.conversations.conversationLookup[conversationKey] + const conversationType = conversation.type; return (
diff --git a/ts/global.d.ts b/ts/global.d.ts index 67fdb4cce..4b6d2ea17 100644 --- a/ts/global.d.ts +++ b/ts/global.d.ts @@ -14,6 +14,7 @@ interface Window { getConversations: any; getConversationByKey: any; getMessagesByKey: any; + getLastMessageByKey: any; getFriendsFromContacts: any; mnemonic: any; diff --git a/ts/state/ducks/conversations.ts b/ts/state/ducks/conversations.ts index 9f32c9580..0e95ef0b1 100644 --- a/ts/state/ducks/conversations.ts +++ b/ts/state/ducks/conversations.ts @@ -62,6 +62,7 @@ export type ConversationLookupType = { export type ConversationsStateType = { conversationLookup: ConversationLookupType; + messageCollection: string; selectedConversation?: string; showArchived: boolean; }; @@ -238,6 +239,7 @@ function showArchivedConversations() { function getEmptyState(): ConversationsStateType { return { conversationLookup: {}, + messageCollection: 'stuff', showArchived: false, }; } @@ -247,7 +249,7 @@ export function reducer( action: ConversationActionType ): ConversationsStateType { if (!state) { - return getEmptyState();\ + return getEmptyState(); } if (action.type === 'CONVERSATION_ADDED') { @@ -291,10 +293,17 @@ export function reducer( } } + let lastMessage: any; + window.getLastMessageByKey(id).then((message: any) => lastMessage = message); + const messages = [...state.messageCollection].push(lastMessage); + + console.log(`[vince][update] Updated messages to `, messages); + return { ...state, selectedConversation, showArchived, + messageCollection: 'afgjvhbrgvkhbrg', conversationLookup: { ...conversationLookup, [id]: data, diff --git a/ts/state/smart/SessionConversation.tsx b/ts/state/smart/SessionConversation.tsx index b578bc89d..f00b90477 100644 --- a/ts/state/smart/SessionConversation.tsx +++ b/ts/state/smart/SessionConversation.tsx @@ -9,10 +9,13 @@ const mapStateToProps = (state: StateType) => { //const conversationInfo = getSessionConversationInfo(state); // console.log(`[vince] stateToProps from SessionConversation:`, conversationInfo); - console.log(`[vince] stateToProps from SessionConversation:`,state); + // console.log(`[vince] stateToProps from SessionConversation:`, state); + + // You only want to rerender SessionConversation if the CURRENT conversation updates + // Use SelectedConversationChangedActionType FROM actions.ts return { - ...state, + conversations: state.conversations, } };