From bf5badda3bf33eaa78418e13ff6bdb8a8acc28ad Mon Sep 17 00:00:00 2001 From: Vincent Date: Fri, 21 Feb 2020 10:17:04 +1100 Subject: [PATCH] Working conversation openning --- js/background.js | 14 +- js/conversation_controller.js | 5 +- js/modules/signal.js | 2 + js/views/inbox_view.js | 134 ++++--- .../session/LeftPaneMessageSection.tsx | 4 +- ts/components/session/SessionConversation.tsx | 342 +++++++++--------- ts/state/roots/createSessionConversation.tsx | 16 + ts/state/smart/SessionConversation.tsx | 15 + 8 files changed, 295 insertions(+), 237 deletions(-) create mode 100644 ts/state/roots/createSessionConversation.tsx create mode 100644 ts/state/smart/SessionConversation.tsx diff --git a/js/background.js b/js/background.js index f5e55bea3..6ba4937bb 100644 --- a/js/background.js +++ b/js/background.js @@ -986,19 +986,7 @@ } return toastID; - }; - - window.renderConversationView = conversationKey => { - const sessionConversation = new Whisper.SessionConversationView({ - el: $('#main-view'), - conversationKey, - }); - sessionConversation.render(); - - console.log(conversationKey); - console.log(conversationKey); - console.log(conversationKey); - }; + }; window.getFriendsFromContacts = contacts => { // To call from TypeScript, input / output are both diff --git a/js/conversation_controller.js b/js/conversation_controller.js index c661f4ed6..6976e6732 100644 --- a/js/conversation_controller.js +++ b/js/conversation_controller.js @@ -69,7 +69,9 @@ const conversation = window.getConversationByKey(key); // Grab messages and push to conv object. - await conversation.fetchMessages(); + if (!conversation.messageCollection.models.length){ + await conversation.fetchMessages(); + } const messagesModel = conversation.messageCollection.models; const messages = messagesModel.map(conv => conv.attributes); @@ -77,7 +79,6 @@ return messages; } - window.ConversationController = { get(id) { if (!this._initialFetchComplete) { diff --git a/js/modules/signal.js b/js/modules/signal.js index 435b943c3..1d29fd688 100644 --- a/js/modules/signal.js +++ b/js/modules/signal.js @@ -146,6 +146,7 @@ const { // State const { createLeftPane } = require('../../ts/state/roots/createLeftPane'); +const { createSessionConversation } = require('../../ts/state/roots/createSessionConversation'); const { createStore } = require('../../ts/state/createStore'); const conversationsDuck = require('../../ts/state/ducks/conversations'); const userDuck = require('../../ts/state/ducks/user'); @@ -335,6 +336,7 @@ exports.setup = (options = {}) => { const Roots = { createLeftPane, + createSessionConversation, }; const Ducks = { conversations: conversationsDuck, diff --git a/js/views/inbox_view.js b/js/views/inbox_view.js index d57b6a824..ec9c76140 100644 --- a/js/views/inbox_view.js +++ b/js/views/inbox_view.js @@ -21,61 +21,16 @@ Whisper.ConversationStack = Whisper.View.extend({ className: 'conversation-stack', open(conversation) { - const id = `conversation-${conversation.cid}`; - const container = $('#main-view .conversation-stack'); - - // Has been opened since app start, but not focussed - const conversationExists = container.children(`#${id}`).length > 0; - // Is focussed - const conversationOpened = container.children().first().id === id; - - // To limit the size of the DOM for speed - const maxNumConversations = 10; - const numConversations = container - .children() - .not('.conversation.placeholder').length; - const shouldTrimConversations = numConversations > maxNumConversations; - - if (shouldTrimConversations) { - // Removes conversation which has been hidden the longest - container.children()[numConversations - 1].remove(); - } - - if (conversationExists) { - // User opened conversation, move it to top of stack, rather than re-rendering - const conversations = container - .children() - .not('.conversation.placeholder'); - container - .children(`#${id}`) - .first() - .insertBefore(conversations.first()); - conversation.trigger('opened'); - - return; - } + // const container = $('#main-view .conversation-stack'); + // container.html(''); - if (!conversationOpened) { - this.$el - .first() - .find('video, audio') - .each(function pauseMedia() { - this.pause(); - }); - let $el = this.$(`#${id}`); - if ($el === null || $el.length === 0) { - const view = new Whisper.ConversationView({ - model: conversation, - window: this.model.window, - }); - view.view.resetScrollPosition(); - - // eslint-disable-next-line prefer-destructuring - $el = view.$el; - } + this.setupSessionConversation(); + // const sessionConversationView = new Whisper.SessionConversationView({ + // el: container, + // conversationKey: conversation.id, + // }); + // sessionConversationView.render(); - container.prepend($el); - } conversation.trigger('opened'); }, close(conversation) { @@ -98,6 +53,77 @@ reject: onCancel, }); }, + setupSessionConversation() { + // Here we set up a full redux store with initial state for our LeftPane 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, + }, + }; + + this.store = Signal.State.createStore(initialState); + window.inboxStore = this.store; + + this.sessionConversationView = new Whisper.ReactWrapperView({ + JSX: Signal.State.Roots.createSessionConversation(this.store), + className: 'session-conversation-redux-wrapper', + }); + + // 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, + this.store.dispatch + ); + const { userChanged } = Signal.State.bindActionCreators( + Signal.State.Ducks.user.actions, + this.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').prepend(this.sessionConversationView.el); + }, }); Whisper.AppLoadingScreen = Whisper.View.extend({ @@ -345,7 +371,7 @@ } this.conversation_stack.open(conversation); - this.focusConversation(); + // this.focusConversation(); }, closeConversation(conversation) { if (conversation) { diff --git a/ts/components/session/LeftPaneMessageSection.tsx b/ts/components/session/LeftPaneMessageSection.tsx index 2032d3986..c68c604bd 100644 --- a/ts/components/session/LeftPaneMessageSection.tsx +++ b/ts/components/session/LeftPaneMessageSection.tsx @@ -109,7 +109,7 @@ export class LeftPaneMessageSection extends React.Component { key, style, }: RowRendererParamsType): JSX.Element => { - // const { openConversationInternal } = this.props; + const { openConversationInternal } = this.props; const conversations = this.getCurrentConversations(); @@ -125,7 +125,7 @@ export class LeftPaneMessageSection extends React.Component { key={key} style={style} {...conversation} - onClick={() => window.renderConversationView(conversationKey)} + onClick={openConversationInternal} i18n={window.i18n} /> ); diff --git a/ts/components/session/SessionConversation.tsx b/ts/components/session/SessionConversation.tsx index 14b8f5509..b73a43d06 100644 --- a/ts/components/session/SessionConversation.tsx +++ b/ts/components/session/SessionConversation.tsx @@ -8,181 +8,191 @@ import { Message } from '../conversation/Message'; import { SessionSpinner } from './SessionSpinner'; -interface Props { - getHeaderProps: any; - conversationKey: any; -} - -interface State { - sendingProgess: number; - prevSendingProgess: number; - messages: Array; -} - -export class SessionConversation extends React.Component { +// interface Props { +// getHeaderProps: any; +// conversationKey: any; +// } + +// interface State { +// sendingProgess: number; +// prevSendingProgess: number; +// messages: Array; +// } + +export class SessionConversation extends React.Component { constructor(props: any) { super(props); - this.state = { - sendingProgess: 0, - prevSendingProgess: 0, - messages: [], - }; + // this.state = { + // sendingProgess: 0, + // prevSendingProgess: 0, + // messages: [], + // }; } - async componentWillMount() { - const { conversationKey } = this.props; + // async componentWillMount() { + // const { conversationKey } = this.props; - this.setState({ - messages: await window.getMessagesByKey(conversationKey) - }); + // this.setState({ + // messages: await window.getMessagesByKey(conversationKey) + // }); - } + // } render() { - // const headerProps = this.props.getHeaderProps; - const { conversationKey } = this.props; - const loadingMessages = this.state.messages.length === 0; - - // 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(`Conversation key: `, conversationKey); - - return ( -
-
- {this.renderHeader(conversation)} -
- - + // console.log(`[vince]`, this.props); + + // // const headerProps = this.props.getHeaderProps; + // const { conversationKey } = this.props; + // const loadingMessages = this.state.messages.length === 0; + + // // 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(`Conversation key: `, conversationKey); + + // return ( + //
+ //
+ // {this.renderHeader(conversation)} + //
+ + // + + //
+ // { loadingMessages ? ( + //
+ // + //
+ // ) : ( + // <> + // {/*this.renderMessages(conversationKey, conversationType)*/} + // {this.props.conversations[0].lastMessage} + // + // )} + //
- -
- { loadingMessages ? ( -
- -
- ) : ( - <> - {this.renderMessages(conversationKey, conversationType)} - - )} -
- - - null} - /> -
- ); - } - - public renderMessages(conversationKey: string, conversationType: 'group' | 'direct') { - const { messages } = this.state; - - console.log(`Messages`, messages); - - // FIND FOR EACH MESSAGE - const isExpired = false; - const isDeletable = false; - const messageType = 'direct'; - const selected = false; - const preview:any = []; - const multiSelectMode = false; - const onSelectMessage = () => null; - const onSelectMessageUnchecked = () => null; - const onShowDetail = () => null; - const onShowUserDetails = () => null; - - - // FIXME PAY ATTENTION; ONLY RENDER MESSAGES THAT ARE VISIBLE - return ( - <>{ - messages.map((message: any) => ( - - )) - } - ); - - } - - public renderHeader(conversation: any) { + // null} + // /> + //
+ // ); return ( - null} - onDeleteMessages={() => null} - onDeleteContact={() => null} - onResetSession={() => null} - onCloseOverlay={() => null} - onDeleteSelectedMessages={() => null} - onArchive={() => null} - onMoveToInbox={() => null} - onShowSafetyNumber={() => null} - onShowAllMedia={() => null} - onShowGroupMembers={() => null} - onGoBack={() => null} - onBlockUser={() => null} - onUnblockUser={() => null} - onClearNickname={() => null} - onChangeNickname={() => null} - onCopyPublicKey={() => null} - onLeaveGroup={() => null} - onAddModerators={() => null} - onRemoveModerators={() => null} - onInviteFriends={() => null} - /> - ); - } - - public scrollToUnread() { - + <> + {/*this.props.conversations[0].lastMessage*/} + Lorem ipsum dolor sit amet consectetur, adipisicing elit. Quidem fugiat omnis aperiam nulla ducimus dolores, molestias hic aliquam laboriosam velit, quas quis autem distinctio vero beatae id tempora error nostrum? + + ) } - public scrollToBottom() { - - } -} + // public renderMessages(conversationKey: string, conversationType: 'group' | 'direct') { + // const { messages } = this.state; + + // console.log(`Messages`, messages); + + // // FIND FOR EACH MESSAGE + // const isExpired = false; + // const isDeletable = false; + // const messageType = 'direct'; + // const selected = false; + // const preview:any = []; + // const multiSelectMode = false; + // const onSelectMessage = () => null; + // const onSelectMessageUnchecked = () => null; + // const onShowDetail = () => null; + // const onShowUserDetails = () => null; + + + // // FIXME PAY ATTENTION; ONLY RENDER MESSAGES THAT ARE VISIBLE + // return ( + // <>{ + // messages.map((message: any) => { + + // return message.body && ( + // + // )} + // ) + // } + // ); + + // } + +// public renderHeader(conversation: any) { +// return ( +// null} +// onDeleteMessages={() => null} +// onDeleteContact={() => null} +// onResetSession={() => null} +// onCloseOverlay={() => null} +// onDeleteSelectedMessages={() => null} +// onArchive={() => null} +// onMoveToInbox={() => null} +// onShowSafetyNumber={() => null} +// onShowAllMedia={() => null} +// onShowGroupMembers={() => null} +// onGoBack={() => null} +// onBlockUser={() => null} +// onUnblockUser={() => null} +// onClearNickname={() => null} +// onChangeNickname={() => null} +// onCopyPublicKey={() => null} +// onLeaveGroup={() => null} +// onAddModerators={() => null} +// onRemoveModerators={() => null} +// onInviteFriends={() => null} +// /> +// ); +// } + +// public scrollToUnread() { + +// } + +// public scrollToBottom() { + +// } +// } diff --git a/ts/state/roots/createSessionConversation.tsx b/ts/state/roots/createSessionConversation.tsx new file mode 100644 index 000000000..cd27dc370 --- /dev/null +++ b/ts/state/roots/createSessionConversation.tsx @@ -0,0 +1,16 @@ +import React from 'react'; +import { Provider } from 'react-redux'; + +import { Store } from 'redux'; + +import { SmartSessionConversation } from '../smart/SessionConversation'; + +// Workaround: A react component's required properties are filtering up through connect() +// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/31363 +const FilteredSessionConversation = SmartSessionConversation as any; + +export const createSessionConversation = (store: Store) => ( + + + +); diff --git a/ts/state/smart/SessionConversation.tsx b/ts/state/smart/SessionConversation.tsx new file mode 100644 index 000000000..af7231c46 --- /dev/null +++ b/ts/state/smart/SessionConversation.tsx @@ -0,0 +1,15 @@ +import { connect } from 'react-redux'; +import { mapDispatchToProps } from '../actions'; +import { SessionConversation } from '../../components/session/SessionConversation'; +import { StateType } from '../reducer'; + +import { getLeftPaneLists } from '../selectors/conversations'; + +const mapStateToProps = (state: StateType) => { + const lists = getLeftPaneLists(state); + + return lists; +}; + +const smart = connect(mapStateToProps, mapDispatchToProps); +export const SmartSessionConversation = smart(SessionConversation);