move InboxView to react
							parent
							
								
									2ae7a6dfe5
								
							
						
					
					
						commit
						918eeae275
					
				| @ -1,26 +0,0 @@ | |||||||
| /* global Whisper */ |  | ||||||
| 
 |  | ||||||
| // eslint-disable-next-line func-names
 |  | ||||||
| (function() { |  | ||||||
|   'use strict'; |  | ||||||
| 
 |  | ||||||
|   window.Whisper = window.Whisper || {}; |  | ||||||
| 
 |  | ||||||
|   Whisper.SessionConversationView = Whisper.View.extend({ |  | ||||||
|     initialize(options) { |  | ||||||
|       this.props = { |  | ||||||
|         ...options, |  | ||||||
|       }; |  | ||||||
|     }, |  | ||||||
| 
 |  | ||||||
|     render() { |  | ||||||
|       this.conversationView = new Whisper.ReactWrapperView({ |  | ||||||
|         className: 'session-conversation-wrapper', |  | ||||||
|         Component: window.Signal.Components.SessionConversation, |  | ||||||
|         props: this.props, |  | ||||||
|       }); |  | ||||||
| 
 |  | ||||||
|       this.$el.prepend(this.conversationView.el); |  | ||||||
|     }, |  | ||||||
|   }); |  | ||||||
| })(); |  | ||||||
| @ -0,0 +1,28 @@ | |||||||
|  | /* global Whisper */ | ||||||
|  | 
 | ||||||
|  | // eslint-disable-next-line func-names
 | ||||||
