Merge branch 'develop' into skindex-nextgen
						commit
						c281fe785a
					
				|  | @ -1,45 +0,0 @@ | |||
| /* | ||||
| Copyright 2015 OpenMarket Ltd | ||||
| 
 | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
| 
 | ||||
|     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| 
 | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| 'use strict'; | ||||
| 
 | ||||
| var days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; | ||||
| var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; | ||||
| 
 | ||||
| module.exports = { | ||||
|     formatDate: function(date) { | ||||
|         // date.toLocaleTimeString is completely system dependent.
 | ||||
|         // just go 24h for now
 | ||||
|         function pad(n) { | ||||
|             return (n < 10 ? '0' : '') + n; | ||||
|         } | ||||
| 
 | ||||
|         var now = new Date(); | ||||
|         if (date.toDateString() === now.toDateString()) { | ||||
|             return pad(date.getHours()) + ':' + pad(date.getMinutes()); | ||||
|         } | ||||
|         else if (now.getTime() - date.getTime() < 6 * 24 * 60 * 60 * 1000) { | ||||
|             return days[date.getDay()] + " " + pad(date.getHours()) + ':' + pad(date.getMinutes()); | ||||
|         } | ||||
|         else if (now.getFullYear() === date.getFullYear()) { | ||||
|             return days[date.getDay()] + ", " + months[date.getMonth()] + " " + (date.getDay()+1) + " " + pad(date.getHours()) + ':' + pad(date.getMinutes()); | ||||
|         } | ||||
|         else { | ||||
|             return days[date.getDay()] + ", " + months[date.getMonth()] + " " + (date.getDay()+1) + " " + date.getFullYear() + " " + pad(date.getHours()) + ':' + pad(date.getMinutes()); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -1,24 +0,0 @@ | |||
| var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg'); | ||||
| var dis = require('matrix-react-sdk/lib/dispatcher'); | ||||
| 
 | ||||
| module.exports = { | ||||
|     resend: function(event) { | ||||
|         MatrixClientPeg.get().resendEvent( | ||||
|             event, MatrixClientPeg.get().getRoom(event.getRoomId()) | ||||
|         ).done(function() { | ||||
|             dis.dispatch({ | ||||
|                 action: 'message_sent', | ||||
|                 event: event | ||||
|             }); | ||||
|         }, function() { | ||||
|             dis.dispatch({ | ||||
|                 action: 'message_send_failed', | ||||
|                 event: event | ||||
|             }); | ||||
|         }); | ||||
|         dis.dispatch({ | ||||
|             action: 'message_resend_started', | ||||
|             event: event | ||||
|         }); | ||||
|     }, | ||||
| }; | ||||
|  | @ -23,13 +23,8 @@ limitations under the License. | |||
| 
 | ||||
| module.exports.components = require('matrix-react-sdk/lib/component-index').components; | ||||
| 
 | ||||
| module.exports.components['structures.login.Login'] = require('./components/structures/login/Login'); | ||||
| module.exports.components['structures.login.PostRegistration'] = require('./components/structures/login/PostRegistration'); | ||||
| module.exports.components['structures.login.Registration'] = require('./components/structures/login/Registration'); | ||||
| module.exports.components['views.elements.ImageView'] = require('./components/views/elements/ImageView'); | ||||
| module.exports.components['views.elements.Spinner'] = require('./components/views/elements/Spinner'); | ||||
| module.exports.components['views.login.RegistrationForm'] = require('./components/views/login/RegistrationForm'); | ||||
| module.exports.components['views.login.ServerConfig'] = require('./components/views/login/ServerConfig'); | ||||
| module.exports.components['views.messages.MessageTimestamp'] = require('./components/views/messages/MessageTimestamp'); | ||||
| module.exports.components['views.rooms.RoomDNDView'] = require('./components/views/rooms/RoomDNDView'); | ||||
| 
 | ||||
|  | @ -44,19 +39,10 @@ module.exports.components['molecules.RoomDropTarget'] = require('./skins/vector/ | |||
| module.exports.components['molecules.RoomTooltip'] = require('./skins/vector/views/molecules/RoomTooltip'); | ||||
| module.exports.components['molecules.SearchBar'] = require('./skins/vector/views/molecules/SearchBar'); | ||||
| module.exports.components['molecules.SenderProfile'] = require('./skins/vector/views/molecules/SenderProfile'); | ||||
| module.exports.components['organisms.CreateRoom'] = require('./skins/vector/views/organisms/CreateRoom'); | ||||
| module.exports.components['organisms.ErrorDialog'] = require('./skins/vector/views/organisms/ErrorDialog'); | ||||
| module.exports.components['organisms.LeftPanel'] = require('./skins/vector/views/organisms/LeftPanel'); | ||||
| module.exports.components['organisms.LogoutPrompt'] = require('./skins/vector/views/organisms/LogoutPrompt'); | ||||
| module.exports.components['organisms.MemberList'] = require('./skins/vector/views/organisms/MemberList'); | ||||
| module.exports.components['organisms.Notifier'] = require('./skins/vector/views/organisms/Notifier'); | ||||
| module.exports.components['organisms.QuestionDialog'] = require('./skins/vector/views/organisms/QuestionDialog'); | ||||
| module.exports.components['organisms.RightPanel'] = require('./skins/vector/views/organisms/RightPanel'); | ||||
| module.exports.components['organisms.RoomDirectory'] = require('./skins/vector/views/organisms/RoomDirectory'); | ||||
| module.exports.components['organisms.RoomList'] = require('./skins/vector/views/organisms/RoomList'); | ||||
| module.exports.components['organisms.RoomSubList'] = require('./skins/vector/views/organisms/RoomSubList'); | ||||
| module.exports.components['organisms.RoomView'] = require('./skins/vector/views/organisms/RoomView'); | ||||
| module.exports.components['organisms.UserSettings'] = require('./skins/vector/views/organisms/UserSettings'); | ||||
| module.exports.components['organisms.ViewSource'] = require('./skins/vector/views/organisms/ViewSource'); | ||||
| module.exports.components['pages.CompatibilityPage'] = require('./skins/vector/views/pages/CompatibilityPage'); | ||||
| module.exports.components['pages.MatrixChat'] = require('./skins/vector/views/pages/MatrixChat'); | ||||
|  |  | |||
|  | @ -1,199 +0,0 @@ | |||
| /* | ||||
| Copyright 2015 OpenMarket Ltd | ||||
| 
 | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
| 
 | ||||
|     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| 
 | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| 'use strict'; | ||||
| 
 | ||||
| var React = require('react'); | ||||
| var ReactDOM = require('react-dom'); | ||||
| var sdk = require('matrix-react-sdk'); | ||||
| var Signup = require("matrix-react-sdk/lib/Signup"); | ||||
| var PasswordLogin = require("matrix-react-sdk/lib/components/views/login/PasswordLogin"); | ||||
| var CasLogin = require("matrix-react-sdk/lib/components/views/login/CasLogin"); | ||||
| var ServerConfig = require("../../views/login/ServerConfig"); | ||||
| 
 | ||||
| /** | ||||
|  * A wire component which glues together login UI components and Signup logic | ||||
|  */ | ||||
| module.exports = React.createClass({displayName: 'Login', | ||||
|     propTypes: { | ||||
|         onLoggedIn: React.PropTypes.func.isRequired, | ||||
|         homeserverUrl: React.PropTypes.string, | ||||
|         identityServerUrl: React.PropTypes.string, | ||||
|         // login shouldn't know or care how registration is done.
 | ||||
|         onRegisterClick: React.PropTypes.func.isRequired | ||||
|     }, | ||||
| 
 | ||||
|     getDefaultProps: function() { | ||||
|         return { | ||||
|             homeserverUrl: 'https://matrix.org/', | ||||
|             identityServerUrl: 'https://vector.im' | ||||
|         }; | ||||
|     }, | ||||
| 
 | ||||
|     getInitialState: function() { | ||||
|         return { | ||||
|             busy: false, | ||||
|             errorText: null, | ||||
|             enteredHomeserverUrl: this.props.homeserverUrl, | ||||
|             enteredIdentityServerUrl: this.props.identityServerUrl | ||||
|         }; | ||||
|     }, | ||||
| 
 | ||||
|     componentWillMount: function() { | ||||
|         this._initLoginLogic(); | ||||
|     }, | ||||
| 
 | ||||
|     onPasswordLogin: function(username, password) { | ||||
|         var self = this; | ||||
|         self.setState({ | ||||
|             busy: true | ||||
|         }); | ||||
| 
 | ||||
|         this._loginLogic.loginViaPassword(username, password).then(function(data) { | ||||
|             self.props.onLoggedIn(data); | ||||
|         }, function(error) { | ||||
|             self._setErrorTextFromError(error); | ||||
|         }).finally(function() { | ||||
|             self.setState({ | ||||
|                 busy: false | ||||
|             }); | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     onHsUrlChanged: function(newHsUrl) { | ||||
|         this._initLoginLogic(newHsUrl); | ||||
|     }, | ||||
| 
 | ||||
|     onIsUrlChanged: function(newIsUrl) { | ||||
|         this._initLoginLogic(null, newIsUrl); | ||||
|     }, | ||||
| 
 | ||||
|     _initLoginLogic: function(hsUrl, isUrl) { | ||||
|         var self = this; | ||||
|         hsUrl = hsUrl || this.state.enteredHomeserverUrl; | ||||
|         isUrl = isUrl || this.state.enteredIdentityServerUrl; | ||||
| 
 | ||||
|         var loginLogic = new Signup.Login(hsUrl, isUrl); | ||||
|         this._loginLogic = loginLogic; | ||||
| 
 | ||||
|         loginLogic.getFlows().then(function(flows) { | ||||
|             // old behaviour was to always use the first flow without presenting
 | ||||
|             // options. This works in most cases (we don't have a UI for multiple
 | ||||
|             // logins so let's skip that for now).
 | ||||
|             loginLogic.chooseFlow(0); | ||||
|         }, function(err) { | ||||
|             self._setErrorTextFromError(err); | ||||
|         }).finally(function() { | ||||
|             self.setState({ | ||||
|                 busy: false | ||||
|             }); | ||||
|         }); | ||||
| 
 | ||||
|         this.setState({ | ||||
|             enteredHomeserverUrl: hsUrl, | ||||
|             enteredIdentityServerUrl: isUrl, | ||||
|             busy: true, | ||||
|             errorText: null // reset err messages
 | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     _getCurrentFlowStep: function() { | ||||
|         return this._loginLogic ? this._loginLogic.getCurrentFlowStep() : null | ||||
|     }, | ||||
| 
 | ||||
|     _setErrorTextFromError: function(err) { | ||||
|         if (err.friendlyText) { | ||||
|             this.setState({ | ||||
|                 errorText: err.friendlyText | ||||
|             }); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         var errCode = err.errcode; | ||||
|         if (!errCode && err.httpStatus) { | ||||
|             errCode = "HTTP " + err.httpStatus; | ||||
|         } | ||||
|         this.setState({ | ||||
|             errorText: ( | ||||
|                 "Error: Problem communicating with the given homeserver " + | ||||
|                 (errCode ? "(" + errCode + ")" : "") | ||||
|             ) | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     componentForStep: function(step) { | ||||
|         switch (step) { | ||||
|             case 'm.login.password': | ||||
|                 return ( | ||||
|                     <PasswordLogin onSubmit={this.onPasswordLogin} /> | ||||
|                 ); | ||||
|             case 'm.login.cas': | ||||
|                 return ( | ||||
|                     <CasLogin /> | ||||
|                 ); | ||||
|             default: | ||||
|                 if (!step) { | ||||
|                     return; | ||||
|                 } | ||||
|                 return ( | ||||
|                     <div> | ||||
|                     Sorry, this homeserver is using a login which is not | ||||
|                     recognised by Vector ({step}) | ||||
|                     </div> | ||||
|                 ); | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     render: function() { | ||||
|         var Loader = sdk.getComponent("elements.Spinner"); | ||||
|         var loader = this.state.busy ? <div className="mx_Login_loader"><Loader /></div> : null; | ||||
| 
 | ||||
|         return ( | ||||
|             <div className="mx_Login"> | ||||
|                 <div className="mx_Login_box"> | ||||
|                     <div className="mx_Login_logo"> | ||||
|                         <img src="img/logo.png" width="249" height="78" alt="vector"/> | ||||
|                     </div> | ||||
|                     <div> | ||||
|                         <h2>Sign in</h2> | ||||
|                         { this.componentForStep(this._getCurrentFlowStep()) } | ||||
|                         <ServerConfig ref="serverConfig" | ||||
|                             withToggleButton={true} | ||||
|                             defaultHsUrl={this.props.homeserverUrl} | ||||
|                             defaultIsUrl={this.props.identityServerUrl} | ||||
|                             onHsUrlChanged={this.onHsUrlChanged} | ||||
|                             onIsUrlChanged={this.onIsUrlChanged} | ||||
|                             delayTimeMs={1000}/> | ||||
|                         <div className="mx_Login_error"> | ||||
|                                 { loader } | ||||
|                                 { this.state.errorText } | ||||
|                         </div> | ||||
|                         <a className="mx_Login_create" onClick={this.props.onRegisterClick} href="#"> | ||||
|                             Create a new account | ||||
|                         </a> | ||||
|                         <br/> | ||||
|                         <div className="mx_Login_links"> | ||||
|                             <a href="https://medium.com/@Vector">blog</a>  ·   | ||||
|                             <a href="https://twitter.com/@VectorCo">twitter</a>  ·   | ||||
|                             <a href="https://github.com/vector-im/vector-web">github</a>  ·   | ||||
|                             <a href="https://matrix.org">powered by Matrix</a> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </div> | ||||
|         ); | ||||
|     } | ||||
| }); | ||||
|  | @ -1,81 +0,0 @@ | |||
| /* | ||||
| Copyright 2015 OpenMarket Ltd | ||||
| 
 | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
| 
 | ||||
|     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| 
 | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| 'use strict'; | ||||
| 
 | ||||
| var React = require('react'); | ||||
| 
 | ||||
| var sdk = require('matrix-react-sdk'); | ||||
| var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg'); | ||||
| 
 | ||||
| module.exports = React.createClass({ | ||||
|     displayName: 'PostRegistration', | ||||
| 
 | ||||
|     propTypes: { | ||||
|         onComplete: React.PropTypes.func.isRequired | ||||
|     }, | ||||
| 
 | ||||
|     getInitialState: function() { | ||||
|         return { | ||||
|             avatarUrl: null, | ||||
|             errorString: null, | ||||
|             busy: false | ||||
|         }; | ||||
|     }, | ||||
| 
 | ||||
|     componentWillMount: function() { | ||||
|         // There is some assymetry between ChangeDisplayName and ChangeAvatar,
 | ||||
|         // as ChangeDisplayName will auto-get the name but ChangeAvatar expects
 | ||||
|         // the URL to be passed to you (because it's also used for room avatars).
 | ||||
|         var cli = MatrixClientPeg.get(); | ||||
|         this.setState({busy: true}); | ||||
|         var self = this; | ||||
|         cli.getProfileInfo(cli.credentials.userId).done(function(result) { | ||||
|             self.setState({ | ||||
|                 avatarUrl: MatrixClientPeg.get().mxcUrlToHttp(result.avatar_url), | ||||
|                 busy: false | ||||
|             }); | ||||
|         }, function(error) { | ||||
|             self.setState({ | ||||
|                 errorString: "Failed to fetch avatar URL", | ||||
|                 busy: false | ||||
|             }); | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     render: function() { | ||||
|         var ChangeDisplayName = sdk.getComponent('settings.ChangeDisplayName'); | ||||
|         var ChangeAvatar = sdk.getComponent('settings.ChangeAvatar'); | ||||
|         return ( | ||||
|             <div className="mx_Login"> | ||||
|                 <div className="mx_Login_box"> | ||||
|                     <div className="mx_Login_logo"> | ||||
|                         <img src="img/logo.png" width="249" height="78" alt="vector"/> | ||||
|                     </div> | ||||
|                     <div className="mx_Login_profile"> | ||||
|                         Set a display name: | ||||
|                         <ChangeDisplayName /> | ||||
|                         Upload an avatar: | ||||
|                         <ChangeAvatar | ||||
|                             initialAvatarUrl={this.state.avatarUrl} /> | ||||
|                         <button onClick={this.props.onComplete}>Continue</button> | ||||
|                         {this.state.errorString} | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </div> | ||||
|         ); | ||||
|     } | ||||
| }); | ||||
|  | @ -1,247 +0,0 @@ | |||
| /* | ||||
| Copyright 2015 OpenMarket Ltd | ||||
| 
 | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
| 
 | ||||
|     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| 
 | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| 'use strict'; | ||||
| 
 | ||||
| var React = require('react'); | ||||
| 
 | ||||
| var sdk = require('matrix-react-sdk'); | ||||
| var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg'); | ||||
| var dis = require('matrix-react-sdk/lib/dispatcher'); | ||||
| var ServerConfig = require("../../views/login/ServerConfig"); | ||||
| var RegistrationForm = require("../../views/login/RegistrationForm"); | ||||
| var CaptchaForm = require("matrix-react-sdk/lib/components/views/login/CaptchaForm"); | ||||
| var Signup = require("matrix-react-sdk/lib/Signup"); | ||||
| var MIN_PASSWORD_LENGTH = 6; | ||||
| 
 | ||||
| module.exports = React.createClass({ | ||||
|     displayName: 'Registration', | ||||
| 
 | ||||
|     propTypes: { | ||||
|         onLoggedIn: React.PropTypes.func.isRequired, | ||||
|         clientSecret: React.PropTypes.string, | ||||
|         sessionId: React.PropTypes.string, | ||||
|         registrationUrl: React.PropTypes.string, | ||||
|         idSid: React.PropTypes.string, | ||||
|         hsUrl: React.PropTypes.string, | ||||
|         isUrl: React.PropTypes.string, | ||||
|         // registration shouldn't know or care how login is done.
 | ||||
|         onLoginClick: React.PropTypes.func.isRequired | ||||
|     }, | ||||
| 
 | ||||
|     getInitialState: function() { | ||||
|         return { | ||||
|             busy: false, | ||||
|             errorText: null, | ||||
|             enteredHomeserverUrl: this.props.hsUrl, | ||||
|             enteredIdentityServerUrl: this.props.isUrl | ||||
|         }; | ||||
|     }, | ||||
| 
 | ||||
|     componentWillMount: function() { | ||||
|         this.dispatcherRef = dis.register(this.onAction); | ||||
|         // attach this to the instance rather than this.state since it isn't UI
 | ||||
|         this.registerLogic = new Signup.Register( | ||||
|             this.props.hsUrl, this.props.isUrl | ||||
|         ); | ||||
|         this.registerLogic.setClientSecret(this.props.clientSecret); | ||||
|         this.registerLogic.setSessionId(this.props.sessionId); | ||||
|         this.registerLogic.setRegistrationUrl(this.props.registrationUrl); | ||||
|         this.registerLogic.setIdSid(this.props.idSid); | ||||
|         this.registerLogic.recheckState(); | ||||
|     }, | ||||
| 
 | ||||
|     componentWillUnmount: function() { | ||||
|         dis.unregister(this.dispatcherRef); | ||||
|     }, | ||||
| 
 | ||||
|     componentDidMount: function() { | ||||
|         // may have already done an HTTP hit (e.g. redirect from an email) so
 | ||||
|         // check for any pending response
 | ||||
|         var promise = this.registerLogic.getPromise(); | ||||
|         if (promise) { | ||||
|             this.onProcessingRegistration(promise); | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     onHsUrlChanged: function(newHsUrl) { | ||||
|         this.registerLogic.setHomeserverUrl(newHsUrl); | ||||
|     }, | ||||
| 
 | ||||
|     onIsUrlChanged: function(newIsUrl) { | ||||
|         this.registerLogic.setIdentityServerUrl(newIsUrl); | ||||
|     }, | ||||
| 
 | ||||
|     onAction: function(payload) { | ||||
|         if (payload.action !== "registration_step_update") { | ||||
|             return; | ||||
|         } | ||||
|         this.forceUpdate(); // registration state has changed.
 | ||||
|     }, | ||||
| 
 | ||||
|     onFormSubmit: function(formVals) { | ||||
|         var self = this; | ||||
|         this.setState({ | ||||
|             errorText: "", | ||||
|             busy: true | ||||
|         }); | ||||
|         this.onProcessingRegistration(this.registerLogic.register(formVals)); | ||||
|     }, | ||||
| 
 | ||||
|     // Promise is resolved when the registration process is FULLY COMPLETE
 | ||||
|     onProcessingRegistration: function(promise) { | ||||
|         var self = this; | ||||
|         promise.done(function(response) { | ||||
|             if (!response || !response.access_token) { | ||||
|                 console.warn( | ||||
|                     "FIXME: Register fulfilled without a final response, " + | ||||
|                     "did you break the promise chain?" | ||||
|                 ); | ||||
|                 // no matter, we'll grab it direct
 | ||||
|                 response = self.registerLogic.getCredentials(); | ||||
|             } | ||||
|             if (!response || !response.user_id || !response.access_token) { | ||||
|                 console.error("Final response is missing keys."); | ||||
|                 self.setState({ | ||||
|                     errorText: "There was a problem processing the response." | ||||
|                 }); | ||||
|                 return; | ||||
|             } | ||||
|             self.props.onLoggedIn({ | ||||
|                 userId: response.user_id, | ||||
|                 homeserverUrl: self.registerLogic.getHomeserverUrl(), | ||||
|                 identityServerUrl: self.registerLogic.getIdentityServerUrl(), | ||||
|                 accessToken: response.access_token | ||||
|             }); | ||||
|             self.setState({ | ||||
|                 busy: false | ||||
|             }); | ||||
|         }, function(err) { | ||||
|             if (err.message) { | ||||
|                 self.setState({ | ||||
|                     errorText: err.message | ||||
|                 }); | ||||
|             } | ||||
|             self.setState({ | ||||
|                 busy: false | ||||
|             }); | ||||
|             console.log(err); | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     onFormValidationFailed: function(errCode) { | ||||
|         var errMsg; | ||||
|         switch (errCode) { | ||||
|             case "RegistrationForm.ERR_PASSWORD_MISSING": | ||||
|                 errMsg = "Missing password."; | ||||
|                 break; | ||||
|             case "RegistrationForm.ERR_PASSWORD_MISMATCH": | ||||
|                 errMsg = "Passwords don't match."; | ||||
|                 break; | ||||
|             case "RegistrationForm.ERR_PASSWORD_LENGTH": | ||||
|                 errMsg = `Password too short (min ${MIN_PASSWORD_LENGTH}).`; | ||||
|                 break; | ||||
|             default: | ||||
|                 console.error("Unknown error code: %s", errCode); | ||||
|                 errMsg = "An unknown error occurred."; | ||||
|                 break; | ||||
|         } | ||||
|         this.setState({ | ||||
|             errorText: errMsg | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     onCaptchaLoaded: function(divIdName) { | ||||
|         this.registerLogic.tellStage("m.login.recaptcha", { | ||||
|             divId: divIdName | ||||
|         }); | ||||
|         this.setState({ | ||||
|             busy: false // requires user input
 | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     _getRegisterContentJsx: function() { | ||||
|         var currStep = this.registerLogic.getStep(); | ||||
|         var registerStep; | ||||
|         switch (currStep) { | ||||
|             case "Register.COMPLETE": | ||||
|                 break; // NOP
 | ||||
|             case "Register.START": | ||||
|             case "Register.STEP_m.login.dummy": | ||||
|                 registerStep = ( | ||||
|                     <RegistrationForm | ||||
|                         showEmail={true} | ||||
|                         minPasswordLength={MIN_PASSWORD_LENGTH} | ||||
|                         onError={this.onFormValidationFailed} | ||||
|                         onRegisterClick={this.onFormSubmit} /> | ||||
|                 ); | ||||
|                 break; | ||||
|             case "Register.STEP_m.login.email.identity": | ||||
|                 registerStep = ( | ||||
|                     <div> | ||||
|                         Please check your email to continue registration. | ||||
|                     </div> | ||||
|                 ); | ||||
|                 break; | ||||
|             case "Register.STEP_m.login.recaptcha": | ||||
|                 registerStep = ( | ||||
|                     <CaptchaForm onCaptchaLoaded={this.onCaptchaLoaded} /> | ||||
|                 ); | ||||
|                 break; | ||||
|             default: | ||||
|                 console.error("Unknown register state: %s", currStep); | ||||
|                 break; | ||||
|         } | ||||
|         var busySpinner; | ||||
|         if (this.state.busy) { | ||||
|             var Spinner = sdk.getComponent("elements.Spinner"); | ||||
|             busySpinner = ( | ||||
|                 <Spinner /> | ||||
|             ); | ||||
|         } | ||||
|         return ( | ||||
|             <div> | ||||
|                 <h2>Create an account</h2> | ||||
|                 {registerStep} | ||||
|                 <div className="mx_Login_error">{this.state.errorText}</div> | ||||
|                 {busySpinner} | ||||
|                 <ServerConfig ref="serverConfig" | ||||
|                     withToggleButton={true} | ||||
|                     defaultHsUrl={this.state.enteredHomeserverUrl} | ||||
|                     defaultIsUrl={this.state.enteredIdentityServerUrl} | ||||
|                     onHsUrlChanged={this.onHsUrlChanged} | ||||
|                     onIsUrlChanged={this.onIsUrlChanged} | ||||
|                     delayTimeMs={1000} /> | ||||
|                 <a className="mx_Login_create" onClick={this.props.onLoginClick} href="#"> | ||||
|                     I already have an account | ||||
|                 </a> | ||||
|             </div> | ||||
|         ); | ||||
|     }, | ||||
| 
 | ||||
|     render: function() { | ||||
|         return ( | ||||
|             <div className="mx_Login"> | ||||
|                 <div className="mx_Login_box"> | ||||
|                     <div className="mx_Login_logo"> | ||||
|                         <img src="img/logo.png" width="249" height="78" alt="vector"/> | ||||
|                     </div> | ||||
|                     {this._getRegisterContentJsx()} | ||||
|                 </div> | ||||
|             </div> | ||||
|         ); | ||||
|     } | ||||
| }); | ||||
|  | @ -20,7 +20,7 @@ var React = require('react'); | |||
| 
 | ||||
| var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg'); | ||||
| 
 | ||||
| var DateUtils = require('../../../DateUtils'); | ||||
| var DateUtils = require('matrix-react-sdk/lib/DateUtils'); | ||||
| var filesize = require('filesize'); | ||||
| 
 | ||||
| module.exports = React.createClass({ | ||||
|  | @ -55,7 +55,7 @@ module.exports = React.createClass({ | |||
|         ).done(function() { | ||||
|             if (self.props.onFinished) self.props.onFinished(); | ||||
|         }, function(e) { | ||||
|             var ErrorDialog = sdk.getComponent("organisms.ErrorDialog"); | ||||
|             var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); | ||||
|             // display error message stating you couldn't delete this.
 | ||||
|             var code = e.errcode || e.statusCode; | ||||
|             Modal.createDialog(ErrorDialog, { | ||||
|  |  | |||
|  | @ -1,126 +0,0 @@ | |||
| /* | ||||
| Copyright 2015 OpenMarket Ltd | ||||
| 
 | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
| 
 | ||||
|     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| 
 | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| 'use strict'; | ||||
| 
 | ||||
| var React = require('react'); | ||||
| var sdk = require('matrix-react-sdk') | ||||
| 
 | ||||
| /** | ||||
|  * A pure UI component which displays a registration form. | ||||
|  */ | ||||
| module.exports = React.createClass({ | ||||
|     displayName: 'RegistrationForm', | ||||
| 
 | ||||
|     propTypes: { | ||||
|         defaultEmail: React.PropTypes.string, | ||||
|         defaultUsername: React.PropTypes.string, | ||||
|         showEmail: React.PropTypes.bool, | ||||
|         minPasswordLength: React.PropTypes.number, | ||||
|         onError: React.PropTypes.func, | ||||
|         onRegisterClick: React.PropTypes.func // onRegisterClick(Object) => ?Promise
 | ||||
|     }, | ||||
| 
 | ||||
|     getDefaultProps: function() { | ||||
|         return { | ||||
|             showEmail: false, | ||||
|             minPasswordLength: 6, | ||||
|             onError: function(e) { | ||||
|                 console.error(e); | ||||
|             } | ||||
|         }; | ||||
|     }, | ||||
| 
 | ||||
|     getInitialState: function() { | ||||
|         return { | ||||
|             email: this.props.defaultEmail, | ||||
|             username: this.props.defaultUsername, | ||||
|             password: null, | ||||
|             passwordConfirm: null | ||||
|         }; | ||||
|     }, | ||||
| 
 | ||||
|     onSubmit: function(ev) { | ||||
|         ev.preventDefault(); | ||||
| 
 | ||||
|         var pwd1 = this.refs.password.value.trim(); | ||||
|         var pwd2 = this.refs.passwordConfirm.value.trim() | ||||
| 
 | ||||
|         var errCode; | ||||
|         if (!pwd1 || !pwd2) { | ||||
|             errCode = "RegistrationForm.ERR_PASSWORD_MISSING"; | ||||
|         } | ||||
|         else if (pwd1 !== pwd2) { | ||||
|             errCode = "RegistrationForm.ERR_PASSWORD_MISMATCH"; | ||||
|         } | ||||
|         else if (pwd1.length < this.props.minPasswordLength) { | ||||
|             errCode = "RegistrationForm.ERR_PASSWORD_LENGTH"; | ||||
|         } | ||||
|         if (errCode) { | ||||
|             this.props.onError(errCode); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         var promise = this.props.onRegisterClick({ | ||||
|             username: this.refs.username.value.trim(), | ||||
|             password: pwd1, | ||||
|             email: this.refs.email.value.trim() | ||||
|         }); | ||||
| 
 | ||||
|         if (promise) { | ||||
|             ev.target.disabled = true; | ||||
|             promise.finally(function() { | ||||
|                 ev.target.disabled = false; | ||||
|             }); | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     render: function() { | ||||
|         var emailSection, registerButton; | ||||
|         if (this.props.showEmail) { | ||||
|             emailSection = ( | ||||
|                 <input className="mx_Login_field" type="text" ref="email" | ||||
|                     autoFocus={true} placeholder="Email address" | ||||
|                     defaultValue={this.state.email} /> | ||||
|             ); | ||||
|         } | ||||
|         if (this.props.onRegisterClick) { | ||||
|             registerButton = ( | ||||
|                 <input className="mx_Login_submit" type="submit" value="Register" /> | ||||
|             ); | ||||
|         } | ||||
| 
 | ||||
|         return ( | ||||
|             <div> | ||||
|                 <form onSubmit={this.onSubmit}> | ||||
|                     {emailSection} | ||||
|                     <br /> | ||||
|                     <input className="mx_Login_field" type="text" ref="username" | ||||
|                         placeholder="User name" defaultValue={this.state.username} /> | ||||
|                     <br /> | ||||
|                     <input className="mx_Login_field" type="password" ref="password" | ||||
|                         placeholder="Password" defaultValue={this.state.password} /> | ||||
|                     <br /> | ||||
|                     <input className="mx_Login_field" type="password" ref="passwordConfirm" | ||||
|                         placeholder="Confirm password" | ||||
|                         defaultValue={this.state.passwordConfirm} /> | ||||
|                     <br /> | ||||
|                     {registerButton} | ||||
|                 </form> | ||||
|             </div> | ||||
|         ); | ||||
|     } | ||||
| }); | ||||
|  | @ -1,161 +0,0 @@ | |||
| /* | ||||
| Copyright 2015 OpenMarket Ltd | ||||
| 
 | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
| 
 | ||||
|     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| 
 | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| 'use strict'; | ||||
| 
 | ||||
| var React = require('react'); | ||||
| var Modal = require('matrix-react-sdk/lib/Modal'); | ||||
| var sdk = require('matrix-react-sdk') | ||||
| 
 | ||||
| /** | ||||
|  * A pure UI component which displays the HS and IS to use. | ||||
|  */ | ||||
| module.exports = React.createClass({ | ||||
|     displayName: 'ServerConfig', | ||||
| 
 | ||||
|     propTypes: { | ||||
|         onHsUrlChanged: React.PropTypes.func, | ||||
|         onIsUrlChanged: React.PropTypes.func, | ||||
|         defaultHsUrl: React.PropTypes.string, | ||||
|         defaultIsUrl: React.PropTypes.string, | ||||
|         withToggleButton: React.PropTypes.bool, | ||||
|         delayTimeMs: React.PropTypes.number // time to wait before invoking onChanged
 | ||||
|     }, | ||||
| 
 | ||||
|     getDefaultProps: function() { | ||||
|         return { | ||||
|             onHsUrlChanged: function() {}, | ||||
|             onIsUrlChanged: function() {}, | ||||
|             withToggleButton: false, | ||||
|             delayTimeMs: 0 | ||||
|         }; | ||||
|     }, | ||||
| 
 | ||||
|     getInitialState: function() { | ||||
|         return { | ||||
|             hs_url: this.props.defaultHsUrl, | ||||
|             is_url: this.props.defaultIsUrl, | ||||
|             original_hs_url: this.props.defaultHsUrl, | ||||
|             original_is_url: this.props.defaultIsUrl, | ||||
|             // no toggle button = show, toggle button = hide
 | ||||
|             configVisible: !this.props.withToggleButton | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     onHomeserverChanged: function(ev) { | ||||
|         this.setState({hs_url: ev.target.value}, function() { | ||||
|             this._hsTimeoutId = this._waitThenInvoke(this._hsTimeoutId, function() { | ||||
|                 this.props.onHsUrlChanged(this.state.hs_url); | ||||
|             }); | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     onIdentityServerChanged: function(ev) { | ||||
|         this.setState({is_url: ev.target.value}, function() { | ||||
|             this._isTimeoutId = this._waitThenInvoke(this._isTimeoutId, function() { | ||||
|                 this.props.onIsUrlChanged(this.state.is_url); | ||||
|             }); | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     _waitThenInvoke: function(existingTimeoutId, fn) { | ||||
|         if (existingTimeoutId) { | ||||
|             clearTimeout(existingTimeoutId); | ||||
|         } | ||||
|         return setTimeout(fn.bind(this), this.props.delayTimeMs); | ||||
|     }, | ||||
| 
 | ||||
|     getHsUrl: function() { | ||||
|         return this.state.hs_url; | ||||
|     }, | ||||
| 
 | ||||
|     getIsUrl: function() { | ||||
|         return this.state.is_url; | ||||
|     }, | ||||
| 
 | ||||
|     onServerConfigVisibleChange: function(ev) { | ||||
|         this.setState({ | ||||
|             configVisible: ev.target.checked | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     showHelpPopup: function() { | ||||
|         var ErrorDialog = sdk.getComponent('organisms.ErrorDialog'); | ||||
|         Modal.createDialog(ErrorDialog, { | ||||
|             title: 'Custom Server Options', | ||||
|             description: <span> | ||||
|                 You can use the custom server options to log into other Matrix | ||||
|                 servers by specifying a different Home server URL. | ||||
|                 <br/> | ||||
|                 This allows you to use Vector with an existing Matrix account on | ||||
|                 a different Home server. | ||||
|                 <br/> | ||||
|                 <br/> | ||||
|                 You can also set a custom Identity server but this will affect | ||||
|                 people's ability to find you if you use a server in a group other | ||||
|                 than the main Matrix.org group. | ||||
|             </span>, | ||||
|             button: "Dismiss", | ||||
|             focus: true | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     render: function() { | ||||
|         var serverConfigStyle = {}; | ||||
|         serverConfigStyle.display = this.state.configVisible ? 'block' : 'none'; | ||||
| 
 | ||||
|         var toggleButton; | ||||
|         if (this.props.withToggleButton) { | ||||
|             toggleButton = ( | ||||
|                 <div> | ||||
|                     <input className="mx_Login_checkbox" id="advanced" type="checkbox" | ||||
|                         checked={this.state.configVisible} | ||||
|                         onChange={this.onServerConfigVisibleChange} /> | ||||
|                     <label className="mx_Login_label" htmlFor="advanced"> | ||||
|                         Use custom server options (advanced) | ||||
|                     </label> | ||||
|                 </div> | ||||
|             ); | ||||
|         } | ||||
| 
 | ||||
|         return ( | ||||
|         <div> | ||||
|             {toggleButton} | ||||
|             <div style={serverConfigStyle}> | ||||
|                 <div className="mx_ServerConfig"> | ||||
|                     <label className="mx_Login_label mx_ServerConfig_hslabel" htmlFor="hsurl"> | ||||
|                         Home server URL | ||||
|                     </label> | ||||
|                     <input className="mx_Login_field" id="hsurl" type="text" | ||||
|                         placeholder={this.state.original_hs_url} | ||||
|                         value={this.state.hs_url} | ||||
|                         onChange={this.onHomeserverChanged} /> | ||||
|                     <label className="mx_Login_label mx_ServerConfig_islabel" htmlFor="isurl"> | ||||
|                         Identity server URL | ||||
|                     </label> | ||||
|                     <input className="mx_Login_field" id="isurl" type="text" | ||||
|                         placeholder={this.state.original_is_url} | ||||
|                         value={this.state.is_url} | ||||
|                         onChange={this.onIdentityServerChanged} /> | ||||
|                     <a className="mx_ServerConfig_help" href="#" onClick={this.showHelpPopup}> | ||||
|                         What does this mean? | ||||
|                     </a> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|         ); | ||||
|     } | ||||
| }); | ||||
|  | @ -17,7 +17,7 @@ limitations under the License. | |||
| 'use strict'; | ||||
| 
 | ||||
| var React = require('react'); | ||||
| var DateUtils = require('../../../DateUtils'); | ||||
| var DateUtils = require('matrix-react-sdk/lib/DateUtils'); | ||||
| 
 | ||||
| module.exports = React.createClass({ | ||||
|     displayName: 'MessageTimestamp', | ||||
|  |  | |||
|  | @ -77,7 +77,7 @@ var roomTileSource = { | |||
|                 MatrixClientPeg.get().deleteRoomTag(item.room.roomId, item.originalList.props.tagName).finally(function() { | ||||
|                     //component.state.set({ spinner: component.state.spinner-- });
 | ||||
|                 }).fail(function(err) { | ||||
|                     var ErrorDialog = sdk.getComponent("organisms.ErrorDialog"); | ||||
|                     var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); | ||||
|                     Modal.createDialog(ErrorDialog, { | ||||
|                         title: "Failed to remove tag " + item.originalList.props.tagName + " from room", | ||||
|                         description: err.toString() | ||||
|  | @ -96,7 +96,7 @@ var roomTileSource = { | |||
|                 MatrixClientPeg.get().setRoomTag(item.room.roomId, item.targetList.props.tagName, newOrder).finally(function() { | ||||
|                     //component.state.set({ spinner: component.state.spinner-- });
 | ||||
|                 }).fail(function(err) { | ||||
|                     var ErrorDialog = sdk.getComponent("organisms.ErrorDialog"); | ||||
|                     var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); | ||||
|                     Modal.createDialog(ErrorDialog, { | ||||
|                         title: "Failed to add tag " + item.targetList.props.tagName + " to room", | ||||
|                         description: err.toString() | ||||
|  |  | |||
|  | @ -1,210 +0,0 @@ | |||
| /* | ||||
| Copyright 2015 OpenMarket Ltd | ||||
| 
 | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
| 
 | ||||
|     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| 
 | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| 'use strict'; | ||||
| 
 | ||||
| var React = require("react"); | ||||
| var ReactDOM = require("react-dom"); | ||||
| 
 | ||||
| var MatrixClientPeg = require("matrix-react-sdk/lib/MatrixClientPeg"); | ||||
| var RoomListSorter = require("matrix-react-sdk/lib/RoomListSorter"); | ||||
| var dis = require("matrix-react-sdk/lib/dispatcher"); | ||||
| 
 | ||||
| var sdk = require('matrix-react-sdk'); | ||||
| var VectorConferenceHandler = require("../../modules/VectorConferenceHandler"); | ||||
| 
 | ||||
| var HIDE_CONFERENCE_CHANS = true; | ||||
| 
 | ||||
| module.exports = { | ||||
|     getInitialState: function() { | ||||
|         return { | ||||
|             activityMap: null, | ||||
|             lists: {}, | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     componentWillMount: function() { | ||||
|         var cli = MatrixClientPeg.get(); | ||||
|         cli.on("Room", this.onRoom); | ||||
|         cli.on("Room.timeline", this.onRoomTimeline); | ||||
|         cli.on("Room.name", this.onRoomName); | ||||
|         cli.on("Room.tags", this.onRoomTags); | ||||
|         cli.on("RoomState.events", this.onRoomStateEvents); | ||||
|         cli.on("RoomMember.name", this.onRoomMemberName); | ||||
| 
 | ||||
|         var s = this.getRoomLists(); | ||||
|         s.activityMap = {}; | ||||
|         this.setState(s); | ||||
|     }, | ||||
| 
 | ||||
|     componentDidMount: function() { | ||||
|         this.dispatcherRef = dis.register(this.onAction); | ||||
|     }, | ||||
| 
 | ||||
|     onAction: function(payload) { | ||||
|         switch (payload.action) { | ||||
|             case 'view_tooltip': | ||||
|                 this.tooltip = payload.tooltip; | ||||
|                 this._repositionTooltip(); | ||||
|                 if (this.tooltip) this.tooltip.style.display = 'block'; | ||||
|                 break | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     componentWillUnmount: function() { | ||||
|         dis.unregister(this.dispatcherRef); | ||||
|         if (MatrixClientPeg.get()) { | ||||
|             MatrixClientPeg.get().removeListener("Room", this.onRoom); | ||||
|             MatrixClientPeg.get().removeListener("Room.timeline", this.onRoomTimeline); | ||||
|             MatrixClientPeg.get().removeListener("Room.name", this.onRoomName); | ||||
|             MatrixClientPeg.get().removeListener("RoomState.events", this.onRoomStateEvents); | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     componentWillReceiveProps: function(newProps) { | ||||
|         this.state.activityMap[newProps.selectedRoom] = undefined; | ||||
|         this.setState({ | ||||
|             activityMap: this.state.activityMap | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     onRoom: function(room) { | ||||
|         this.refreshRoomList(); | ||||
|     }, | ||||
| 
 | ||||
|     onRoomTimeline: function(ev, room, toStartOfTimeline) { | ||||
|         if (toStartOfTimeline) return; | ||||
| 
 | ||||
|         var hl = 0; | ||||
|         if ( | ||||
|             room.roomId != this.props.selectedRoom && | ||||
|             ev.getSender() != MatrixClientPeg.get().credentials.userId) | ||||
|         { | ||||
|             // don't mark rooms as unread for just member changes
 | ||||
|             if (ev.getType() != "m.room.member") { | ||||
|                 hl = 1; | ||||
|             } | ||||
| 
 | ||||
|             var actions = MatrixClientPeg.get().getPushActionsForEvent(ev); | ||||
|             if (actions && actions.tweaks && actions.tweaks.highlight) { | ||||
|                 hl = 2; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if (hl > 0) { | ||||
|             var newState = this.getRoomLists(); | ||||
| 
 | ||||
|             // obviously this won't deep copy but this shouldn't be necessary
 | ||||
|             var amap = this.state.activityMap; | ||||
|             amap[room.roomId] = Math.max(amap[room.roomId] || 0, hl); | ||||
| 
 | ||||
|             newState.activityMap = amap; | ||||
| 
 | ||||
|             this.setState(newState); | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     onRoomName: function(room) { | ||||
|         this.refreshRoomList(); | ||||
|     }, | ||||
| 
 | ||||
|     onRoomTags: function(event, room) { | ||||
|         this.refreshRoomList();         | ||||
|     }, | ||||
| 
 | ||||
|     onRoomStateEvents: function(ev, state) { | ||||
|         setTimeout(this.refreshRoomList, 0); | ||||
|     }, | ||||
| 
 | ||||
|     onRoomMemberName: function(ev, member) { | ||||
|         setTimeout(this.refreshRoomList, 0); | ||||
|     }, | ||||
| 
 | ||||
|     refreshRoomList: function() { | ||||
|         // TODO: rather than bluntly regenerating and re-sorting everything
 | ||||
|         // every time we see any kind of room change from the JS SDK
 | ||||
|         // we could do incremental updates on our copy of the state
 | ||||
|         // based on the room which has actually changed.  This would stop
 | ||||
|         // us re-rendering all the sublists every time anything changes anywhere
 | ||||
|         // in the state of the client.
 | ||||
|         this.setState(this.getRoomLists()); | ||||
|     }, | ||||
| 
 | ||||
|     getRoomLists: function() { | ||||
|         var s = { lists: {} }; | ||||
| 
 | ||||
|         s.lists["m.invite"] = []; | ||||
|         s.lists["m.favourite"] = []; | ||||
|         s.lists["m.recent"] = []; | ||||
|         s.lists["m.lowpriority"] = []; | ||||
|         s.lists["m.archived"] = []; | ||||
| 
 | ||||
|         MatrixClientPeg.get().getRooms().forEach(function(room) { | ||||
|             var me = room.getMember(MatrixClientPeg.get().credentials.userId); | ||||
| 
 | ||||
|             if (me && me.membership == "invite") { | ||||
|                 s.lists["m.invite"].push(room); | ||||
|             } | ||||
|             else { | ||||
|                 var shouldShowRoom =  ( | ||||
|                     me && (me.membership == "join") | ||||
|                 ); | ||||
| 
 | ||||
|                 // hiding conf rooms only ever toggles shouldShowRoom to false
 | ||||
|                 if (shouldShowRoom && HIDE_CONFERENCE_CHANS) { | ||||
|                     // we want to hide the 1:1 conf<->user room and not the group chat
 | ||||
|                     var joinedMembers = room.getJoinedMembers(); | ||||
|                     if (joinedMembers.length === 2) { | ||||
|                         var otherMember = joinedMembers.filter(function(m) { | ||||
|                             return m.userId !== me.userId | ||||
|                         })[0]; | ||||
|                         if (VectorConferenceHandler.isConferenceUser(otherMember)) { | ||||
|                             // console.log("Hiding conference 1:1 room %s", room.roomId);
 | ||||
|                             shouldShowRoom = false; | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 if (shouldShowRoom) { | ||||
|                     var tagNames = Object.keys(room.tags); | ||||
|                     if (tagNames.length) { | ||||
|                         for (var i = 0; i < tagNames.length; i++) { | ||||
|                             var tagName = tagNames[i]; | ||||
|                             s.lists[tagName] = s.lists[tagName] || []; | ||||
|                             s.lists[tagNames[i]].push(room); | ||||
|                         } | ||||
|                     } | ||||
|                     else { | ||||
|                         s.lists["m.recent"].push(room);  | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
| 
 | ||||
|         //console.log("calculated new roomLists; m.recent = " + s.lists["m.recent"]);
 | ||||
| 
 | ||||
|         // we actually apply the sorting to this when receiving the prop in RoomSubLists.
 | ||||
| 
 | ||||
|         return s; | ||||
|     }, | ||||
| 
 | ||||
|     _repositionTooltip: function(e) { | ||||
|         if (this.tooltip && this.tooltip.parentElement) { | ||||
|             var scroll = ReactDOM.findDOMNode(this); | ||||
|             this.tooltip.style.top = (scroll.parentElement.offsetTop + this.tooltip.parentElement.offsetTop - scroll.children[2].scrollTop) + "px";  | ||||
|         } | ||||
|     }, | ||||
| }; | ||||
|  | @ -1,738 +0,0 @@ | |||
| /* | ||||
| Copyright 2015 OpenMarket Ltd | ||||
| 
 | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
| 
 | ||||
|     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| 
 | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| var Matrix = require("matrix-js-sdk"); | ||||
| var MatrixClientPeg = require("matrix-react-sdk/lib/MatrixClientPeg"); | ||||
| var React = require("react"); | ||||
| var ReactDOM = require("react-dom"); | ||||
| var q = require("q"); | ||||
| var ContentMessages = require("matrix-react-sdk/lib//ContentMessages"); | ||||
| var WhoIsTyping = require("matrix-react-sdk/lib/WhoIsTyping"); | ||||
| var Modal = require("matrix-react-sdk/lib/Modal"); | ||||
| var sdk = require('matrix-react-sdk/lib/index'); | ||||
| var CallHandler = require('matrix-react-sdk/lib/CallHandler'); | ||||
| var VectorConferenceHandler = require('../../modules/VectorConferenceHandler'); | ||||
| var Resend = require("../../Resend"); | ||||
| 
 | ||||
| var dis = require("matrix-react-sdk/lib/dispatcher"); | ||||
| 
 | ||||
| var PAGINATE_SIZE = 20; | ||||
| var INITIAL_SIZE = 20; | ||||
| 
 | ||||
| module.exports = { | ||||
|     getInitialState: function() { | ||||
|         var room = this.props.roomId ? MatrixClientPeg.get().getRoom(this.props.roomId) : null; | ||||
|         return { | ||||
|             room: room, | ||||
|             messageCap: INITIAL_SIZE, | ||||
|             editingRoomSettings: false, | ||||
|             uploadingRoomSettings: false, | ||||
|             numUnreadMessages: 0, | ||||
|             draggingFile: false, | ||||
|             searching: false, | ||||
|             searchResults: null, | ||||
|             syncState: MatrixClientPeg.get().getSyncState(), | ||||
|             hasUnsentMessages: this._hasUnsentMessages(room) | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     componentWillMount: function() { | ||||
|         this.dispatcherRef = dis.register(this.onAction); | ||||
|         MatrixClientPeg.get().on("Room.timeline", this.onRoomTimeline); | ||||
|         MatrixClientPeg.get().on("Room.name", this.onRoomName); | ||||
|         MatrixClientPeg.get().on("Room.receipt", this.onRoomReceipt); | ||||
|         MatrixClientPeg.get().on("RoomMember.typing", this.onRoomMemberTyping); | ||||
|         MatrixClientPeg.get().on("RoomState.members", this.onRoomStateMember); | ||||
|         MatrixClientPeg.get().on("sync", this.onSyncStateChange); | ||||
|         this.atBottom = true; | ||||
|     }, | ||||
| 
 | ||||
|     componentWillUnmount: function() { | ||||
|         if (this.refs.messagePanel) { | ||||
|             var messagePanel = ReactDOM.findDOMNode(this.refs.messagePanel); | ||||
|             messagePanel.removeEventListener('drop', this.onDrop); | ||||
|             messagePanel.removeEventListener('dragover', this.onDragOver); | ||||
|             messagePanel.removeEventListener('dragleave', this.onDragLeaveOrEnd); | ||||
|             messagePanel.removeEventListener('dragend', this.onDragLeaveOrEnd); | ||||
|         } | ||||
|         dis.unregister(this.dispatcherRef); | ||||
|         if (MatrixClientPeg.get()) { | ||||
|             MatrixClientPeg.get().removeListener("Room.timeline", this.onRoomTimeline); | ||||
|             MatrixClientPeg.get().removeListener("Room.name", this.onRoomName); | ||||
|             MatrixClientPeg.get().removeListener("Room.receipt", this.onRoomReceipt); | ||||
|             MatrixClientPeg.get().removeListener("RoomMember.typing", this.onRoomMemberTyping); | ||||
|             MatrixClientPeg.get().removeListener("RoomState.members", this.onRoomStateMember); | ||||
|             MatrixClientPeg.get().removeListener("sync", this.onSyncStateChange); | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     onAction: function(payload) { | ||||
|         switch (payload.action) { | ||||
|             case 'message_send_failed': | ||||
|             case 'message_sent': | ||||
|                 this.setState({ | ||||
|                     hasUnsentMessages: this._hasUnsentMessages(this.state.room) | ||||
|                 }); | ||||
|             case 'message_resend_started': | ||||
|                 this.setState({ | ||||
|                     room: MatrixClientPeg.get().getRoom(this.props.roomId) | ||||
|                 }); | ||||
|                 this.forceUpdate(); | ||||
|                 break; | ||||
|             case 'notifier_enabled': | ||||
|                 this.forceUpdate(); | ||||
|                 break; | ||||
|             case 'call_state': | ||||
|                 if (CallHandler.getCallForRoom(this.props.roomId)) { | ||||
|                     // Call state has changed so we may be loading video elements
 | ||||
|                     // which will obscure the message log.
 | ||||
|                     // scroll to bottom
 | ||||
|                     var scrollNode = this._getScrollNode(); | ||||
|                     if (scrollNode) { | ||||
|                         scrollNode.scrollTop = scrollNode.scrollHeight; | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 // possibly remove the conf call notification if we're now in
 | ||||
|                 // the conf
 | ||||
|                 this._updateConfCallNotification(); | ||||
|                 break; | ||||
|             case 'user_activity': | ||||
|                 this.sendReadReceipt(); | ||||
|                 break; | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     _getScrollNode: function() { | ||||
|         var panel = ReactDOM.findDOMNode(this.refs.messagePanel); | ||||
|         if (!panel) return null; | ||||
| 
 | ||||
|         if (panel.classList.contains('gm-prevented')) { | ||||
|             return panel; | ||||
|         } else { | ||||
|             return panel.children[2]; // XXX: Fragile!
 | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     onSyncStateChange: function(state) { | ||||
|         this.setState({ | ||||
|             syncState: state | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     // MatrixRoom still showing the messages from the old room?
 | ||||
|     // Set the key to the room_id. Sadly you can no longer get at
 | ||||
|     // the key from inside the component, or we'd check this in code.
 | ||||
|     /*componentWillReceiveProps: function(props) { | ||||
|     },*/ | ||||
| 
 | ||||
|     onRoomTimeline: function(ev, room, toStartOfTimeline) { | ||||
|         if (!this.isMounted()) return; | ||||
| 
 | ||||
|         // ignore anything that comes in whilst paginating: we get one
 | ||||
|         // event for each new matrix event so this would cause a huge
 | ||||
|         // number of UI updates. Just update the UI when the paginate
 | ||||
|         // call returns.
 | ||||
|         if (this.state.paginating) return; | ||||
| 
 | ||||
|         // no point handling anything while we're waiting for the join to finish:
 | ||||
|         // we'll only be showing a spinner.
 | ||||
|         if (this.state.joining) return; | ||||
|         if (room.roomId != this.props.roomId) return; | ||||
| 
 | ||||
|         var scrollNode = this._getScrollNode(); | ||||
|         if (scrollNode) { | ||||
|             this.atBottom = ( | ||||
|                 scrollNode.scrollHeight - scrollNode.scrollTop <= | ||||
|                 (scrollNode.clientHeight + 150) // 150?
 | ||||
|             ); | ||||
|         } | ||||
| 
 | ||||
|         var currentUnread = this.state.numUnreadMessages; | ||||
|         if (!toStartOfTimeline && | ||||
|                 (ev.getSender() !== MatrixClientPeg.get().credentials.userId)) { | ||||
|             // update unread count when scrolled up
 | ||||
|             if (this.atBottom) { | ||||
|                 currentUnread = 0; | ||||
|             } | ||||
|             else { | ||||
|                 currentUnread += 1; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         this.setState({ | ||||
|             room: MatrixClientPeg.get().getRoom(this.props.roomId), | ||||
|             numUnreadMessages: currentUnread | ||||
|         }); | ||||
| 
 | ||||
|         if (toStartOfTimeline && !this.state.paginating) { | ||||
|             this.fillSpace(); | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     onRoomName: function(room) { | ||||
|         if (room.roomId == this.props.roomId) { | ||||
|             this.setState({ | ||||
|                 room: room | ||||
|             }); | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     onRoomReceipt: function(receiptEvent, room) { | ||||
|         if (room.roomId == this.props.roomId) { | ||||
|             this.forceUpdate(); | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     onRoomMemberTyping: function(ev, member) { | ||||
|         this.forceUpdate(); | ||||
|     }, | ||||
| 
 | ||||
|     onRoomStateMember: function(ev, state, member) { | ||||
|         if (member.roomId !== this.props.roomId || | ||||
|                 member.userId !== VectorConferenceHandler.getConferenceUserIdForRoom(member.roomId)) { | ||||
|             return; | ||||
|         } | ||||
|         this._updateConfCallNotification(); | ||||
|     }, | ||||
| 
 | ||||
|     _hasUnsentMessages: function(room) { | ||||
|         return this._getUnsentMessages(room).length > 0; | ||||
|     }, | ||||
| 
 | ||||
|     _getUnsentMessages: function(room) { | ||||
|         if (!room) { return []; } | ||||
|         // TODO: It would be nice if the JS SDK provided nicer constant-time
 | ||||
|         // constructs rather than O(N) (N=num msgs) on this.
 | ||||
|         return room.timeline.filter(function(ev) { | ||||
|             return ev.status === Matrix.EventStatus.NOT_SENT; | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     _updateConfCallNotification: function() { | ||||
|         var room = MatrixClientPeg.get().getRoom(this.props.roomId); | ||||
|         if (!room) return; | ||||
|         var confMember = room.getMember( | ||||
|             VectorConferenceHandler.getConferenceUserIdForRoom(this.props.roomId) | ||||
|         ); | ||||
| 
 | ||||
|         if (!confMember) { | ||||
|             return; | ||||
|         } | ||||
|         var confCall = VectorConferenceHandler.getConferenceCallForRoom(confMember.roomId); | ||||
| 
 | ||||
|         // A conf call notification should be displayed if there is an ongoing
 | ||||
|         // conf call but this cilent isn't a part of it.
 | ||||
|         this.setState({ | ||||
|             displayConfCallNotification: ( | ||||
|                 (!confCall || confCall.call_state === "ended") && | ||||
|                 confMember.membership === "join" | ||||
|             ) | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     componentDidMount: function() { | ||||
|         if (this.refs.messagePanel) { | ||||
|             var messagePanel = ReactDOM.findDOMNode(this.refs.messagePanel); | ||||
| 
 | ||||
|             messagePanel.addEventListener('drop', this.onDrop); | ||||
|             messagePanel.addEventListener('dragover', this.onDragOver); | ||||
|             messagePanel.addEventListener('dragleave', this.onDragLeaveOrEnd); | ||||
|             messagePanel.addEventListener('dragend', this.onDragLeaveOrEnd); | ||||
| 
 | ||||
|             var messageWrapperScroll = this._getScrollNode(); | ||||
| 
 | ||||
|             messageWrapperScroll.scrollTop = messageWrapperScroll.scrollHeight; | ||||
| 
 | ||||
|             this.sendReadReceipt(); | ||||
| 
 | ||||
|             this.fillSpace(); | ||||
|         } | ||||
| 
 | ||||
|         this._updateConfCallNotification(); | ||||
|     }, | ||||
| 
 | ||||
|     componentDidUpdate: function() { | ||||
|         if (!this.refs.messagePanel) return; | ||||
| 
 | ||||
|         var messageWrapperScroll = this._getScrollNode(); | ||||
| 
 | ||||
|         if (this.state.paginating && !this.waiting_for_paginate) { | ||||
|             var heightGained = messageWrapperScroll.scrollHeight - this.oldScrollHeight; | ||||
|             messageWrapperScroll.scrollTop += heightGained; | ||||
|             this.oldScrollHeight = undefined; | ||||
|             if (!this.fillSpace()) { | ||||
|                 this.setState({paginating: false}); | ||||
|             } | ||||
|         } else if (this.atBottom) { | ||||
|             messageWrapperScroll.scrollTop = messageWrapperScroll.scrollHeight; | ||||
|             if (this.state.numUnreadMessages !== 0) { | ||||
|                 this.setState({numUnreadMessages: 0}); | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     fillSpace: function() { | ||||
|         if (!this.refs.messagePanel) return; | ||||
|         if (this.state.searchResults) return; // TODO: paginate search results
 | ||||
|         var messageWrapperScroll = this._getScrollNode(); | ||||
|         if (messageWrapperScroll.scrollTop < messageWrapperScroll.clientHeight && this.state.room.oldState.paginationToken) { | ||||
|             this.setState({paginating: true}); | ||||
| 
 | ||||
|             this.oldScrollHeight = messageWrapperScroll.scrollHeight; | ||||
| 
 | ||||
|             if (this.state.messageCap < this.state.room.timeline.length) { | ||||
|                 this.waiting_for_paginate = false; | ||||
|                 var cap = Math.min(this.state.messageCap + PAGINATE_SIZE, this.state.room.timeline.length); | ||||
|                 this.setState({messageCap: cap, paginating: true}); | ||||
|             } else { | ||||
|                 this.waiting_for_paginate = true; | ||||
|                 var cap = this.state.messageCap + PAGINATE_SIZE; | ||||
|                 this.setState({messageCap: cap, paginating: true}); | ||||
|                 var self = this; | ||||
|                 MatrixClientPeg.get().scrollback(this.state.room, PAGINATE_SIZE).finally(function() { | ||||
|                     self.waiting_for_paginate = false; | ||||
|                     if (self.isMounted()) { | ||||
|                         self.setState({ | ||||
|                             room: MatrixClientPeg.get().getRoom(self.props.roomId) | ||||
|                         }); | ||||
|                     } | ||||
|                     // wait and set paginating to false when the component updates
 | ||||
|                 }); | ||||
|             } | ||||
| 
 | ||||
|             return true; | ||||
|         } | ||||
|         return false; | ||||
|     }, | ||||
| 
 | ||||
|     onResendAllClick: function() { | ||||
|         var eventsToResend = this._getUnsentMessages(this.state.room); | ||||
|         eventsToResend.forEach(function(event) { | ||||
|             Resend.resend(event); | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     onJoinButtonClicked: function(ev) { | ||||
|         var self = this; | ||||
|         MatrixClientPeg.get().joinRoom(this.props.roomId).then(function() { | ||||
|             self.setState({ | ||||
|                 joining: false, | ||||
|                 room: MatrixClientPeg.get().getRoom(self.props.roomId) | ||||
|             }); | ||||
|         }, function(error) { | ||||
|             self.setState({ | ||||
|                 joining: false, | ||||
|                 joinError: error | ||||
|             }); | ||||
|         }); | ||||
|         this.setState({ | ||||
|             joining: true | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     onMessageListScroll: function(ev) { | ||||
|         if (this.refs.messagePanel) { | ||||
|             var messageWrapperScroll = this._getScrollNode(); | ||||
|             var wasAtBottom = this.atBottom; | ||||
|             this.atBottom = messageWrapperScroll.scrollHeight - messageWrapperScroll.scrollTop <= messageWrapperScroll.clientHeight + 1; | ||||
|             if (this.atBottom && !wasAtBottom) { | ||||
|                 this.forceUpdate(); // remove unread msg count
 | ||||
|             } | ||||
|         } | ||||
|         if (!this.state.paginating) this.fillSpace(); | ||||
|     }, | ||||
| 
 | ||||
|     onDragOver: function(ev) { | ||||
|         ev.stopPropagation(); | ||||
|         ev.preventDefault(); | ||||
| 
 | ||||
|         ev.dataTransfer.dropEffect = 'none'; | ||||
| 
 | ||||
|         var items = ev.dataTransfer.items; | ||||
|         if (items.length == 1) { | ||||
|             if (items[0].kind == 'file') { | ||||
|                 this.setState({ draggingFile : true }); | ||||
|                 ev.dataTransfer.dropEffect = 'copy'; | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     onDrop: function(ev) { | ||||
|         ev.stopPropagation(); | ||||
|         ev.preventDefault(); | ||||
|         this.setState({ draggingFile : false }); | ||||
|         var files = ev.dataTransfer.files; | ||||
|         if (files.length == 1) { | ||||
|             this.uploadFile(files[0]); | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     onDragLeaveOrEnd: function(ev) { | ||||
|         ev.stopPropagation(); | ||||
|         ev.preventDefault(); | ||||
|         this.setState({ draggingFile : false }); | ||||
|     }, | ||||
| 
 | ||||
|     uploadFile: function(file) { | ||||
|         this.setState({ | ||||
|             upload: { | ||||
|                 fileName: file.name, | ||||
|                 uploadedBytes: 0, | ||||
|                 totalBytes: file.size | ||||
|             } | ||||
|         }); | ||||
|         var self = this; | ||||
|         ContentMessages.sendContentToRoom( | ||||
|             file, this.props.roomId, MatrixClientPeg.get() | ||||
|         ).progress(function(ev) { | ||||
|             //console.log("Upload: "+ev.loaded+" / "+ev.total);
 | ||||
|             self.setState({ | ||||
|                 upload: { | ||||
|                     fileName: file.name, | ||||
|                     uploadedBytes: ev.loaded, | ||||
|                     totalBytes: ev.total | ||||
|                 } | ||||
|             }); | ||||
|         }).finally(function() { | ||||
|             self.setState({ | ||||
|                 upload: undefined | ||||
|             }); | ||||
|         }).done(undefined, function(error) { | ||||
|             var ErrorDialog = sdk.getComponent("organisms.ErrorDialog"); | ||||
|             Modal.createDialog(ErrorDialog, { | ||||
|                 title: "Failed to upload file", | ||||
|                 description: error.toString() | ||||
|             }); | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     getWhoIsTypingString: function() { | ||||
|         return WhoIsTyping.whoIsTypingString(this.state.room); | ||||
|     }, | ||||
| 
 | ||||
|     onSearch: function(term, scope) { | ||||
|         var filter; | ||||
|         if (scope === "Room") { | ||||
|             filter = { | ||||
|                 // XXX: it's unintuitive that the filter for searching doesn't have the same shape as the v2 filter API :(
 | ||||
|                 rooms: [ | ||||
|                     this.props.roomId | ||||
|                 ] | ||||
|             }; | ||||
|         } | ||||
| 
 | ||||
|         var self = this; | ||||
|         MatrixClientPeg.get().search({ | ||||
|             body: { | ||||
|                 search_categories: { | ||||
|                     room_events: { | ||||
|                         search_term: term, | ||||
|                         filter: filter, | ||||
|                         order_by: "recent", | ||||
|                         include_state: true, | ||||
|                         groupings: { | ||||
|                             group_by: [ | ||||
|                                 { | ||||
|                                     key: "room_id" | ||||
|                                 } | ||||
|                             ] | ||||
|                         }, | ||||
|                         event_context: { | ||||
|                             before_limit: 1, | ||||
|                             after_limit: 1, | ||||
|                             include_profile: true, | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             }             | ||||
|         }).then(function(data) { | ||||
|             // for debugging:
 | ||||
|             // data.search_categories.room_events.highlights = ["hello", "everybody"];
 | ||||
| 
 | ||||
|             var highlights; | ||||
|             if (data.search_categories.room_events.highlights && | ||||
|                 data.search_categories.room_events.highlights.length > 0) | ||||
|             { | ||||
|                 // postgres on synapse returns us precise details of the
 | ||||
|                 // strings which actually got matched for highlighting.
 | ||||
|                 // for overlapping highlights, favour longer (more specific) terms first
 | ||||
|                 highlights = data.search_categories.room_events.highlights | ||||
|                              .sort(function(a, b) { b.length - a.length }); | ||||
|             } | ||||
|             else { | ||||
|                 // sqlite doesn't, so just try to highlight the literal search term
 | ||||
|                 highlights = [ term ]; | ||||
|             } | ||||
| 
 | ||||
|             self.setState({ | ||||
|                 highlights: highlights, | ||||
|                 searchResults: data, | ||||
|                 searchScope: scope, | ||||
|             }); | ||||
|         }, function(error) { | ||||
|             var ErrorDialog = sdk.getComponent("organisms.ErrorDialog"); | ||||
|             Modal.createDialog(ErrorDialog, { | ||||
|                 title: "Search failed", | ||||
|                 description: error.toString() | ||||
|             }); | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     getEventTiles: function() { | ||||
|         var DateSeparator = sdk.getComponent('molecules.DateSeparator'); | ||||
|         var cli = MatrixClientPeg.get(); | ||||
| 
 | ||||
|         var ret = []; | ||||
|         var count = 0; | ||||
| 
 | ||||
|         var EventTile = sdk.getComponent('rooms.EventTile'); | ||||
|         var self = this; | ||||
| 
 | ||||
|         if (this.state.searchResults && | ||||
|             this.state.searchResults.search_categories.room_events.results && | ||||
|             this.state.searchResults.search_categories.room_events.groups) | ||||
|         { | ||||
|             // XXX: this dance is foul, due to the results API not directly returning sorted results
 | ||||
|             var results = this.state.searchResults.search_categories.room_events.results; | ||||
|             var roomIdGroups = this.state.searchResults.search_categories.room_events.groups.room_id; | ||||
| 
 | ||||
|             Object.keys(roomIdGroups) | ||||
|                   .sort(function(a, b) { roomIdGroups[a].order - roomIdGroups[b].order }) // WHY NOT RETURN AN ORDERED ARRAY?!?!?!
 | ||||
|                   .forEach(function(roomId) | ||||
|             { | ||||
|                 // XXX: todo: merge overlapping results somehow?
 | ||||
|                 // XXX: why doesn't searching on name work?
 | ||||
|                 if (self.state.searchScope === 'All') { | ||||
|                     ret.push(<li key={ roomId }><h1>Room: { cli.getRoom(roomId).name }</h1></li>); | ||||
|                 } | ||||
| 
 | ||||
|                 var resultList = roomIdGroups[roomId].results.map(function(eventId) { return results[eventId]; }); | ||||
|                 for (var i = resultList.length - 1; i >= 0; i--) { | ||||
|                     var ts1 = resultList[i].result.origin_server_ts; | ||||
|                     ret.push(<li key={ts1 + "-search"}><DateSeparator ts={ts1}/></li>); // Rank: {resultList[i].rank} | ||||
|                     var mxEv = new Matrix.MatrixEvent(resultList[i].result); | ||||
|                     if (resultList[i].context.events_before[0]) { | ||||
|                         var mxEv2 = new Matrix.MatrixEvent(resultList[i].context.events_before[0]); | ||||
|                         if (EventTile.haveTileForEvent(mxEv2)) { | ||||
|                             ret.push(<li key={mxEv.getId() + "-1"}><EventTile mxEvent={mxEv2} contextual={true} /></li>); | ||||
|                         } | ||||
|                     } | ||||
|                     if (EventTile.haveTileForEvent(mxEv)) { | ||||
|                         ret.push(<li key={mxEv.getId() + "+0"}><EventTile mxEvent={mxEv} highlights={self.state.highlights}/></li>); | ||||
|                     } | ||||
|                     if (resultList[i].context.events_after[0]) { | ||||
|                         var mxEv2 = new Matrix.MatrixEvent(resultList[i].context.events_after[0]); | ||||
|                         if (EventTile.haveTileForEvent(mxEv2)) { | ||||
|                             ret.push(<li key={mxEv.getId() + "+1"}><EventTile mxEvent={mxEv2} contextual={true} /></li>); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             }); | ||||
|             return ret; | ||||
|         } | ||||
| 
 | ||||
|         for (var i = this.state.room.timeline.length-1; i >= 0 && count < this.state.messageCap; --i) { | ||||
|             var mxEv = this.state.room.timeline[i]; | ||||
| 
 | ||||
|             if (!EventTile.haveTileForEvent(mxEv)) { | ||||
|                 continue; | ||||
|             } | ||||
| 
 | ||||
|             var continuation = false; | ||||
|             var last = false; | ||||
|             var dateSeparator = null; | ||||
|             if (i == this.state.room.timeline.length - 1) { | ||||
|                 last = true; | ||||
|             } | ||||
|             if (i > 0 && count < this.state.messageCap - 1) { | ||||
|                 if (this.state.room.timeline[i].sender && | ||||
|                     this.state.room.timeline[i - 1].sender && | ||||
|                     (this.state.room.timeline[i].sender.userId === | ||||
|                         this.state.room.timeline[i - 1].sender.userId) && | ||||
|                     (this.state.room.timeline[i].getType() == | ||||
|                         this.state.room.timeline[i - 1].getType()) | ||||
|                     ) | ||||
|                 { | ||||
|                     continuation = true; | ||||
|                 } | ||||
| 
 | ||||
|                 var ts0 = this.state.room.timeline[i - 1].getTs(); | ||||
|                 var ts1 = this.state.room.timeline[i].getTs(); | ||||
|                 if (new Date(ts0).toDateString() !== new Date(ts1).toDateString()) { | ||||
|                     dateSeparator = <li key={ts1}><DateSeparator key={ts1} ts={ts1}/></li>; | ||||
|                     continuation = false; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             if (i === 1) { // n.b. 1, not 0, as the 0th event is an m.room.create and so doesn't show on the timeline
 | ||||
|                 var ts1 = this.state.room.timeline[i].getTs(); | ||||
|                 dateSeparator = <li key={ts1}><DateSeparator ts={ts1}/></li>; | ||||
|                 continuation = false; | ||||
|             } | ||||
| 
 | ||||
|             ret.unshift( | ||||
|                 <li key={mxEv.getId()} ref={this._collectEventNode.bind(this, mxEv.getId())}><EventTile mxEvent={mxEv} continuation={continuation} last={last}/></li> | ||||
|             ); | ||||
|             if (dateSeparator) { | ||||
|                 ret.unshift(dateSeparator); | ||||
|             } | ||||
|             ++count; | ||||
|         } | ||||
|         return ret; | ||||
|     }, | ||||
| 
 | ||||
|     uploadNewState: function(new_name, new_topic, new_join_rule, new_history_visibility, new_power_levels) { | ||||
|         var old_name = this.state.room.name; | ||||
| 
 | ||||
|         var old_topic = this.state.room.currentState.getStateEvents('m.room.topic', ''); | ||||
|         if (old_topic) { | ||||
|             old_topic = old_topic.getContent().topic; | ||||
|         } else { | ||||
|             old_topic = ""; | ||||
|         } | ||||
| 
 | ||||
|         var old_join_rule = this.state.room.currentState.getStateEvents('m.room.join_rules', ''); | ||||
|         if (old_join_rule) { | ||||
|             old_join_rule = old_join_rule.getContent().join_rule; | ||||
|         } else { | ||||
|             old_join_rule = "invite"; | ||||
|         } | ||||
| 
 | ||||
|         var old_history_visibility = this.state.room.currentState.getStateEvents('m.room.history_visibility', ''); | ||||
|         if (old_history_visibility) { | ||||
|             old_history_visibility = old_history_visibility.getContent().history_visibility; | ||||
|         } else { | ||||
|             old_history_visibility = "shared"; | ||||
|         } | ||||
| 
 | ||||
|         var deferreds = []; | ||||
| 
 | ||||
|         if (old_name != new_name && new_name != undefined && new_name) { | ||||
|             deferreds.push( | ||||
|                 MatrixClientPeg.get().setRoomName(this.state.room.roomId, new_name) | ||||
|             ); | ||||
|         } | ||||
| 
 | ||||
|         if (old_topic != new_topic && new_topic != undefined) { | ||||
|             deferreds.push( | ||||
|                 MatrixClientPeg.get().setRoomTopic(this.state.room.roomId, new_topic) | ||||
|             ); | ||||
|         } | ||||
| 
 | ||||
|         if (old_join_rule != new_join_rule && new_join_rule != undefined) { | ||||
|             deferreds.push( | ||||
|                 MatrixClientPeg.get().sendStateEvent( | ||||
|                     this.state.room.roomId, "m.room.join_rules", { | ||||
|                         join_rule: new_join_rule, | ||||
|                     }, "" | ||||
|                 ) | ||||
|             ); | ||||
|         } | ||||
| 
 | ||||
|         if (old_history_visibility != new_history_visibility && new_history_visibility != undefined) { | ||||
|             deferreds.push( | ||||
|                 MatrixClientPeg.get().sendStateEvent( | ||||
|                     this.state.room.roomId, "m.room.history_visibility", { | ||||
|                         history_visibility: new_history_visibility, | ||||
|                     }, "" | ||||
|                 ) | ||||
|             ); | ||||
|         } | ||||
| 
 | ||||
|         if (new_power_levels) { | ||||
|             deferreds.push( | ||||
|                 MatrixClientPeg.get().sendStateEvent( | ||||
|                     this.state.room.roomId, "m.room.power_levels", new_power_levels, "" | ||||
|                 ) | ||||
|             ); | ||||
|         } | ||||
| 
 | ||||
|         if (deferreds.length) { | ||||
|             var self = this; | ||||
|             q.all(deferreds).fail(function(err) { | ||||
|                 var ErrorDialog = sdk.getComponent("organisms.ErrorDialog"); | ||||
|                 Modal.createDialog(ErrorDialog, { | ||||
|                     title: "Failed to set state", | ||||
|                     description: err.toString() | ||||
|                 }); | ||||
|             }).finally(function() { | ||||
|                 self.setState({ | ||||
|                     uploadingRoomSettings: false, | ||||
|                 }); | ||||
|             }); | ||||
|         } else { | ||||
|             this.setState({ | ||||
|                 editingRoomSettings: false, | ||||
|                 uploadingRoomSettings: false, | ||||
|             }); | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     _collectEventNode: function(eventId, node) { | ||||
|         if (this.eventNodes == undefined) this.eventNodes = {}; | ||||
|         this.eventNodes[eventId] = node; | ||||
|     }, | ||||
| 
 | ||||
|     _indexForEventId(evId) { | ||||
|         for (var i = 0; i < this.state.room.timeline.length; ++i) { | ||||
|             if (evId == this.state.room.timeline[i].getId()) { | ||||
|                 return i; | ||||
|             } | ||||
|         } | ||||
|         return null; | ||||
|     }, | ||||
| 
 | ||||
|     sendReadReceipt: function() { | ||||
|         if (!this.state.room) return; | ||||
|         var currentReadUpToEventId = this.state.room.getEventReadUpTo(MatrixClientPeg.get().credentials.userId); | ||||
|         var currentReadUpToEventIndex = this._indexForEventId(currentReadUpToEventId); | ||||
| 
 | ||||
|         var lastReadEventIndex = this._getLastDisplayedEventIndexIgnoringOwn(); | ||||
|         if (lastReadEventIndex === null) return; | ||||
| 
 | ||||
|         if (lastReadEventIndex > currentReadUpToEventIndex) { | ||||
|             MatrixClientPeg.get().sendReadReceipt(this.state.room.timeline[lastReadEventIndex]); | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     _getLastDisplayedEventIndexIgnoringOwn: function() { | ||||
|         if (this.eventNodes === undefined) return null; | ||||
| 
 | ||||
|         var messageWrapper = this.refs.messagePanel; | ||||
|         if (messageWrapper === undefined) return null; | ||||
|         var wrapperRect = ReactDOM.findDOMNode(messageWrapper).getBoundingClientRect(); | ||||
| 
 | ||||
|         for (var i = this.state.room.timeline.length-1; i >= 0; --i) { | ||||
|             var ev = this.state.room.timeline[i]; | ||||
| 
 | ||||
|             if (ev.sender && ev.sender.userId == MatrixClientPeg.get().credentials.userId) { | ||||
|                 continue; | ||||
|             } | ||||
| 
 | ||||
|             var node = this.eventNodes[ev.getId()]; | ||||
|             if (!node) continue; | ||||
| 
 | ||||
|             var boundingRect = node.getBoundingClientRect(); | ||||
| 
 | ||||
|             if (boundingRect.bottom < wrapperRect.bottom) { | ||||
|                 return i; | ||||
|             } | ||||
|         } | ||||
|         return null; | ||||
|     } | ||||
| }; | ||||
|  | @ -17,19 +17,17 @@ limitations under the License. | |||
| 'use strict'; | ||||
| 
 | ||||
| var React = require('react'); | ||||
| 
 | ||||
| var Notifier = require("matrix-react-sdk/lib/Notifier"); | ||||
| var sdk = require('matrix-react-sdk') | ||||
| 
 | ||||
| module.exports = React.createClass({ | ||||
|     displayName: 'MatrixToolbar', | ||||
| 
 | ||||
|     hideToolbar: function() { | ||||
|         var Notifier = sdk.getComponent('organisms.Notifier'); | ||||
|         Notifier.setToolbarHidden(true); | ||||
|     }, | ||||
| 
 | ||||
|     onClick: function() { | ||||
|         var Notifier = sdk.getComponent('organisms.Notifier'); | ||||
|         Notifier.setEnabled(true); | ||||
|     }, | ||||
| 
 | ||||
|  |  | |||
|  | @ -22,7 +22,7 @@ var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg'); | |||
| var dis = require('matrix-react-sdk/lib/dispatcher'); | ||||
| var sdk = require('matrix-react-sdk') | ||||
| var Modal = require('matrix-react-sdk/lib/Modal'); | ||||
| var Resend = require("../../../../Resend"); | ||||
| var Resend = require("matrix-react-sdk/lib/Resend"); | ||||
| 
 | ||||
| module.exports = React.createClass({ | ||||
|     displayName: 'MessageContextMenu', | ||||
|  | @ -46,7 +46,7 @@ module.exports = React.createClass({ | |||
|         ).done(function() { | ||||
|             // message should disappear by itself
 | ||||
|         }, function(e) { | ||||
|             var ErrorDialog = sdk.getComponent("organisms.ErrorDialog"); | ||||
|             var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); | ||||
|             // display error message stating you couldn't delete this.
 | ||||
|             var code = e.errcode || e.statusCode; | ||||
|             Modal.createDialog(ErrorDialog, { | ||||
|  |  | |||
|  | @ -1,173 +0,0 @@ | |||
| /* | ||||
| Copyright 2015 OpenMarket Ltd | ||||
| 
 | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
| 
 | ||||
|     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| 
 | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| 'use strict'; | ||||
| 
 | ||||
| var React = require('react'); | ||||
| 
 | ||||
| var CreateRoomController = require('matrix-react-sdk/lib/controllers/organisms/CreateRoom') | ||||
| 
 | ||||
| var sdk = require('matrix-react-sdk') | ||||
| 
 | ||||
| var PresetValues = { | ||||
|     PrivateChat: "private_chat", | ||||
|     PublicChat: "public_chat", | ||||
|     Custom: "custom", | ||||
| }; | ||||
| 
 | ||||
| module.exports = React.createClass({ | ||||
|     displayName: 'CreateRoom', | ||||
|     mixins: [CreateRoomController], | ||||
| 
 | ||||
|     getPreset: function() { | ||||
|         return this.refs.presets.getPreset(); | ||||
|     }, | ||||
| 
 | ||||
|     getName: function() { | ||||
|         return this.refs.name_textbox.getName(); | ||||
|     }, | ||||
| 
 | ||||
|     getTopic: function() { | ||||
|         return this.refs.topic.getTopic(); | ||||
|     }, | ||||
| 
 | ||||
|     getAliasLocalpart: function() { | ||||
|         return this.refs.alias.getAliasLocalpart(); | ||||
|     }, | ||||
| 
 | ||||
|     getInvitedUsers: function() { | ||||
|         return this.refs.user_selector.getUserIds(); | ||||
|     }, | ||||
| 
 | ||||
|     onPresetChanged: function(preset) { | ||||
|         switch (preset) { | ||||
|             case PresetValues.PrivateChat: | ||||
|                 this.setState({ | ||||
|                     preset: preset, | ||||
|                     is_private: true, | ||||
|                     share_history: false, | ||||
|                 }); | ||||
|                 break; | ||||
|             case PresetValues.PublicChat: | ||||
|                 this.setState({ | ||||
|                     preset: preset, | ||||
|                     is_private: false, | ||||
|                     share_history: true, | ||||
|                 }); | ||||
|                 break; | ||||
|             case PresetValues.Custom: | ||||
|                 this.setState({ | ||||
|                     preset: preset, | ||||
|                 }); | ||||
|                 break; | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     onPrivateChanged: function(ev) { | ||||
|         this.setState({ | ||||
|             preset: PresetValues.Custom, | ||||
|             is_private: ev.target.checked, | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     onShareHistoryChanged: function(ev) { | ||||
|         this.setState({ | ||||
|             preset: PresetValues.Custom, | ||||
|             share_history: ev.target.checked, | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     onTopicChange: function(ev) { | ||||
|         this.setState({ | ||||
|             topic: ev.target.value, | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     onNameChange: function(ev) { | ||||
|         this.setState({ | ||||
|             room_name: ev.target.value, | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     onInviteChanged: function(invited_users) { | ||||
|         this.setState({ | ||||
|             invited_users: invited_users, | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     onAliasChanged: function(alias) { | ||||
|         this.setState({ | ||||
|             alias: alias | ||||
|         }) | ||||
|     }, | ||||
| 
 | ||||
|     onEncryptChanged: function(ev) { | ||||
|         this.setState({ | ||||
|             encrypt: ev.target.checked, | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     render: function() { | ||||
|         var curr_phase = this.state.phase; | ||||
|         if (curr_phase == this.phases.CREATING) { | ||||
|             var Loader = sdk.getComponent("elements.Spinner"); | ||||
|             return ( | ||||
|                 <Loader/> | ||||
|             ); | ||||
|         } else { | ||||
|             var error_box = ""; | ||||
|             if (curr_phase == this.phases.ERROR) { | ||||
|                 error_box = ( | ||||
|                     <div className="mx_Error"> | ||||
|                         An error occured: {this.state.error_string} | ||||
|                     </div> | ||||
|                 ); | ||||
|             } | ||||
| 
 | ||||
|             var CreateRoomButton = sdk.getComponent("create_room.CreateRoomButton"); | ||||
|             var RoomAlias = sdk.getComponent("create_room.RoomAlias"); | ||||
|             var Presets = sdk.getComponent("create_room.Presets"); | ||||
|             var UserSelector = sdk.getComponent("elements.UserSelector"); | ||||
|             var RoomHeader = sdk.getComponent("rooms.RoomHeader"); | ||||
| 
 | ||||
|             return ( | ||||
|                 <div className="mx_CreateRoom"> | ||||
|                     <RoomHeader simpleHeader="Create room" /> | ||||
|                     <div className="mx_CreateRoom_body"> | ||||
|                         <input type="text" ref="room_name" value={this.state.room_name} onChange={this.onNameChange} placeholder="Name"/> <br /> | ||||
|                         <textarea className="mx_CreateRoom_description" ref="topic" value={this.state.topic} onChange={this.onTopicChange} placeholder="Topic"/> <br /> | ||||
|                         <RoomAlias ref="alias" alias={this.state.alias} onChange={this.onAliasChanged}/> <br /> | ||||
|                         <UserSelector ref="user_selector" selected_users={this.state.invited_users} onChange={this.onInviteChanged}/> <br /> | ||||
|                         <Presets ref="presets" onChange={this.onPresetChanged} preset={this.state.preset}/> <br /> | ||||
|                         <div> | ||||
|                             <label><input type="checkbox" ref="is_private" checked={this.state.is_private} onChange={this.onPrivateChanged}/> Make this room private</label> | ||||
|                         </div> | ||||
|                         <div> | ||||
|                             <label><input type="checkbox" ref="share_history" checked={this.state.share_history} onChange={this.onShareHistoryChanged}/> Share message history with new users</label> | ||||
|                         </div> | ||||
|                         <div className="mx_CreateRoom_encrypt"> | ||||
|                             <label><input type="checkbox" ref="encrypt" checked={this.state.encrypt} onChange={this.onEncryptChanged}/> Encrypt room</label> | ||||
|                         </div> | ||||
|                         <div> | ||||
|                             <CreateRoomButton onCreateRoom={this.onCreateRoom} /> <br /> | ||||
|                         </div> | ||||
|                         {error_box} | ||||
|                     </div> | ||||
|                 </div> | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
| }); | ||||
|  | @ -1,54 +0,0 @@ | |||
| /* | ||||
| Copyright 2015 OpenMarket Ltd | ||||
| 
 | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
| 
 | ||||
|     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| 
 | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| 'use strict'; | ||||
| 
 | ||||
| /* | ||||
|  * Usage: | ||||
|  * Modal.createDialog(ErrorDialog, { | ||||
|  *   title: "some text", (default: "Error") | ||||
|  *   description: "some more text", | ||||
|  *   button: "Button Text", | ||||
|  *   onClose: someFunction, | ||||
|  *   focus: true|false (default: true) | ||||
|  * }); | ||||
|  */ | ||||
| 
 | ||||
| var React = require('react'); | ||||
| var ErrorDialogController = require('matrix-react-sdk/lib/controllers/organisms/ErrorDialog') | ||||
| 
 | ||||
| module.exports = React.createClass({ | ||||
|     displayName: 'ErrorDialog', | ||||
|     mixins: [ErrorDialogController], | ||||
| 
 | ||||
|     render: function() { | ||||
|         return ( | ||||
|             <div className="mx_ErrorDialog"> | ||||
|                 <div className="mx_ErrorDialogTitle"> | ||||
|                     {this.props.title} | ||||
|                 </div> | ||||
|                 <div className="mx_Dialog_content"> | ||||
|                     {this.props.description} | ||||
|                 </div> | ||||
|                 <div className="mx_Dialog_buttons"> | ||||
|                     <button onClick={this.props.onFinished} autoFocus={this.props.focus}> | ||||
|                         {this.props.button} | ||||
|                     </button> | ||||
|                 </div> | ||||
|             </div> | ||||
|         ); | ||||
|     } | ||||
| }); | ||||
|  | @ -22,7 +22,7 @@ var HTML5Backend = require('react-dnd-html5-backend'); | |||
| var sdk = require('matrix-react-sdk') | ||||
| var dis = require('matrix-react-sdk/lib/dispatcher'); | ||||
| 
 | ||||
| var VectorConferenceHandler = require('../../../../modules/VectorConferenceHandler'); | ||||
| var VectorConferenceHandler = require('../../../../VectorConferenceHandler'); | ||||
| var CallHandler = require("matrix-react-sdk/lib/CallHandler"); | ||||
| 
 | ||||
| var LeftPanel = React.createClass({ | ||||
|  | @ -85,7 +85,7 @@ var LeftPanel = React.createClass({ | |||
|     }, | ||||
| 
 | ||||
|     render: function() { | ||||
|         var RoomList = sdk.getComponent('organisms.RoomList'); | ||||
|         var RoomList = sdk.getComponent('rooms.RoomList'); | ||||
|         var BottomLeftMenu = sdk.getComponent('molecules.BottomLeftMenu'); | ||||
|         var IncomingCallBox = sdk.getComponent('voip.IncomingCallBox'); | ||||
| 
 | ||||
|  | @ -114,7 +114,10 @@ var LeftPanel = React.createClass({ | |||
|                 { collapseButton } | ||||
|                 <IncomingCallBox /> | ||||
|                 { callPreview } | ||||
|                 <RoomList selectedRoom={this.props.selectedRoom} collapsed={this.props.collapsed}/> | ||||
|                 <RoomList | ||||
|                     selectedRoom={this.props.selectedRoom} | ||||
|                     collapsed={this.props.collapsed} | ||||
|                     ConferenceHandler={VectorConferenceHandler} /> | ||||
|                 <BottomLeftMenu collapsed={this.props.collapsed}/> | ||||
|             </aside> | ||||
|         ); | ||||
|  |  | |||
|  | @ -1,41 +0,0 @@ | |||
| /* | ||||
| Copyright 2015 OpenMarket Ltd | ||||
| 
 | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
| 
 | ||||
|     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| 
 | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| 'use strict'; | ||||
| 
 | ||||
| var React = require('react'); | ||||
| 
 | ||||
| var LogoutPromptController = require('matrix-react-sdk/lib/controllers/organisms/LogoutPrompt') | ||||
| 
 | ||||
| module.exports = React.createClass({ | ||||
|     displayName: 'LogoutPrompt', | ||||
|     mixins: [LogoutPromptController], | ||||
| 
 | ||||
|     render: function() { | ||||
|         return ( | ||||
|             <div> | ||||
|                 <div className="mx_Dialog_content"> | ||||
|                     Sign out? | ||||
|                 </div> | ||||
|                 <div className="mx_Dialog_buttons"> | ||||
|                     <button onClick={this.logOut}>Sign Out</button> | ||||
|                     <button onClick={this.cancelPrompt}>Cancel</button> | ||||
|                 </div> | ||||
|             </div> | ||||
|         ); | ||||
|     }, | ||||
| }); | ||||
| 
 | ||||
|  | @ -1,121 +0,0 @@ | |||
| /* | ||||
| Copyright 2015 OpenMarket Ltd | ||||
| 
 | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
| 
 | ||||
|     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| 
 | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| 'use strict'; | ||||
| 
 | ||||
| var React = require('react'); | ||||
| var classNames = require('classnames'); | ||||
| 
 | ||||
| var MemberListController = require('matrix-react-sdk/lib/controllers/organisms/MemberList') | ||||
| var GeminiScrollbar = require('react-gemini-scrollbar'); | ||||
| 
 | ||||
| var sdk = require('matrix-react-sdk') | ||||
| 
 | ||||
| 
 | ||||
| module.exports = React.createClass({ | ||||
|     displayName: 'MemberList', | ||||
|     mixins: [MemberListController], | ||||
| 
 | ||||
|     getInitialState: function() { | ||||
|     }, | ||||
| 
 | ||||
|     memberSort: function(userIdA, userIdB) { | ||||
|         var userA = this.memberDict[userIdA].user; | ||||
|         var userB = this.memberDict[userIdB].user; | ||||
| 
 | ||||
|         var presenceMap = { | ||||
|             online: 3, | ||||
|             unavailable: 2, | ||||
|             offline: 1 | ||||
|         }; | ||||
| 
 | ||||
|         var presenceOrdA = userA ? presenceMap[userA.presence] : 0; | ||||
|         var presenceOrdB = userB ? presenceMap[userB.presence] : 0; | ||||
| 
 | ||||
|         if (presenceOrdA != presenceOrdB) { | ||||
|             return presenceOrdB - presenceOrdA; | ||||
|         } | ||||
| 
 | ||||
|         var latA = userA ? (userA.lastPresenceTs - (userA.lastActiveAgo || userA.lastPresenceTs)) : 0; | ||||
|         var latB = userB ? (userB.lastPresenceTs - (userB.lastActiveAgo || userB.lastPresenceTs)) : 0; | ||||
| 
 | ||||
|         return latB - latA; | ||||
|     }, | ||||
| 
 | ||||
|     makeMemberTiles: function(membership) { | ||||
|         var MemberTile = sdk.getComponent("rooms.MemberTile"); | ||||
| 
 | ||||
|         var self = this; | ||||
|         return self.state.members.filter(function(userId) { | ||||
|             var m = self.memberDict[userId]; | ||||
|             return m.membership == membership; | ||||
|         }).map(function(userId) { | ||||
|             var m = self.memberDict[userId]; | ||||
|             return ( | ||||
|                 <MemberTile key={userId} member={m} ref={userId} /> | ||||
|             ); | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     onPopulateInvite: function(e) { | ||||
|         this.onInvite(this.refs.invite.value); | ||||
|         e.preventDefault(); | ||||
|     }, | ||||
| 
 | ||||
|     inviteTile: function() { | ||||
|         if (this.state.inviting) { | ||||
|             var Loader = sdk.getComponent("elements.Spinner"); | ||||
|             return ( | ||||
|                 <Loader /> | ||||
|             ); | ||||
|         } else { | ||||
|             return ( | ||||
|                 <form onSubmit={this.onPopulateInvite}> | ||||
|                     <input className="mx_MemberList_invite" ref="invite" placeholder="Invite another user"/> | ||||
|                 </form> | ||||
|             ); | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     render: function() { | ||||
|         var invitedSection = null; | ||||
|         var invitedMemberTiles = this.makeMemberTiles('invite'); | ||||
|         if (invitedMemberTiles.length > 0) { | ||||
|             invitedSection = ( | ||||
|                 <div className="mx_MemberList_invited"> | ||||
|                     <h2>Invited</h2> | ||||
|                     <div className="mx_MemberList_wrapper"> | ||||
|                         {invitedMemberTiles} | ||||
|                     </div> | ||||
|                 </div> | ||||
|             ); | ||||
|         } | ||||
|         return ( | ||||
|             <div className="mx_MemberList"> | ||||
|                 <GeminiScrollbar autoshow={true} className="mx_MemberList_border"> | ||||
|                     {this.inviteTile()} | ||||
|                     <div> | ||||
|                         <div className="mx_MemberList_wrapper"> | ||||
|                             {this.makeMemberTiles('join')} | ||||
|                         </div> | ||||
|                     </div> | ||||
|                     {invitedSection} | ||||
|                 </GeminiScrollbar> | ||||
|             </div> | ||||
|         ); | ||||
|     } | ||||
| }); | ||||
| 
 | ||||
|  | @ -1,66 +0,0 @@ | |||
| /* | ||||
| Copyright 2015 OpenMarket Ltd | ||||
| 
 | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
| 
 | ||||
|     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| 
 | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| 'use strict'; | ||||
| 
 | ||||
| /* | ||||
|  * Usage: | ||||
|  * Modal.createDialog(ErrorDialog, { | ||||
|  *   title: "some text", (default: "Error") | ||||
|  *   description: "some more text", | ||||
|  *   button: "Button Text", | ||||
|  *   onClose: someFunction, | ||||
|  *   focus: true|false (default: true) | ||||
|  * }); | ||||
|  */ | ||||
| 
 | ||||
| var React = require('react'); | ||||
| var QuestionDialogController = require('matrix-react-sdk/lib/controllers/organisms/QuestionDialog') | ||||
| 
 | ||||
| module.exports = React.createClass({ | ||||
|     displayName: 'QuestionDialog', | ||||
|     mixins: [QuestionDialogController], | ||||
| 
 | ||||
|     onOk: function() { | ||||
|         this.props.onFinished(true); | ||||
|     }, | ||||
| 
 | ||||
|     onCancel: function() { | ||||
|         this.props.onFinished(false); | ||||
|     }, | ||||
| 
 | ||||
|     render: function() { | ||||
|         return ( | ||||
|             <div className="mx_QuestionDialog"> | ||||
|                 <div className="mx_QuestionDialogTitle"> | ||||
|                     {this.props.title} | ||||
|                 </div> | ||||
|                 <div className="mx_Dialog_content"> | ||||
|                     {this.props.description} | ||||
|                 </div> | ||||
|                 <div className="mx_Dialog_buttons"> | ||||
|                     <button onClick={this.onOk} autoFocus={this.props.focus}> | ||||
|                         {this.props.button} | ||||
|                     </button> | ||||
| 
 | ||||
|                     <button onClick={this.onCancel}> | ||||
|                         Cancel | ||||
|                     </button> | ||||
|                 </div> | ||||
|             </div> | ||||
|         ); | ||||
|     } | ||||
| }); | ||||
|  | @ -94,7 +94,7 @@ module.exports = React.createClass({ | |||
|     }, | ||||
| 
 | ||||
|     render: function() { | ||||
|         var MemberList = sdk.getComponent('organisms.MemberList'); | ||||
|         var MemberList = sdk.getComponent('rooms.MemberList'); | ||||
|         var buttonGroup; | ||||
|         var panel; | ||||
| 
 | ||||
|  |  | |||
|  | @ -40,7 +40,7 @@ module.exports = React.createClass({ | |||
|             if (err) { | ||||
|                 self.setState({ loading: false });                 | ||||
|                 console.error("Failed to get publicRooms: %s", JSON.stringify(err)); | ||||
|                 var ErrorDialog = sdk.getComponent("organisms.ErrorDialog"); | ||||
|                 var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); | ||||
|                 Modal.createDialog(ErrorDialog, { | ||||
|                     title: "Failed to get public room list", | ||||
|                     description: err.message | ||||
|  | @ -67,7 +67,7 @@ module.exports = React.createClass({ | |||
|             }); | ||||
|         }, function(err) { | ||||
|             console.error("Failed to join room: %s", JSON.stringify(err)); | ||||
|             var ErrorDialog = sdk.getComponent("organisms.ErrorDialog"); | ||||
|             var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); | ||||
|             Modal.createDialog(ErrorDialog, { | ||||
|                 title: "Failed to join room", | ||||
|                 description: err.message | ||||
|  |  | |||
|  | @ -1,116 +0,0 @@ | |||
| /* | ||||
| Copyright 2015 OpenMarket Ltd | ||||
| 
 | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
| 
 | ||||
|     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| 
 | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| 'use strict'; | ||||
| 
 | ||||
| var React = require('react'); | ||||
| var sdk = require('matrix-react-sdk') | ||||
| var dis = require('matrix-react-sdk/lib/dispatcher'); | ||||
| 
 | ||||
| var GeminiScrollbar = require('react-gemini-scrollbar'); | ||||
| var RoomListController = require('../../../../controllers/organisms/RoomList') | ||||
| 
 | ||||
| module.exports = React.createClass({ | ||||
|     displayName: 'RoomList', | ||||
|     mixins: [RoomListController], | ||||
| 
 | ||||
|     onShowClick: function() { | ||||
|         dis.dispatch({ | ||||
|             action: 'show_left_panel', | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     render: function() { | ||||
|         var expandButton = this.props.collapsed ?  | ||||
|                            <img className="mx_RoomList_expandButton" onClick={ this.onShowClick } src="img/menu.png" width="20" alt=">"/> : | ||||
|                            null; | ||||
| 
 | ||||
|         var RoomSubList = sdk.getComponent('organisms.RoomSubList'); | ||||
|         var self = this; | ||||
| 
 | ||||
|         return ( | ||||
|             <GeminiScrollbar className="mx_RoomList_scrollbar" autoshow={true} onScroll={self._repositionTooltip}> | ||||
|             <div className="mx_RoomList"> | ||||
|                 { expandButton } | ||||
| 
 | ||||
|                 <RoomSubList list={ self.state.lists['m.invite'] } | ||||
|                              label="Invites" | ||||
|                              editable={ false } | ||||
|                              order="recent" | ||||
|                              activityMap={ self.state.activityMap } | ||||
|                              selectedRoom={ self.props.selectedRoom } | ||||
|                              collapsed={ self.props.collapsed } /> | ||||
| 
 | ||||
|                 <RoomSubList list={ self.state.lists['m.favourite'] } | ||||
|                              label="Favourites" | ||||
|                              tagName="m.favourite" | ||||
|                              verb="favourite" | ||||
|                              editable={ true } | ||||
|                              order="manual" | ||||
|                              activityMap={ self.state.activityMap } | ||||
|                              selectedRoom={ self.props.selectedRoom } | ||||
|                              collapsed={ self.props.collapsed } /> | ||||
| 
 | ||||
|                 <RoomSubList list={ self.state.lists['m.recent'] } | ||||
|                              label="Conversations" | ||||
|                              editable={ true } | ||||
|                              verb="restore" | ||||
|                              order="recent" | ||||
|                              activityMap={ self.state.activityMap } | ||||
|                              selectedRoom={ self.props.selectedRoom } | ||||
|                              collapsed={ self.props.collapsed } /> | ||||
| 
 | ||||
|                 { Object.keys(self.state.lists).map(function(tagName) { | ||||
|                     if (!tagName.match(/^m\.(invite|favourite|recent|lowpriority|archived)$/)) { | ||||
|                         return <RoomSubList list={ self.state.lists[tagName] } | ||||
|                              key={ tagName } | ||||
|                              label={ tagName } | ||||
|                              tagName={ tagName } | ||||
|                              verb={ "tag as " + tagName } | ||||
|                              editable={ true } | ||||
|                              order="manual" | ||||
|                              activityMap={ self.state.activityMap } | ||||
|                              selectedRoom={ self.props.selectedRoom } | ||||
|                              collapsed={ self.props.collapsed } /> | ||||
| 
 | ||||
|                     } | ||||
|                 }) } | ||||
| 
 | ||||
|                 <RoomSubList list={ self.state.lists['m.lowpriority'] } | ||||
|                              label="Low priority" | ||||
|                              tagName="m.lowpriority" | ||||
|                              verb="demote" | ||||
|                              editable={ true } | ||||
|                              order="recent" | ||||
|                              bottommost={ self.state.lists['m.archived'].length === 0 } | ||||
|                              activityMap={ self.state.activityMap } | ||||
|                              selectedRoom={ self.props.selectedRoom } | ||||
|                              collapsed={ self.props.collapsed } /> | ||||
| 
 | ||||
|                 <RoomSubList list={ self.state.lists['m.archived'] } | ||||
|                              label="Historical" | ||||
|                              editable={ false } | ||||
|                              order="recent" | ||||
|                              bottommost={ true } | ||||
|                              activityMap={ self.state.activityMap } | ||||
|                              selectedRoom={ self.props.selectedRoom } | ||||
|                              collapsed={ self.props.collapsed } /> | ||||
|             </div> | ||||
|             </GeminiScrollbar> | ||||
|         ); | ||||
|     } | ||||
| }); | ||||
| 
 | ||||
|  | @ -1,330 +0,0 @@ | |||
| /* | ||||
| Copyright 2015 OpenMarket Ltd | ||||
| 
 | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
| 
 | ||||
|     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| 
 | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| 'use strict'; | ||||
| 
 | ||||
| var React = require('react'); | ||||
| var ReactDOM = require('react-dom'); | ||||
| 
 | ||||
| var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg'); | ||||
| var dis = require('matrix-react-sdk/lib/dispatcher'); | ||||
| 
 | ||||
| var sdk = require('matrix-react-sdk') | ||||
| var classNames = require("classnames"); | ||||
| var filesize = require('filesize'); | ||||
| 
 | ||||
| var GeminiScrollbar = require('react-gemini-scrollbar'); | ||||
| var RoomViewController = require('../../../../controllers/organisms/RoomView') | ||||
| var VectorConferenceHandler = require('../../../../modules/VectorConferenceHandler'); | ||||
| 
 | ||||
| module.exports = React.createClass({ | ||||
|     displayName: 'RoomView', | ||||
|     mixins: [RoomViewController], | ||||
| 
 | ||||
|     onSettingsClick: function() { | ||||
|         this.setState({editingRoomSettings: true}); | ||||
|     }, | ||||
| 
 | ||||
|     onSaveClick: function() { | ||||
|         this.setState({ | ||||
|             editingRoomSettings: false, | ||||
|             uploadingRoomSettings: true, | ||||
|         }); | ||||
| 
 | ||||
|         var new_name = this.refs.header.getRoomName(); | ||||
|         var new_topic = this.refs.room_settings.getTopic(); | ||||
|         var new_join_rule = this.refs.room_settings.getJoinRules(); | ||||
|         var new_history_visibility = this.refs.room_settings.getHistoryVisibility(); | ||||
|         var new_power_levels = this.refs.room_settings.getPowerLevels(); | ||||
| 
 | ||||
|         this.uploadNewState( | ||||
|             new_name, | ||||
|             new_topic, | ||||
|             new_join_rule, | ||||
|             new_history_visibility, | ||||
|             new_power_levels | ||||
|         ); | ||||
|     }, | ||||
| 
 | ||||
|     onCancelClick: function() { | ||||
|         this.setState(this.getInitialState()); | ||||
|     }, | ||||
| 
 | ||||
|     onRejectButtonClicked: function(ev) { | ||||
|         var self = this; | ||||
|         this.setState({ | ||||
|             rejecting: true | ||||
|         }); | ||||
|         MatrixClientPeg.get().leave(this.props.roomId).done(function() { | ||||
|             dis.dispatch({ action: 'view_next_room' }); | ||||
|             self.setState({ | ||||
|                 rejecting: false | ||||
|             }); | ||||
|         }, function(err) { | ||||
|             console.error("Failed to reject invite: %s", err); | ||||
|             self.setState({ | ||||
|                 rejecting: false, | ||||
|                 rejectError: err | ||||
|             }); | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     onSearchClick: function() { | ||||
|         this.setState({ searching: true }); | ||||
|     }, | ||||
| 
 | ||||
|     onConferenceNotificationClick: function() { | ||||
|         dis.dispatch({ | ||||
|             action: 'place_call', | ||||
|             type: "video", | ||||
|             room_id: this.props.roomId | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     getUnreadMessagesString: function() { | ||||
|         if (!this.state.numUnreadMessages) { | ||||
|             return ""; | ||||
|         } | ||||
|         return this.state.numUnreadMessages + " new message" + (this.state.numUnreadMessages > 1 ? "s" : ""); | ||||
|     }, | ||||
| 
 | ||||
|     scrollToBottom: function() { | ||||
|         var scrollNode = this._getScrollNode(); | ||||
|         if (!scrollNode) return; | ||||
|         scrollNode.scrollTop = scrollNode.scrollHeight; | ||||
|     }, | ||||
| 
 | ||||
|     render: function() { | ||||
|         var RoomHeader = sdk.getComponent('rooms.RoomHeader'); | ||||
|         var MessageComposer = sdk.getComponent('rooms.MessageComposer'); | ||||
|         var CallView = sdk.getComponent("voip.CallView"); | ||||
|         var RoomSettings = sdk.getComponent("rooms.RoomSettings"); | ||||
|         var SearchBar = sdk.getComponent("molecules.SearchBar"); | ||||
| 
 | ||||
|         if (!this.state.room) { | ||||
|             if (this.props.roomId) { | ||||
|                 return ( | ||||
|                     <div> | ||||
|                     <button onClick={this.onJoinButtonClicked}>Join Room</button> | ||||
|                     </div> | ||||
|                 ); | ||||
|             } else { | ||||
|                 return ( | ||||
|                     <div /> | ||||
|                 ); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         var myUserId = MatrixClientPeg.get().credentials.userId; | ||||
|         if (this.state.room.currentState.members[myUserId].membership == 'invite') { | ||||
|             if (this.state.joining || this.state.rejecting) { | ||||
|                 var Loader = sdk.getComponent("elements.Spinner"); | ||||
|                 return ( | ||||
|                     <div className="mx_RoomView"> | ||||
|                         <Loader /> | ||||
|                     </div> | ||||
|                 ); | ||||
|             } else { | ||||
|                 var inviteEvent = this.state.room.currentState.members[myUserId].events.member.event; | ||||
|                 // XXX: Leaving this intentionally basic for now because invites are about to change totally
 | ||||
|                 var joinErrorText = this.state.joinError ? "Failed to join room!" : ""; | ||||
|                 var rejectErrorText = this.state.rejectError ? "Failed to reject invite!" : ""; | ||||
|                 return ( | ||||
|                     <div className="mx_RoomView"> | ||||
|                         <RoomHeader ref="header" room={this.state.room} simpleHeader="Room invite"/> | ||||
|                         <div className="mx_RoomView_invitePrompt"> | ||||
|                             <div>{inviteEvent.user_id} has invited you to a room</div> | ||||
|                             <br/> | ||||
|                             <button ref="joinButton" onClick={this.onJoinButtonClicked}>Join</button> | ||||
|                             <button onClick={this.onRejectButtonClicked}>Reject</button> | ||||
|                             <div className="error">{joinErrorText}</div> | ||||
|                             <div className="error">{rejectErrorText}</div> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 ); | ||||
|             } | ||||
|         } else { | ||||
|             var scrollheader_classes = classNames({ | ||||
|                 mx_RoomView_scrollheader: true, | ||||
|                 loading: this.state.paginating | ||||
|             }); | ||||
| 
 | ||||
|             var statusBar = ( | ||||
|                 <div /> | ||||
|             ); | ||||
| 
 | ||||
|             // for testing UI...
 | ||||
|             // this.state.upload = {
 | ||||
|             //     uploadedBytes: 123493,
 | ||||
|             //     totalBytes: 347534,
 | ||||
|             //     fileName: "testing_fooble.jpg",
 | ||||
|             // }
 | ||||
| 
 | ||||
|             if (this.state.upload) { | ||||
|                 var innerProgressStyle = { | ||||
|                     width: ((this.state.upload.uploadedBytes / this.state.upload.totalBytes) * 100) + '%' | ||||
|                 }; | ||||
|                 var uploadedSize = filesize(this.state.upload.uploadedBytes); | ||||
|                 var totalSize = filesize(this.state.upload.totalBytes); | ||||
|                 if (uploadedSize.replace(/^.* /,'') === totalSize.replace(/^.* /,'')) { | ||||
|                     uploadedSize = uploadedSize.replace(/ .*/, ''); | ||||
|                 } | ||||
|                 statusBar = ( | ||||
|                     <div className="mx_RoomView_uploadBar"> | ||||
|                         <div className="mx_RoomView_uploadProgressOuter"> | ||||
|                             <div className="mx_RoomView_uploadProgressInner" style={innerProgressStyle}></div> | ||||
|                         </div> | ||||
|                         <img className="mx_RoomView_uploadIcon" src="img/fileicon.png" width="17" height="22"/> | ||||
|                         <img className="mx_RoomView_uploadCancel" src="img/cancel.png" width="18" height="18"/> | ||||
|                         <div className="mx_RoomView_uploadBytes"> | ||||
|                             { uploadedSize } / { totalSize } | ||||
|                         </div> | ||||
|                         <div className="mx_RoomView_uploadFilename">Uploading {this.state.upload.fileName}</div> | ||||
|                     </div> | ||||
|                 ); | ||||
|             } else { | ||||
|                 var typingString = this.getWhoIsTypingString(); | ||||
|                 //typingString = "Testing typing...";
 | ||||
|                 var unreadMsgs = this.getUnreadMessagesString(); | ||||
|                 // no conn bar trumps unread count since you can't get unread messages
 | ||||
|                 // without a connection! (technically may already have some but meh)
 | ||||
|                 // It also trumps the "some not sent" msg since you can't resend without
 | ||||
|                 // a connection!
 | ||||
|                 if (this.state.syncState === "ERROR") { | ||||
|                     statusBar = ( | ||||
|                         <div className="mx_RoomView_connectionLostBar"> | ||||
|                             <img src="img/warning2.png" width="30" height="30" alt="/!\ "/> | ||||
|                             <div className="mx_RoomView_connectionLostBar_textArea"> | ||||
|                                 <div className="mx_RoomView_connectionLostBar_title"> | ||||
|                                     Connectivity to the server has been lost. | ||||
|                                 </div> | ||||
|                                 <div className="mx_RoomView_connectionLostBar_desc"> | ||||
|                                     Sent messages will be stored until your connection has returned. | ||||
|                                 </div> | ||||
|                             </div> | ||||
|                         </div> | ||||
|                     ); | ||||
|                 } | ||||
|                 else if (this.state.hasUnsentMessages) { | ||||
|                     statusBar = ( | ||||
|                         <div className="mx_RoomView_connectionLostBar"> | ||||
|                             <img src="img/warning2.png" width="30" height="30" alt="/!\ "/> | ||||
|                             <div className="mx_RoomView_connectionLostBar_textArea"> | ||||
|                                 <div className="mx_RoomView_connectionLostBar_title"> | ||||
|                                     Some of your messages have not been sent. | ||||
|                                 </div> | ||||
|                                 <div className="mx_RoomView_connectionLostBar_desc"> | ||||
|                                     <a className="mx_RoomView_resend_link" | ||||
|                                         onClick={ this.onResendAllClick }> | ||||
|                                     Resend all now | ||||
|                                     </a> or select individual messages to re-send. | ||||
|                                 </div> | ||||
|                             </div> | ||||
|                         </div> | ||||
|                     ); | ||||
|                 } | ||||
|                 // unread count trumps who is typing since the unread count is only
 | ||||
|                 // set when you've scrolled up
 | ||||
|                 else if (unreadMsgs) { | ||||
|                     statusBar = ( | ||||
|                         <div className="mx_RoomView_unreadMessagesBar" onClick={ this.scrollToBottom }> | ||||
|                             <img src="img/newmessages.png" width="24" height="24" alt=""/> | ||||
|                             {unreadMsgs} | ||||
|                         </div> | ||||
|                     ); | ||||
|                 } | ||||
|                 else if (typingString) { | ||||
|                     statusBar = ( | ||||
|                         <div className="mx_RoomView_typingBar"> | ||||
|                             <div className="mx_RoomView_typingImage">...</div> | ||||
|                             {typingString} | ||||
|                         </div> | ||||
|                     ); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             var aux = null; | ||||
|             if (this.state.editingRoomSettings) { | ||||
|                 aux = <RoomSettings ref="room_settings" onSaveClick={this.onSaveClick} room={this.state.room} />; | ||||
|             } | ||||
|             else if (this.state.uploadingRoomSettings) { | ||||
|                 var Loader = sdk.getComponent("elements.Spinner");                 | ||||
|                 aux = <Loader/>; | ||||
|             } | ||||
|             else if (this.state.searching) { | ||||
|                 aux = <SearchBar ref="search_bar" onCancelClick={this.onCancelClick} onSearch={this.onSearch}/>; | ||||
|             } | ||||
| 
 | ||||
|             var conferenceCallNotification = null; | ||||
|             if (this.state.displayConfCallNotification) { | ||||
|                 var supportedText; | ||||
|                 if (!MatrixClientPeg.get().supportsVoip()) { | ||||
|                     supportedText = " (unsupported)"; | ||||
|                 } | ||||
|                 conferenceCallNotification = ( | ||||
|                     <div className="mx_RoomView_ongoingConfCallNotification" onClick={this.onConferenceNotificationClick}> | ||||
|                         Ongoing conference call {supportedText} | ||||
|                     </div> | ||||
|                 ); | ||||
|             } | ||||
| 
 | ||||
|             var fileDropTarget = null; | ||||
|             if (this.state.draggingFile) { | ||||
|                 fileDropTarget = <div className="mx_RoomView_fileDropTarget"> | ||||
|                                     <div className="mx_RoomView_fileDropTargetLabel"> | ||||
|                                         <img src="img/upload-big.png" width="43" height="57" alt="Drop File Here"/><br/> | ||||
|                                         Drop File Here | ||||
|                                     </div> | ||||
|                                  </div>; | ||||
|             } | ||||
| 
 | ||||
|             var messageComposer; | ||||
|             if (!this.state.searchResults) { | ||||
|                 messageComposer = | ||||
|                     <MessageComposer room={this.state.room} roomView={this} uploadFile={this.uploadFile} /> | ||||
|             } | ||||
| 
 | ||||
|             return ( | ||||
|                 <div className="mx_RoomView"> | ||||
|                     <RoomHeader ref="header" room={this.state.room} editing={this.state.editingRoomSettings} onSearchClick={this.onSearchClick} | ||||
|                         onSettingsClick={this.onSettingsClick} onSaveClick={this.onSaveClick} onCancelClick={this.onCancelClick} /> | ||||
|                     <div className="mx_RoomView_auxPanel"> | ||||
|                         <CallView room={this.state.room} ConferenceHandler={VectorConferenceHandler}/> | ||||
|                         { conferenceCallNotification } | ||||
|                         { aux } | ||||
|                     </div> | ||||
|                     <GeminiScrollbar autoshow={true} ref="messagePanel" className="mx_RoomView_messagePanel" onScroll={ this.onMessageListScroll }> | ||||
|                         <div className="mx_RoomView_messageListWrapper"> | ||||
|                             { fileDropTarget }     | ||||
|                             <ol className="mx_RoomView_MessageList" aria-live="polite"> | ||||
|                                 <li className={scrollheader_classes}> | ||||
|                                 </li> | ||||
|                                 {this.getEventTiles()} | ||||
|                             </ol> | ||||
|                         </div> | ||||
|                     </GeminiScrollbar> | ||||
|                     <div className="mx_RoomView_statusArea"> | ||||
|                         <div className="mx_RoomView_statusAreaBox"> | ||||
|                             <div className="mx_RoomView_statusAreaBox_line"></div> | ||||
|                             { this.state.searchResults ? null : statusBar } | ||||
|                         </div> | ||||
|                     </div> | ||||
|                     { messageComposer } | ||||
|                 </div> | ||||
|             ); | ||||
|         } | ||||
|     }, | ||||
| }); | ||||
|  | @ -1,124 +0,0 @@ | |||
| /* | ||||
| Copyright 2015 OpenMarket Ltd | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
|     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| 'use strict'; | ||||
| 
 | ||||
| var React = require('react'); | ||||
| var sdk = require('matrix-react-sdk') | ||||
| var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg'); | ||||
| 
 | ||||
| var UserSettingsController = require('matrix-react-sdk/lib/controllers/organisms/UserSettings') | ||||
| 
 | ||||
| var Modal = require('matrix-react-sdk/lib/Modal'); | ||||
| 
 | ||||
| module.exports = React.createClass({ | ||||
|     displayName: 'UserSettings', | ||||
|     mixins: [UserSettingsController], | ||||
| 
 | ||||
|     editAvatar: function() { | ||||
|         var url = MatrixClientPeg.get().mxcUrlToHttp(this.state.avatarUrl); | ||||
|         var ChangeAvatar = sdk.getComponent('settings.ChangeAvatar'); | ||||
|         var avatarDialog = ( | ||||
|             <div> | ||||
|                 <ChangeAvatar initialAvatarUrl={url} /> | ||||
|                 <div className="mx_Dialog_buttons"> | ||||
|                     <button onClick={this.onAvatarDialogCancel}>Cancel</button> | ||||
|                 </div> | ||||
|             </div> | ||||
|         ); | ||||
|         this.avatarDialog = Modal.createDialogWithElement(avatarDialog); | ||||
|     }, | ||||
| 
 | ||||
|     addEmail: function() { | ||||
| 
 | ||||
|     }, | ||||
| 
 | ||||
|     editDisplayName: function() { | ||||
|         this.refs.displayname.edit(); | ||||
|     }, | ||||
| 
 | ||||
|     changePassword: function() { | ||||
|         var ChangePassword = sdk.getComponent('settings.ChangePassword'); | ||||
|         Modal.createDialog(ChangePassword); | ||||
|     }, | ||||
| 
 | ||||
|     onLogoutClicked: function(ev) { | ||||
|         var LogoutPrompt = sdk.getComponent('organisms.LogoutPrompt'); | ||||
|         this.logoutModal = Modal.createDialog(LogoutPrompt, {onCancel: this.onLogoutPromptCancel}); | ||||
|     }, | ||||
| 
 | ||||
|     onLogoutPromptCancel: function() { | ||||
|         this.logoutModal.closeDialog(); | ||||
|     }, | ||||
| 
 | ||||
|     onAvatarDialogCancel: function() { | ||||
|         this.avatarDialog.close(); | ||||
|     }, | ||||
| 
 | ||||
|     render: function() { | ||||
|         var Loader = sdk.getComponent("elements.Spinner");         | ||||
|         switch (this.state.phase) { | ||||
|             case this.Phases.Loading: | ||||
|                 return <Loader /> | ||||
|             case this.Phases.Display: | ||||
|                 var ChangeDisplayName = sdk.getComponent('settings.ChangeDisplayName'); | ||||
|                 var EnableNotificationsButton = sdk.getComponent('settings.EnableNotificationsButton'); | ||||
|                 return ( | ||||
|                     <div className="mx_UserSettings"> | ||||
|                         <div className="mx_UserSettings_User"> | ||||
|                             <h1>User Settings</h1> | ||||
|                             <hr/> | ||||
|                             <div className="mx_UserSettings_User_Inner"> | ||||
|                                 <div className="mx_UserSettings_Avatar"> | ||||
|                                     <div className="mx_UserSettings_Avatar_Text">Profile Photo</div> | ||||
|                                     <div className="mx_UserSettings_Avatar_Edit" onClick={this.editAvatar}>Edit</div> | ||||
|                                 </div> | ||||
| 
 | ||||
|                                 <div className="mx_UserSettings_DisplayName"> | ||||
|                                     <ChangeDisplayName ref="displayname" /> | ||||
|                                     <div className="mx_UserSettings_DisplayName_Edit" onClick={this.editDisplayName}>Edit</div> | ||||
|                                 </div> | ||||
| 
 | ||||
|                                 <div className="mx_UserSettings_3pids"> | ||||
|                                     {this.state.threepids.map(function(val) { | ||||
|                                         return <div key={val.address}>{val.address}</div>; | ||||
|                                     })} | ||||
|                                 </div> | ||||
| 
 | ||||
|                                 <div className="mx_UserSettings_Add3pid" onClick={this.addEmail}>Add email</div> | ||||
|                             </div> | ||||
|                         </div> | ||||
| 
 | ||||
|                         <div className="mx_UserSettings_Global"> | ||||
|                             <h1>Global Settings</h1> | ||||
|                             <hr/> | ||||
|                             <div className="mx_UserSettings_Global_Inner"> | ||||
|                                 <div className="mx_UserSettings_ChangePassword" onClick={this.changePassword}> | ||||
|                                     Change Password | ||||
|                                 </div> | ||||
|                                 <div className="mx_UserSettings_ClientVersion"> | ||||
|                                     Version {this.state.clientVersion} | ||||
|                                 </div> | ||||
|                                 <div className="mx_UserSettings_EnableNotifications"> | ||||
|                                     <EnableNotificationsButton /> | ||||
|                                 </div> | ||||
|                                 <div className="mx_UserSettings_Logout"> | ||||
|                                     <button onClick={this.onLogoutClicked}>Sign Out</button> | ||||
|                                 </div> | ||||
|                             </div> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 ); | ||||
|         } | ||||
|     } | ||||
| }); | ||||
|  | @ -1,223 +0,0 @@ | |||
| /* | ||||
| Copyright 2015 OpenMarket Ltd | ||||
| 
 | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
| 
 | ||||
|     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| 
 | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| 'use strict'; | ||||
| 
 | ||||
| var React = require('react'); | ||||
| var sdk = require('matrix-react-sdk') | ||||
| 
 | ||||
| var MatrixChatController = require('matrix-react-sdk/lib/controllers/pages/MatrixChat') | ||||
| 
 | ||||
| var dis = require('matrix-react-sdk/lib/dispatcher'); | ||||
| var Matrix = require("matrix-js-sdk"); | ||||
| 
 | ||||
| var ContextualMenu = require("matrix-react-sdk/lib/ContextualMenu"); | ||||
| var Login = require("../../../../components/structures/login/Login"); | ||||
| var Registration = require("../../../../components/structures/login/Registration"); | ||||
| var PostRegistration = require("../../../../components/structures/login/PostRegistration"); | ||||
| var config = require("../../../../../config.json"); | ||||
| 
 | ||||
| module.exports = React.createClass({ | ||||
|     displayName: 'MatrixChat', | ||||
|     mixins: [MatrixChatController], | ||||
| 
 | ||||
|     getInitialState: function() { | ||||
|         return { | ||||
|             width: 10000, | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     componentDidMount: function() { | ||||
|         window.addEventListener('resize', this.handleResize); | ||||
|         this.handleResize(); | ||||
|     }, | ||||
| 
 | ||||
|     componentWillUnmount: function() { | ||||
|         window.removeEventListener('resize', this.handleResize); | ||||
|     }, | ||||
| 
 | ||||
|     onAliasClick: function(event, alias) { | ||||
|         event.preventDefault(); | ||||
|         dis.dispatch({action: 'view_room_alias', room_alias: alias}); | ||||
|     }, | ||||
| 
 | ||||
|     onUserClick: function(event, userId) { | ||||
|         event.preventDefault(); | ||||
|         var MemberInfo = sdk.getComponent('rooms.MemberInfo'); | ||||
|         var member = new Matrix.RoomMember(null, userId); | ||||
|         ContextualMenu.createMenu(MemberInfo, { | ||||
|             member: member, | ||||
|             right: window.innerWidth - event.pageX, | ||||
|             top: event.pageY | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     onLogoutClick: function(event) { | ||||
|         dis.dispatch({ | ||||
|             action: 'logout' | ||||
|         }); | ||||
|         event.stopPropagation(); | ||||
|         event.preventDefault(); | ||||
|     }, | ||||
| 
 | ||||
|     handleResize: function(e) { | ||||
|         var hideLhsThreshold = 1000; | ||||
|         var showLhsThreshold = 1000; | ||||
|         var hideRhsThreshold = 820; | ||||
|         var showRhsThreshold = 820; | ||||
| 
 | ||||
|         if (this.state.width > hideLhsThreshold && window.innerWidth <= hideLhsThreshold) { | ||||
|             dis.dispatch({ action: 'hide_left_panel' }); | ||||
|         } | ||||
|         if (this.state.width <= showLhsThreshold && window.innerWidth > showLhsThreshold) { | ||||
|             dis.dispatch({ action: 'show_left_panel' }); | ||||
|         } | ||||
|         if (this.state.width > hideRhsThreshold && window.innerWidth <= hideRhsThreshold) { | ||||
|             dis.dispatch({ action: 'hide_right_panel' }); | ||||
|         } | ||||
|         if (this.state.width <= showRhsThreshold && window.innerWidth > showRhsThreshold) { | ||||
|             dis.dispatch({ action: 'show_right_panel' }); | ||||
|         } | ||||
| 
 | ||||
|         this.setState({width: window.innerWidth}); | ||||
|     }, | ||||
| 
 | ||||
|     onRoomCreated: function(room_id) { | ||||
|         dis.dispatch({ | ||||
|             action: "view_room", | ||||
|             room_id: room_id, | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     onRegisterClick: function() { | ||||
|         this.showScreen("register"); | ||||
|     }, | ||||
| 
 | ||||
|     onLoginClick: function() { | ||||
|         this.showScreen("login"); | ||||
|     }, | ||||
| 
 | ||||
|     onRegistered: function(credentials) { | ||||
|         this.onLoggedIn(credentials); | ||||
|         // do post-registration stuff
 | ||||
|         this.showScreen("post_registration"); | ||||
|     }, | ||||
| 
 | ||||
|     onFinishPostRegistration: function() { | ||||
|         // Don't confuse this with "PageType" which is the middle window to show
 | ||||
|         this.setState({ | ||||
|             screen: undefined | ||||
|         }); | ||||
|         this.showScreen("settings"); | ||||
|     }, | ||||
| 
 | ||||
|     render: function() { | ||||
|         var LeftPanel = sdk.getComponent('organisms.LeftPanel'); | ||||
|         var RoomView = sdk.getComponent('organisms.RoomView'); | ||||
|         var RightPanel = sdk.getComponent('organisms.RightPanel'); | ||||
|         var UserSettings = sdk.getComponent('organisms.UserSettings'); | ||||
|         var CreateRoom = sdk.getComponent('organisms.CreateRoom'); | ||||
|         var RoomDirectory = sdk.getComponent('organisms.RoomDirectory'); | ||||
|         var MatrixToolbar = sdk.getComponent('molecules.MatrixToolbar'); | ||||
|         var Notifier = sdk.getComponent('organisms.Notifier'); | ||||
| 
 | ||||
|         // needs to be before normal PageTypes as you are logged in technically
 | ||||
|         if (this.state.screen == 'post_registration') { | ||||
|             return ( | ||||
|                 <PostRegistration | ||||
|                     onComplete={this.onFinishPostRegistration} /> | ||||
|             ); | ||||
|         } | ||||
|         else if (this.state.logged_in && this.state.ready) { | ||||
|             var page_element; | ||||
|             var right_panel = ""; | ||||
| 
 | ||||
|             switch (this.state.page_type) { | ||||
|                 case this.PageTypes.RoomView: | ||||
|                     page_element = <RoomView roomId={this.state.currentRoom} key={this.state.currentRoom} /> | ||||
|                     right_panel = <RightPanel roomId={this.state.currentRoom} collapsed={this.state.collapse_rhs} /> | ||||
|                     break; | ||||
|                 case this.PageTypes.UserSettings: | ||||
|                     page_element = <UserSettings /> | ||||
|                     right_panel = <RightPanel collapsed={this.state.collapse_rhs}/> | ||||
|                     break; | ||||
|                 case this.PageTypes.CreateRoom: | ||||
|                     page_element = <CreateRoom onRoomCreated={this.onRoomCreated}/> | ||||
|                     right_panel = <RightPanel collapsed={this.state.collapse_rhs}/> | ||||
|                     break; | ||||
|                 case this.PageTypes.RoomDirectory: | ||||
|                     page_element = <RoomDirectory /> | ||||
|                     right_panel = <RightPanel collapsed={this.state.collapse_rhs}/> | ||||
|                     break; | ||||
|             } | ||||
| 
 | ||||
|             // TODO: Fix duplication here and do conditionals like we do above
 | ||||
|             if (Notifier.supportsDesktopNotifications() && !Notifier.isEnabled() && !Notifier.isToolbarHidden()) { | ||||
|                 return ( | ||||
|                         <div className="mx_MatrixChat_wrapper"> | ||||
|                             <MatrixToolbar /> | ||||
|                             <div className="mx_MatrixChat mx_MatrixChat_toolbarShowing"> | ||||
|                                 <LeftPanel selectedRoom={this.state.currentRoom} collapsed={this.state.collapse_lhs} /> | ||||
|                                 <main className="mx_MatrixChat_middlePanel"> | ||||
|                                     {page_element} | ||||
|                                 </main> | ||||
|                                 {right_panel} | ||||
|                             </div> | ||||
|                         </div> | ||||
|                 ); | ||||
|             } | ||||
|             else { | ||||
|                 return ( | ||||
|                         <div className="mx_MatrixChat"> | ||||
|                             <LeftPanel selectedRoom={this.state.currentRoom} collapsed={this.state.collapse_lhs} /> | ||||
|                             <main className="mx_MatrixChat_middlePanel"> | ||||
|                                 {page_element} | ||||
|                             </main> | ||||
|                             {right_panel} | ||||
|                         </div> | ||||
|                 ); | ||||
|             } | ||||
|         } else if (this.state.logged_in) { | ||||
|             var Spinner = sdk.getComponent('elements.Spinner'); | ||||
|             return ( | ||||
|                 <div className="mx_MatrixChat_splash"> | ||||
|                     <Spinner /> | ||||
|                     <a href="#" className="mx_MatrixChat_splashButtons" onClick={ this.onLogoutClick }>Logout</a> | ||||
|                 </div> | ||||
|             ); | ||||
|         } else if (this.state.screen == 'register') { | ||||
|             return ( | ||||
|                 <Registration | ||||
|                     clientSecret={this.state.register_client_secret} | ||||
|                     sessionId={this.state.register_session_id} | ||||
|                     idSid={this.state.register_id_sid} | ||||
|                     hsUrl={config.default_hs_url} | ||||
|                     isUrl={config.default_is_url} | ||||
|                     registrationUrl={this.props.registrationUrl} | ||||
|                     onLoggedIn={this.onRegistered} | ||||
|                     onLoginClick={this.onLoginClick} /> | ||||
|             ); | ||||
|         } else { | ||||
|             return ( | ||||
|                 <Login | ||||
|                     onLoggedIn={this.onLoggedIn} | ||||
|                     onRegisterClick={this.onRegisterClick} | ||||
|                     homeserverUrl={config.default_hs_url} | ||||
|                     identityServerUrl={config.default_is_url} /> | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
| }); | ||||
|  | @ -21,12 +21,16 @@ var React = require("react"); | |||
| var ReactDOM = require("react-dom"); | ||||
| var sdk = require("matrix-react-sdk"); | ||||
| sdk.loadSkin(require('../component-index')); | ||||
| sdk.loadModule(require('../modules/VectorConferenceHandler')); | ||||
| var VectorConferenceHandler = require('../VectorConferenceHandler'); | ||||
| var configJson = require("../../config.json"); | ||||
| 
 | ||||
| var qs = require("querystring"); | ||||
| 
 | ||||
| var lastLocationHashSet = null; | ||||
| 
 | ||||
| var CallHandler = require("matrix-react-sdk/lib/CallHandler"); | ||||
| CallHandler.setConferenceHandler(VectorConferenceHandler); | ||||
| 
 | ||||
| function checkBrowserFeatures(featureList) { | ||||
|     if (!window.Modernizr) { | ||||
|         console.error("Cannot check features - Modernizr global is missing."); | ||||
|  | @ -136,9 +140,13 @@ window.onload = function() { | |||
| 
 | ||||
| function loadApp() { | ||||
|     if (validBrowser) { | ||||
|         var MatrixChat = sdk.getComponent('pages.MatrixChat'); | ||||
|         var MatrixChat = sdk.getComponent('structures.MatrixChat'); | ||||
|         window.matrixChat = ReactDOM.render( | ||||
|             <MatrixChat onNewScreen={onNewScreen} registrationUrl={makeRegistrationUrl()} />, | ||||
|             <MatrixChat | ||||
|                 onNewScreen={onNewScreen} | ||||
|                 registrationUrl={makeRegistrationUrl()} | ||||
|                 ConferenceHandler={VectorConferenceHandler} | ||||
|                 config={configJson} />, | ||||
|             document.getElementById('matrixchat') | ||||
|         ); | ||||
|     } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 David Baker
						David Baker