|  | (function() { | ||||||
|  |     'use strict'; | ||||||
|  | 
 | ||||||
|  |     window.Whisper = window.Whisper || {}; | ||||||
|  | 
 | ||||||
|  |     Whisper.InboxView = Whisper.View.extend({ | ||||||
|  |       initialize() { | ||||||
|  |         this.render(); | ||||||
|  |       }, | ||||||
|  | 
 | ||||||
|  |       render() { | ||||||
|  |         this.dialogView = new Whisper.ReactWrapperView({ | ||||||
|  |           className: 'inbox index', | ||||||
|  |           Component: window.Signal.Components.SessionInboxView, | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         this.$el.append(this.dialogView.el); | ||||||
|  |         return this; | ||||||
|  |       }, | ||||||
|  | 
 | ||||||
|  |       close() { | ||||||
|  |         this.remove(); | ||||||
|  |       }, | ||||||
|  |     }); | ||||||
|  |   })(); | ||||||
| @ -1,28 +0,0 @@ | |||||||
| /* global i18n, Whisper */ |  | ||||||
| 
 |  | ||||||
| // eslint-disable-next-line func-names
 |  | ||||||
| (function() { |  | ||||||
|   'use strict'; |  | ||||||
| 
 |  | ||||||
|   window.Whisper = window.Whisper || {}; |  | ||||||
| 
 |  | ||||||
|   Whisper.SessionSettingsView = Whisper.View.extend({ |  | ||||||
|     initialize() { |  | ||||||
|       this.render(); |  | ||||||
|     }, |  | ||||||
|     render() { |  | ||||||
|       this.settingsView = new Whisper.ReactWrapperView({ |  | ||||||
|         className: 'session-settings', |  | ||||||
|         Component: window.Signal.Components.SettingsView, |  | ||||||
|         props: { |  | ||||||
|           i18n, |  | ||||||
|         }, |  | ||||||
|       }); |  | ||||||
| 
 |  | ||||||
|       this.$el.append(this.settingsView.el); |  | ||||||
|     }, |  | ||||||
|     close() { |  | ||||||
|       this.remove(); |  | ||||||
|     }, |  | ||||||
|   }); |  | ||||||
| })(); |  | ||||||
| @ -0,0 +1,273 @@ | |||||||
|  | import React from 'react'; | ||||||
|  | import { Provider } from 'react-redux'; | ||||||
|  | import { bindActionCreators } from 'redux'; | ||||||
|  | import { getMessageQueue } from '../../session'; | ||||||
|  | import { createStore } from '../../state/createStore'; | ||||||
|  | import { StateType } from '../../state/reducer'; | ||||||
|  | import { SmartLeftPane } from '../../state/smart/LeftPane'; | ||||||
|  | import { SmartSessionConversation } from '../../state/smart/SessionConversation'; | ||||||
|  | import { SessionSettingCategory, SettingsView } from './settings/SessionSettings'; | ||||||
|  | 
 | ||||||
|  | // Workaround: A react component's required properties are filtering up through connect()
 | ||||||
|  | //   https://github.com/DefinitelyTyped/DefinitelyTyped/issues/31363
 | ||||||
|  | const FilteredLeftPane = SmartLeftPane as any; | ||||||
|  | const FilteredSessionConversation = SmartSessionConversation as any; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | type Props = { | ||||||
|  |   focusedSection: number; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | type State = { | ||||||
|  |   isInitialLoadComplete: boolean; | ||||||
|  |   settingsCategory?: SessionSettingCategory; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // tslint:disable: react-a11y-img-has-alt
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | export class SessionInboxView extends React.Component<Props, State> { | ||||||
|  |     private store: any; | ||||||
|  | 
 | ||||||
|  |     constructor(props: any) { | ||||||
|  |         super(props); | ||||||
|  |         this.state = { | ||||||
|  |           isInitialLoadComplete: false, | ||||||
|  |           settingsCategory: undefined, | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         // Inbox
 | ||||||
|  |         const inboxCollection = window.getInboxCollection(); | ||||||
|  |         this.fetchHandleMessageSentData = this.fetchHandleMessageSentData.bind( | ||||||
|  |             this | ||||||
|  |         ); | ||||||
|  |         this.handleMessageSentFailure = this.handleMessageSentFailure.bind(this); | ||||||
|  |       this.handleMessageSentSuccess = this.handleMessageSentSuccess.bind(this); | ||||||
|  |       this.showSessionSettingsCategory = this.showSessionSettingsCategory.bind(this); | ||||||
|  |       this.showSessionViewConversation = this.showSessionViewConversation.bind(this); | ||||||
|  | 
 | ||||||
|  |         void this.setupLeftPane(); | ||||||
|  | 
 | ||||||
|  |       // ConversationCollection
 | ||||||
|  |     //   this.listenTo(inboxCollection, 'messageError', () => {
 | ||||||
|  |         // if (this.networkStatusView) {
 | ||||||
|  |         //   this.networkStatusView.render();
 | ||||||
|  |         // }
 | ||||||
|  |     //   });
 | ||||||
|  | 
 | ||||||
|  |     // this.networkStatusView = new Whisper.NetworkStatusView();
 | ||||||
|  |     // this.$el
 | ||||||
|  |     //   .find('.network-status-container')
 | ||||||
|  |     //   .append(this.networkStatusView.render().el);
 | ||||||
|  | 
 | ||||||
|  |     // extension.expired(expired => {
 | ||||||
|  |     //   if (expired) {
 | ||||||
|  |     //     const banner = new Whisper.ExpiredAlertBanner().render();
 | ||||||
|  |     //     banner.$el.prependTo(this.$el);
 | ||||||
|  |     //     this.$el.addClass('expired');
 | ||||||
|  |     //   }
 | ||||||
|  |     // });
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     public render() { | ||||||
|  |         if (!this.state.isInitialLoadComplete) { | ||||||
|  |             return <></>; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |       const isSettingsView = this.state.settingsCategory !== undefined; | ||||||
|  |         return ( | ||||||
|  |           <Provider store={this.store}> | ||||||
|  |               <div className="gutter"> | ||||||
|  |               <div className="network-status-container"/> | ||||||
|  |               {this.renderLeftPane()} | ||||||
|  |             </div> | ||||||
|  |               {isSettingsView ? this.renderSettings() : this.renderSessionConversation()} | ||||||
|  |           </Provider> | ||||||
|  |       );    } | ||||||
|  | 
 | ||||||
|  |   private renderLeftPane() { | ||||||
|  |     return ( | ||||||
|  |       <FilteredLeftPane | ||||||
|  |         showSessionSettingsCategory={this.showSessionSettingsCategory} | ||||||
|  |         showSessionViewConversation={this.showSessionViewConversation} | ||||||
|  |         settingsCategory={this.state.settingsCategory} | ||||||
|  |       /> | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private renderSettings() { | ||||||
|  |     const isSecondaryDevice = !!window.textsecure.storage.get('isSecondaryDevice'); | ||||||
|  |     const category = this.state.settingsCategory || SessionSettingCategory.Appearance; | ||||||
|  | 
 | ||||||
|  |     return ( | ||||||
|  |       <SettingsView | ||||||
|  |         isSecondaryDevice={isSecondaryDevice} | ||||||
|  |         category={category} | ||||||
|  |       /> | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private renderSessionConversation() { | ||||||
|  |     return ( | ||||||
|  |       <div className="session-conversation"> | ||||||
|  |         <FilteredSessionConversation /> | ||||||
|  |       </div> | ||||||
|  |     ); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  |     private async fetchHandleMessageSentData(m: any) { | ||||||
|  |         // nobody is listening to this freshly fetched message .trigger calls
 | ||||||
|  |         const tmpMsg = await window.Signal.Data.getMessageById(m.identifier, { | ||||||
|  |           Message: window.Whisper.Message, | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         if (!tmpMsg) { | ||||||
|  |           return null; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // find the corresponding conversation of this message
 | ||||||
|  |         const conv = window.ConversationController.get( | ||||||
|  |           tmpMsg.get('conversationId') | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |         if (!conv) { | ||||||
|  |           return null; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // then, find in this conversation the very same message
 | ||||||
|  |         // const msg = conv.messageCollection.models.find(
 | ||||||
|  |         //   convMsg => convMsg.id === tmpMsg.id
 | ||||||
|  |         // );
 | ||||||
|  |         const msg = window.MessageController._get()[m.identifier]; | ||||||
|  | 
 | ||||||
|  |         if (!msg || !msg.message) { | ||||||
|  |           return null; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return { msg: msg.message }; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       private async handleMessageSentSuccess(sentMessage: any, wrappedEnvelope: any) { | ||||||
|  |         const fetchedData = await this.fetchHandleMessageSentData(sentMessage); | ||||||
|  |         if (!fetchedData) { | ||||||
|  |           return; | ||||||
|  |         } | ||||||
|  |         const { msg } = fetchedData; | ||||||
|  | 
 | ||||||
|  |         msg.handleMessageSentSuccess(sentMessage, wrappedEnvelope); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       private async handleMessageSentFailure(sentMessage: any, error: any) { | ||||||
|  |         const fetchedData = await this.fetchHandleMessageSentData(sentMessage); | ||||||
|  |         if (!fetchedData) { | ||||||
|  |           return; | ||||||
|  |         } | ||||||
|  |         const { msg } = fetchedData; | ||||||
|  | 
 | ||||||
|  |         await msg.handleMessageSentFailure(sentMessage, error); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       private async setupLeftPane() { | ||||||
|  |         // Here we set up a full redux store with initial state for our LeftPane Root
 | ||||||
|  |         const convoCollection = window.getConversations(); | ||||||
|  |         const conversations = convoCollection.map( | ||||||
|  |           (conversation: any) => conversation.cachedProps | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |         const filledConversations = conversations.map(async (conv: any) => { | ||||||
|  |           const messages = await window.getMessagesByKey(conv.id); | ||||||
|  |           return { ...conv, messages }; | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         const fullFilledConversations = await Promise.all(filledConversations); | ||||||
|  | 
 | ||||||
|  |         const initialState = { | ||||||
|  |           conversations: { | ||||||
|  |             conversationLookup: window.Signal.Util.makeLookup( | ||||||
|  |               fullFilledConversations, | ||||||
|  |               'id' | ||||||
|  |             ), | ||||||
|  |           }, | ||||||
|  |           user: { | ||||||
|  |             regionCode: window.storage.get('regionCode'), | ||||||
|  |             ourNumber: | ||||||
|  |               window.storage.get('primaryDevicePubKey') || | ||||||
|  |               window.textsecure.storage.user.getNumber(), | ||||||
|  |             isSecondaryDevice: !!window.storage.get('isSecondaryDevice'), | ||||||
|  |             i18n: window.i18n, | ||||||
|  |           }, | ||||||
|  |           section: { | ||||||
|  |             focusedSection: 1, | ||||||
|  |           }, | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         this.store = createStore(initialState); | ||||||
|  |         window.inboxStore = this.store; | ||||||
|  | 
 | ||||||
|  |         // Enables our redux store to be updated by backbone events in the outside world
 | ||||||
|  |         const { | ||||||
|  |           conversationAdded, | ||||||
|  |           conversationChanged, | ||||||
|  |           conversationRemoved, | ||||||
|  |           removeAllConversations, | ||||||
|  |           messageExpired, | ||||||
|  |           openConversationExternal, | ||||||
|  |         } = bindActionCreators( | ||||||
|  |           window.Signal.State.Ducks.conversations.actions, | ||||||
|  |           this.store.dispatch | ||||||
|  |         ); | ||||||
|  |         const { userChanged } = bindActionCreators( | ||||||
|  |           window.Signal.State.Ducks.user.actions, | ||||||
|  |           this.store.dispatch | ||||||
|  |         ); | ||||||
|  |         const { messageChanged } = bindActionCreators( | ||||||
|  |           window.Signal.State.Ducks.messages.actions, | ||||||
|  |           this.store.dispatch | ||||||
|  |         ); | ||||||
|  | 
 | ||||||
|  |         // this.openConversationAction = openConversationExternal;
 | ||||||
|  | 
 | ||||||
|  |         this.fetchHandleMessageSentData = this.fetchHandleMessageSentData.bind( | ||||||
|  |           this | ||||||
|  |         ); | ||||||
|  |         this.handleMessageSentFailure = this.handleMessageSentFailure.bind(this); | ||||||
|  |         this.handleMessageSentSuccess = this.handleMessageSentSuccess.bind(this); | ||||||
|  | 
 | ||||||
|  |         // 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);
 | ||||||
|  | 
 | ||||||
|  |         getMessageQueue() | ||||||
|  |           .events.addListener('success', this.handleMessageSentSuccess); | ||||||
|  | 
 | ||||||
|  |         getMessageQueue() | ||||||
|  |           .events.addListener('fail', this.handleMessageSentFailure); | ||||||
|  | 
 | ||||||
|  |         window.Whisper.events.on('messageExpired', messageExpired); | ||||||
|  |         window.Whisper.events.on('messageChanged', messageChanged); | ||||||
|  |         window.Whisper.events.on('userChanged', userChanged); | ||||||
|  | 
 | ||||||
|  |         // Finally, add it to the DOM
 | ||||||
|  |         // this.$('.left-pane-placeholder').append(this.leftPaneView.el);
 | ||||||
|  |         this.setState({ isInitialLoadComplete: true }); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |   private showSessionSettingsCategory(category: SessionSettingCategory) { | ||||||
|  |     this.setState({ settingsCategory: category }); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   private showSessionViewConversation() { | ||||||
|  |     this.setState({ settingsCategory: undefined }); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -1,4 +0,0 @@ | |||||||
| export type NoopActionType = { |  | ||||||
|   type: 'NOOP'; |  | ||||||
|   payload: null; |  | ||||||
| }; |  | ||||||
| @ -1,16 +0,0 @@ | |||||||
| import React from 'react'; |  | ||||||
| import { Provider } from 'react-redux'; |  | ||||||
| 
 |  | ||||||
| import { Store } from 'redux'; |  | ||||||
| 
 |  | ||||||
| import { SmartLeftPane } from '../smart/LeftPane'; |  | ||||||
| 
 |  | ||||||
| // Workaround: A react component's required properties are filtering up through connect()
 |  | ||||||
| //   https://github.com/DefinitelyTyped/DefinitelyTyped/issues/31363
 |  | ||||||
| const FilteredLeftPane = SmartLeftPane as any; |  | ||||||
| 
 |  | ||||||
| export const createLeftPane = (store: Store) => ( |  | ||||||
|   <Provider store={store as any}> |  | ||||||
|     <FilteredLeftPane /> |  | ||||||
|   </Provider> |  | ||||||
| ); |  | ||||||
| @ -1,16 +0,0 @@ | |||||||
| 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) => ( |  | ||||||
|   <Provider store={store as any}> |  | ||||||
|     <FilteredSessionConversation /> |  | ||||||
|   </Provider> |  | ||||||
| ); |  | ||||||
					Loading…
					
					
				
		Reference in New Issue