Merge pull request #2511 from matrix-org/jryans/rm-team-server
Remove support for team serverspull/21833/head
						commit
						a07ba49641
					
				|  | @ -27,7 +27,6 @@ import UserActivity from './UserActivity'; | |||
| import Presence from './Presence'; | ||||
| import dis from './dispatcher'; | ||||
| import DMRoomMap from './utils/DMRoomMap'; | ||||
| import RtsClient from './RtsClient'; | ||||
| import Modal from './Modal'; | ||||
| import sdk from './index'; | ||||
| import ActiveWidgetStore from './stores/ActiveWidgetStore'; | ||||
|  | @ -224,7 +223,7 @@ function _registerAsGuest(hsUrl, isUrl, defaultDeviceDisplayName) { | |||
| //
 | ||||
| //      The plan is to gradually move the localStorage access done here into
 | ||||
| //      SessionStore to avoid bugs where the view becomes out-of-sync with
 | ||||
| //      localStorage (e.g. teamToken, isGuest etc.)
 | ||||
| //      localStorage (e.g. isGuest etc.)
 | ||||
| async function _restoreFromLocalStorage() { | ||||
|     if (!localStorage) { | ||||
|         return false; | ||||
|  | @ -286,15 +285,6 @@ function _handleLoadSessionFailure(e) { | |||
|     }); | ||||
| } | ||||
| 
 | ||||
| let rtsClient = null; | ||||
| export function initRtsClient(url) { | ||||
|     if (url) { | ||||
|         rtsClient = new RtsClient(url); | ||||
|     } else { | ||||
|         rtsClient = null; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Transitions to a logged-in state using the given credentials. | ||||
|  * | ||||
|  | @ -333,7 +323,7 @@ async function _doSetLoggedIn(credentials, clearStorage) { | |||
|     ); | ||||
| 
 | ||||
|     // This is dispatched to indicate that the user is still in the process of logging in
 | ||||
|     // because `teamPromise` may take some time to resolve, breaking the assumption that
 | ||||
|     // because async code may take some time to resolve, breaking the assumption that
 | ||||
|     // `setLoggedIn` takes an "instant" to complete, and dispatch `on_logged_in` a few ms
 | ||||
|     // later than MatrixChat might assume.
 | ||||
|     //
 | ||||
|  | @ -347,10 +337,6 @@ async function _doSetLoggedIn(credentials, clearStorage) { | |||
| 
 | ||||
|     Analytics.setLoggedIn(credentials.guest, credentials.homeserverUrl, credentials.identityServerUrl); | ||||
| 
 | ||||
|     // Resolves by default
 | ||||
|     let teamPromise = Promise.resolve(null); | ||||
| 
 | ||||
| 
 | ||||
|     if (localStorage) { | ||||
|         try { | ||||
|             _persistCredentialsToLocalStorage(credentials); | ||||
|  | @ -367,27 +353,13 @@ async function _doSetLoggedIn(credentials, clearStorage) { | |||
|         } catch (e) { | ||||
|             console.warn("Error using local storage: can't persist session!", e); | ||||
|         } | ||||
| 
 | ||||
|         if (rtsClient && !credentials.guest) { | ||||
|             teamPromise = rtsClient.login(credentials.userId).then((body) => { | ||||
|                 if (body.team_token) { | ||||
|                     localStorage.setItem("mx_team_token", body.team_token); | ||||
|                 } | ||||
|                 return body.team_token; | ||||
|             }, (err) => { | ||||
|                 console.warn(`Failed to get team token on login: ${err}` ); | ||||
|                 return null; | ||||
|             }); | ||||
|         } | ||||
|     } else { | ||||
|         console.warn("No local storage available: can't persist session!"); | ||||
|     } | ||||
| 
 | ||||
|     MatrixClientPeg.replaceUsingCreds(credentials); | ||||
| 
 | ||||
|     teamPromise.then((teamToken) => { | ||||
|         dis.dispatch({action: 'on_logged_in', teamToken: teamToken}); | ||||
|     }); | ||||
|     dis.dispatch({ action: 'on_logged_in' }); | ||||
| 
 | ||||
|     await startMatrixClient(); | ||||
|     return MatrixClientPeg.get(); | ||||
|  |  | |||
							
								
								
									
										104
									
								
								src/RtsClient.js
								
								
								
								
							
							
						
						
									
										104
									
								
								src/RtsClient.js
								
								
								
								
							|  | @ -1,104 +0,0 @@ | |||
| import 'whatwg-fetch'; | ||||
| 
 | ||||
| let fetchFunction = fetch; | ||||
| 
 | ||||
| function checkStatus(response) { | ||||
|     if (!response.ok) { | ||||
|         return response.text().then((text) => { | ||||
|             throw new Error(text); | ||||
|         }); | ||||
|     } | ||||
|     return response; | ||||
| } | ||||
| 
 | ||||
| function parseJson(response) { | ||||
|     return response.json(); | ||||
| } | ||||
| 
 | ||||
| function encodeQueryParams(params) { | ||||
|     return '?' + Object.keys(params).map((k) => { | ||||
|         return k + '=' + encodeURIComponent(params[k]); | ||||
|     }).join('&'); | ||||
| } | ||||
| 
 | ||||
| const request = (url, opts) => { | ||||
|     if (opts && opts.qs) { | ||||
|         url += encodeQueryParams(opts.qs); | ||||
|         delete opts.qs; | ||||
|     } | ||||
|     if (opts && opts.body) { | ||||
|         if (!opts.headers) { | ||||
|             opts.headers = {}; | ||||
|         } | ||||
|         opts.body = JSON.stringify(opts.body); | ||||
|         opts.headers['Content-Type'] = 'application/json'; | ||||
|     } | ||||
|     return fetchFunction(url, opts) | ||||
|         .then(checkStatus) | ||||
|         .then(parseJson); | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| export default class RtsClient { | ||||
|     constructor(url) { | ||||
|         this._url = url; | ||||
|     } | ||||
| 
 | ||||
|     getTeamsConfig() { | ||||
|         return request(this._url + '/teams'); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Track a referral with the Riot Team Server. This should be called once a referred | ||||
|      * user has been successfully registered. | ||||
|      * @param {string} referrer the user ID of one who referred the user to Riot. | ||||
|      * @param {string} sid the sign-up identity server session ID . | ||||
|      * @param {string} clientSecret the sign-up client secret. | ||||
|      * @returns {Promise} a promise that resolves to { team_token: 'sometoken' } upon | ||||
|      * success. | ||||
|      */ | ||||
|     trackReferral(referrer, sid, clientSecret) { | ||||
|         return request(this._url + '/register', | ||||
|             { | ||||
|                 body: { | ||||
|                     referrer: referrer, | ||||
|                     session_id: sid, | ||||
|                     client_secret: clientSecret, | ||||
|                 }, | ||||
|                 method: 'POST', | ||||
|             }, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     getTeam(teamToken) { | ||||
|         return request(this._url + '/teamConfiguration', | ||||
|             { | ||||
|                 qs: { | ||||
|                     team_token: teamToken, | ||||
|                 }, | ||||
|             }, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Signal to the RTS that a login has occurred and that a user requires their team's | ||||
|      * token. | ||||
|      * @param {string} userId the user ID of the user who is a member of a team. | ||||
|      * @returns {Promise} a promise that resolves to { team_token: 'sometoken' } upon | ||||
|      * success. | ||||
|      */ | ||||
|     login(userId) { | ||||
|         return request(this._url + '/login', | ||||
|             { | ||||
|                 qs: { | ||||
|                     user_id: userId, | ||||
|                 }, | ||||
|             }, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     // allow fetch to be replaced, for testing.
 | ||||
|     static setFetch(fn) { | ||||
|         fetchFunction = fn; | ||||
|     } | ||||
| } | ||||
|  | @ -30,11 +30,6 @@ class HomePage extends React.Component { | |||
|     static displayName = 'HomePage'; | ||||
| 
 | ||||
|     static propTypes = { | ||||
|         // URL base of the team server. Optional.
 | ||||
|         teamServerUrl: PropTypes.string, | ||||
|         // Team token. Optional. If set, used to get the static homepage of the team
 | ||||
|         //      associated. If unset, homePageUrl will be used.
 | ||||
|         teamToken: PropTypes.string, | ||||
|         // URL to use as the iFrame src. Defaults to /home.html.
 | ||||
|         homePageUrl: PropTypes.string, | ||||
|     }; | ||||
|  | @ -56,35 +51,29 @@ class HomePage extends React.Component { | |||
|     componentWillMount() { | ||||
|         this._unmounted = false; | ||||
| 
 | ||||
|         if (this.props.teamToken && this.props.teamServerUrl) { | ||||
|             this.setState({ | ||||
|                 iframeSrc: `${this.props.teamServerUrl}/static/${this.props.teamToken}/home.html`, | ||||
|             }); | ||||
|         } else { | ||||
|             // we use request() to inline the homepage into the react component
 | ||||
|             // so that it can inherit CSS and theming easily rather than mess around
 | ||||
|             // with iframes and trying to synchronise document.stylesheets.
 | ||||
|         // we use request() to inline the homepage into the react component
 | ||||
|         // so that it can inherit CSS and theming easily rather than mess around
 | ||||
|         // with iframes and trying to synchronise document.stylesheets.
 | ||||
| 
 | ||||
|             const src = this.props.homePageUrl || 'home.html'; | ||||
|         const src = this.props.homePageUrl || 'home.html'; | ||||
| 
 | ||||
|             request( | ||||
|                 { method: "GET", url: src }, | ||||
|                 (err, response, body) => { | ||||
|                     if (this._unmounted) { | ||||
|                         return; | ||||
|                     } | ||||
|         request( | ||||
|             { method: "GET", url: src }, | ||||
|             (err, response, body) => { | ||||
|                 if (this._unmounted) { | ||||
|                     return; | ||||
|                 } | ||||
| 
 | ||||
|                     if (err || response.status < 200 || response.status >= 300) { | ||||
|                         console.warn(`Error loading home page: ${err}`); | ||||
|                         this.setState({ page: _t("Couldn't load home page") }); | ||||
|                         return; | ||||
|                     } | ||||
|                 if (err || response.status < 200 || response.status >= 300) { | ||||
|                     console.warn(`Error loading home page: ${err}`); | ||||
|                     this.setState({ page: _t("Couldn't load home page") }); | ||||
|                     return; | ||||
|                 } | ||||
| 
 | ||||
|                     body = body.replace(/_t\(['"]([\s\S]*?)['"]\)/mg, (match, g1)=>this.translate(g1)); | ||||
|                     this.setState({ page: body }); | ||||
|                 }, | ||||
|             ); | ||||
|         } | ||||
|                 body = body.replace(/_t\(['"]([\s\S]*?)['"]\)/mg, (match, g1)=>this.translate(g1)); | ||||
|                 this.setState({ page: body }); | ||||
|             }, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     componentWillUnmount() { | ||||
|  |  | |||
|  | @ -63,7 +63,6 @@ const LoggedInView = React.createClass({ | |||
|         // transitioned to PWLU)
 | ||||
|         onRegistered: PropTypes.func, | ||||
|         collapsedRhs: PropTypes.bool, | ||||
|         teamToken: PropTypes.string, | ||||
| 
 | ||||
|         // Used by the RoomView to handle joining rooms
 | ||||
|         viaServers: PropTypes.arrayOf(PropTypes.string), | ||||
|  | @ -457,8 +456,6 @@ const LoggedInView = React.createClass({ | |||
|                 pageElement = <UserSettings | ||||
|                     onClose={this.props.onCloseAllSettings} | ||||
|                     brand={this.props.config.brand} | ||||
|                     referralBaseUrl={this.props.config.referralBaseUrl} | ||||
|                     teamToken={this.props.teamToken} | ||||
|                 />; | ||||
|                 break; | ||||
| 
 | ||||
|  | @ -475,15 +472,7 @@ const LoggedInView = React.createClass({ | |||
| 
 | ||||
|             case PageTypes.HomePage: | ||||
|                 { | ||||
|                     // If team server config is present, pass the teamServerURL. props.teamToken
 | ||||
|                     // must also be set for the team page to be displayed, otherwise the
 | ||||
|                     // welcomePageUrl is used (which might be undefined).
 | ||||
|                     const teamServerUrl = this.props.config.teamServerConfig ? | ||||
|                         this.props.config.teamServerConfig.teamServerURL : null; | ||||
| 
 | ||||
|                     pageElement = <HomePage | ||||
|                         teamServerUrl={teamServerUrl} | ||||
|                         teamToken={this.props.teamToken} | ||||
|                         homePageUrl={this.props.config.welcomePageUrl} | ||||
|                     />; | ||||
|                 } | ||||
|  |  | |||
|  | @ -75,8 +75,8 @@ const VIEWS = { | |||
|     // we have valid matrix credentials (either via an explicit login, via the
 | ||||
|     // initial re-animation/guest registration, or via a registration), and are
 | ||||
|     // now setting up a matrixclient to talk to it. This isn't an instant
 | ||||
|     // process because (a) we need to clear out indexeddb, and (b) we need to
 | ||||
|     // talk to the team server; while it is going on we show a big spinner.
 | ||||
|     // process because we need to clear out indexeddb. While it is going on we
 | ||||
|     // show a big spinner.
 | ||||
|     LOGGING_IN: 5, | ||||
| 
 | ||||
|     // we are logged in with an active matrix client.
 | ||||
|  | @ -256,42 +256,6 @@ export default React.createClass({ | |||
|             MatrixClientPeg.opts.initialSyncLimit = this.props.config.sync_timeline_limit; | ||||
|         } | ||||
| 
 | ||||
|         // To enable things like riot.im/geektime in a nicer way than rewriting the URL
 | ||||
|         // and appending a team token query parameter, use the first path segment to
 | ||||
|         // indicate a team, with "public" team tokens stored in the config teamTokenMap.
 | ||||
|         let routedTeamToken = null; | ||||
|         if (this.props.config.teamTokenMap) { | ||||
|             const teamName = window.location.pathname.split('/')[1]; | ||||
|             if (teamName && this.props.config.teamTokenMap.hasOwnProperty(teamName)) { | ||||
|                 routedTeamToken = this.props.config.teamTokenMap[teamName]; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // Persist the team token across refreshes using sessionStorage. A new window or
 | ||||
|         // tab will not persist sessionStorage, but refreshes will.
 | ||||
|         if (this.props.startingFragmentQueryParams.team_token) { | ||||
|             window.sessionStorage.setItem( | ||||
|                 'mx_team_token', | ||||
|                 this.props.startingFragmentQueryParams.team_token, | ||||
|             ); | ||||
|         } | ||||
| 
 | ||||
|         // Use the locally-stored team token first, then as a fall-back, check to see if
 | ||||
|         // a referral link was used, which will contain a query parameter `team_token`.
 | ||||
|         this._teamToken = routedTeamToken || | ||||
|             window.localStorage.getItem('mx_team_token') || | ||||
|             window.sessionStorage.getItem('mx_team_token'); | ||||
| 
 | ||||
|         // Some users have ended up with "undefined" as their local storage team token,
 | ||||
|         // treat that as undefined.
 | ||||
|         if (this._teamToken === "undefined") { | ||||
|             this._teamToken = undefined; | ||||
|         } | ||||
| 
 | ||||
|         if (this._teamToken) { | ||||
|             console.info(`Team token set to ${this._teamToken}`); | ||||
|         } | ||||
| 
 | ||||
|         // Set up the default URLs (async)
 | ||||
|         if (this.getDefaultServerName() && !this.getDefaultHsUrl(false)) { | ||||
|             this.setState({loadingDefaultHomeserver: true}); | ||||
|  | @ -360,9 +324,6 @@ export default React.createClass({ | |||
|             linkifyMatrix.onGroupClick = this.onGroupClick; | ||||
|         } | ||||
| 
 | ||||
|         const teamServerConfig = this.props.config.teamServerConfig || {}; | ||||
|         Lifecycle.initRtsClient(teamServerConfig.teamServerURL); | ||||
| 
 | ||||
|         // the first thing to do is to try the token params in the query-string
 | ||||
|         Lifecycle.attemptTokenLogin(this.props.realQueryParams).then((loggedIn) => { | ||||
|             if (loggedIn) { | ||||
|  | @ -726,7 +687,7 @@ export default React.createClass({ | |||
|                 }); | ||||
|                 break; | ||||
|             case 'on_logged_in': | ||||
|                 this._onLoggedIn(payload.teamToken); | ||||
|                 this._onLoggedIn(); | ||||
|                 break; | ||||
|             case 'on_logged_out': | ||||
|                 this._onLoggedOut(); | ||||
|  | @ -1196,16 +1157,10 @@ export default React.createClass({ | |||
| 
 | ||||
|     /** | ||||
|      * Called when a new logged in session has started | ||||
|      * | ||||
|      * @param {string} teamToken | ||||
|      */ | ||||
|     _onLoggedIn: async function(teamToken) { | ||||
|     _onLoggedIn: async function() { | ||||
|         this.setStateForNewView({view: VIEWS.LOGGED_IN}); | ||||
|         if (teamToken) { | ||||
|             // A team member has logged in, not a guest
 | ||||
|             this._teamToken = teamToken; | ||||
|             dis.dispatch({action: 'view_home_page'}); | ||||
|         } else if (this._is_registered) { | ||||
|         if (this._is_registered) { | ||||
|             this._is_registered = false; | ||||
| 
 | ||||
|             if (this.props.config.welcomeUserId && getCurrentLanguage().startsWith("en")) { | ||||
|  | @ -1261,7 +1216,6 @@ export default React.createClass({ | |||
|             currentRoomId: null, | ||||
|             page_type: PageTypes.RoomDirectory, | ||||
|         }); | ||||
|         this._teamToken = null; | ||||
|         this._setPageSubtitle(); | ||||
|     }, | ||||
| 
 | ||||
|  | @ -1707,15 +1661,13 @@ export default React.createClass({ | |||
| 
 | ||||
|     onReturnToAppClick: function() { | ||||
|         // treat it the same as if the user had completed the login
 | ||||
|         this._onLoggedIn(null); | ||||
|         this._onLoggedIn(); | ||||
|     }, | ||||
| 
 | ||||
|     // returns a promise which resolves to the new MatrixClient
 | ||||
|     onRegistered: function(credentials, teamToken) { | ||||
|         // XXX: These both should be in state or ideally store(s) because we risk not
 | ||||
|     onRegistered: function(credentials) { | ||||
|         // XXX: This should be in state or ideally store(s) because we risk not
 | ||||
|         //      rendering the most up-to-date view of state otherwise.
 | ||||
|         // teamToken may not be truthy
 | ||||
|         this._teamToken = teamToken; | ||||
|         this._is_registered = true; | ||||
|         return Lifecycle.setLoggedIn(credentials); | ||||
|     }, | ||||
|  | @ -1888,7 +1840,6 @@ export default React.createClass({ | |||
|                         onCloseAllSettings={this.onCloseAllSettings} | ||||
|                         onRegistered={this.onRegistered} | ||||
|                         currentRoomId={this.state.currentRoomId} | ||||
|                         teamToken={this._teamToken} | ||||
|                         showCookieBar={this.state.showCookieBar} | ||||
|                         {...this.props} | ||||
|                         {...this.state} | ||||
|  | @ -1929,7 +1880,6 @@ export default React.createClass({ | |||
|                     defaultHsUrl={this.getDefaultHsUrl()} | ||||
|                     defaultIsUrl={this.getDefaultIsUrl()} | ||||
|                     brand={this.props.config.brand} | ||||
|                     teamServerConfig={this.props.config.teamServerConfig} | ||||
|                     customHsUrl={this.getCurrentHsUrl()} | ||||
|                     customIsUrl={this.getCurrentIsUrl()} | ||||
|                     makeRegistrationUrl={this._makeRegistrationUrl} | ||||
|  |  | |||
|  | @ -167,13 +167,6 @@ module.exports = React.createClass({ | |||
|         onClose: PropTypes.func, | ||||
|         // The brand string given when creating email pushers
 | ||||
|         brand: PropTypes.string, | ||||
| 
 | ||||
|         // The base URL to use in the referral link. Defaults to window.location.origin.
 | ||||
|         referralBaseUrl: PropTypes.string, | ||||
| 
 | ||||
|         // Team token for the referral link. If falsy, the referral section will
 | ||||
|         // not appear
 | ||||
|         teamToken: PropTypes.string, | ||||
|     }, | ||||
| 
 | ||||
|     getDefaultProps: function() { | ||||
|  | @ -590,27 +583,6 @@ module.exports = React.createClass({ | |||
|         return <GroupUserSettings />; | ||||
|     }, | ||||
| 
 | ||||
|     _renderReferral: function() { | ||||
|         const teamToken = this.props.teamToken; | ||||
|         if (!teamToken) { | ||||
|             return null; | ||||
|         } | ||||
|         if (typeof teamToken !== 'string') { | ||||
|             console.warn('Team token not a string'); | ||||
|             return null; | ||||
|         } | ||||
|         const href = (this.props.referralBaseUrl || window.location.origin) + | ||||
|             `/#/register?referrer=${this._me}&team_token=${teamToken}`; | ||||
|         return ( | ||||
|             <div> | ||||
|                 <h3>Referral</h3> | ||||
|                 <div className="mx_UserSettings_section"> | ||||
|                     { _t("Refer a friend to Riot:") } <a href={href} target="_blank" rel="noopener">{ href }</a> | ||||
|                 </div> | ||||
|             </div> | ||||
|         ); | ||||
|     }, | ||||
| 
 | ||||
|     onLanguageChange: function(newLang) { | ||||
|         if (this.state.language !== newLang) { | ||||
|             SettingsStore.setValue("language", null, SettingLevel.DEVICE, newLang); | ||||
|  | @ -1355,8 +1327,6 @@ module.exports = React.createClass({ | |||
| 
 | ||||
|                 { this._renderGroupSettings() } | ||||
| 
 | ||||
|                 { this._renderReferral() } | ||||
| 
 | ||||
|                 { notificationArea } | ||||
| 
 | ||||
|                 { this._renderUserInterfaceSettings() } | ||||
|  |  | |||
|  | @ -23,9 +23,7 @@ import React from 'react'; | |||
| import PropTypes from 'prop-types'; | ||||
| 
 | ||||
| import sdk from '../../../index'; | ||||
| import MatrixClientPeg from '../../../MatrixClientPeg'; | ||||
| import RegistrationForm from '../../views/auth/RegistrationForm'; | ||||
| import RtsClient from '../../../RtsClient'; | ||||
| import { _t, _td } from '../../../languageHandler'; | ||||
| import SdkConfig from '../../../SdkConfig'; | ||||
| import { messageForResourceLimitError } from '../../../utils/ErrorUtils'; | ||||
|  | @ -48,13 +46,6 @@ module.exports = React.createClass({ | |||
|         brand: PropTypes.string, | ||||
|         email: PropTypes.string, | ||||
|         referrer: PropTypes.string, | ||||
|         teamServerConfig: PropTypes.shape({ | ||||
|             // Email address to request new teams
 | ||||
|             supportEmail: PropTypes.string.isRequired, | ||||
|             // URL of the riot-team-server to get team configurations and track referrals
 | ||||
|             teamServerURL: PropTypes.string.isRequired, | ||||
|         }), | ||||
|         teamSelected: PropTypes.object, | ||||
| 
 | ||||
|         // The default server name to use when the user hasn't specified
 | ||||
|         // one. This is used when displaying the defaultHsUrl in the UI.
 | ||||
|  | @ -70,18 +61,11 @@ module.exports = React.createClass({ | |||
|         onLoginClick: PropTypes.func.isRequired, | ||||
|         onCancelClick: PropTypes.func, | ||||
|         onServerConfigChange: PropTypes.func.isRequired, | ||||
| 
 | ||||
|         rtsClient: PropTypes.shape({ | ||||
|             getTeamsConfig: PropTypes.func.isRequired, | ||||
|             trackReferral: PropTypes.func.isRequired, | ||||
|             getTeam: PropTypes.func.isRequired, | ||||
|         }), | ||||
|     }, | ||||
| 
 | ||||
|     getInitialState: function() { | ||||
|         return { | ||||
|             busy: false, | ||||
|             teamServerBusy: false, | ||||
|             errorText: null, | ||||
|             // We remember the values entered by the user because
 | ||||
|             // the registration form will be unmounted during the
 | ||||
|  | @ -106,37 +90,7 @@ module.exports = React.createClass({ | |||
| 
 | ||||
|     componentWillMount: function() { | ||||
|         this._unmounted = false; | ||||
| 
 | ||||
|         this._replaceClient(); | ||||
| 
 | ||||
|         if ( | ||||
|             this.props.teamServerConfig && | ||||
|             this.props.teamServerConfig.teamServerURL && | ||||
|             !this._rtsClient | ||||
|         ) { | ||||
|             this._rtsClient = this.props.rtsClient || new RtsClient(this.props.teamServerConfig.teamServerURL); | ||||
| 
 | ||||
|             this.setState({ | ||||
|                 teamServerBusy: true, | ||||
|             }); | ||||
|             // GET team configurations including domains, names and icons
 | ||||
|             this._rtsClient.getTeamsConfig().then((data) => { | ||||
|                 const teamsConfig = { | ||||
|                     teams: data, | ||||
|                     supportEmail: this.props.teamServerConfig.supportEmail, | ||||
|                 }; | ||||
|                 console.log('Setting teams config to ', teamsConfig); | ||||
|                 this.setState({ | ||||
|                     teamsConfig: teamsConfig, | ||||
|                     teamServerBusy: false, | ||||
|                 }); | ||||
|             }, (err) => { | ||||
|                 console.error('Error retrieving config for teams', err); | ||||
|                 this.setState({ | ||||
|                     teamServerBusy: false, | ||||
|                 }); | ||||
|             }); | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     onServerConfigChange: function(config) { | ||||
|  | @ -191,7 +145,7 @@ module.exports = React.createClass({ | |||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     _onUIAuthFinished: function(success, response, extra) { | ||||
|     _onUIAuthFinished: async function(success, response, extra) { | ||||
|         if (!success) { | ||||
|             let msg = response.message || response.toString(); | ||||
|             // can we give a better error message?
 | ||||
|  | @ -240,58 +194,15 @@ module.exports = React.createClass({ | |||
|             doingUIAuth: false, | ||||
|         }); | ||||
| 
 | ||||
|         // Done regardless of `teamSelected`. People registering with non-team emails
 | ||||
|         // will just nop. The point of this being we might not have the email address
 | ||||
|         // that the user registered with at this stage (depending on whether this
 | ||||
|         // is the client they initiated registration).
 | ||||
|         let trackPromise = Promise.resolve(null); | ||||
|         if (this._rtsClient && extra.emailSid) { | ||||
|             // Track referral if this.props.referrer set, get team_token in order to
 | ||||
|             // retrieve team config and see welcome page etc.
 | ||||
|             trackPromise = this._rtsClient.trackReferral( | ||||
|                 this.props.referrer || '', // Default to empty string = not referred
 | ||||
|                 extra.emailSid, | ||||
|                 extra.clientSecret, | ||||
|             ).then((data) => { | ||||
|                 const teamToken = data.team_token; | ||||
|                 // Store for use /w welcome pages
 | ||||
|                 window.localStorage.setItem('mx_team_token', teamToken); | ||||
| 
 | ||||
|                 this._rtsClient.getTeam(teamToken).then((team) => { | ||||
|                     console.log( | ||||
|                         `User successfully registered with team ${team.name}`, | ||||
|                     ); | ||||
|                     if (!team.rooms) { | ||||
|                         return; | ||||
|                     } | ||||
|                     // Auto-join rooms
 | ||||
|                     team.rooms.forEach((room) => { | ||||
|                         if (room.auto_join && room.room_id) { | ||||
|                             console.log(`Auto-joining ${room.room_id}`); | ||||
|                             MatrixClientPeg.get().joinRoom(room.room_id); | ||||
|                         } | ||||
|                     }); | ||||
|                 }, (err) => { | ||||
|                     console.error('Error getting team config', err); | ||||
|                 }); | ||||
| 
 | ||||
|                 return teamToken; | ||||
|             }, (err) => { | ||||
|                 console.error('Error tracking referral', err); | ||||
|             }); | ||||
|         } | ||||
| 
 | ||||
|         trackPromise.then((teamToken) => { | ||||
|             return this.props.onLoggedIn({ | ||||
|                 userId: response.user_id, | ||||
|                 deviceId: response.device_id, | ||||
|                 homeserverUrl: this._matrixClient.getHomeserverUrl(), | ||||
|                 identityServerUrl: this._matrixClient.getIdentityServerUrl(), | ||||
|                 accessToken: response.access_token, | ||||
|             }, teamToken); | ||||
|         }).then((cli) => { | ||||
|             return this._setupPushers(cli); | ||||
|         const cli = await this.props.onLoggedIn({ | ||||
|             userId: response.user_id, | ||||
|             deviceId: response.device_id, | ||||
|             homeserverUrl: this._matrixClient.getHomeserverUrl(), | ||||
|             identityServerUrl: this._matrixClient.getIdentityServerUrl(), | ||||
|             accessToken: response.access_token, | ||||
|         }); | ||||
| 
 | ||||
|         this._setupPushers(cli); | ||||
|     }, | ||||
| 
 | ||||
|     _setupPushers: function(matrixClient) { | ||||
|  | @ -356,12 +267,6 @@ module.exports = React.createClass({ | |||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|     onTeamSelected: function(teamSelected) { | ||||
|         if (!this._unmounted) { | ||||
|             this.setState({ teamSelected }); | ||||
|         } | ||||
|     }, | ||||
| 
 | ||||
|     onLoginClick: function(ev) { | ||||
|         ev.preventDefault(); | ||||
|         ev.stopPropagation(); | ||||
|  | @ -418,7 +323,7 @@ module.exports = React.createClass({ | |||
|                     poll={true} | ||||
|                 /> | ||||
|             ); | ||||
|         } else if (this.state.busy || this.state.teamServerBusy || !this.state.flows) { | ||||
|         } else if (this.state.busy || !this.state.flows) { | ||||
|             registerBody = <Spinner />; | ||||
|         } else { | ||||
|             let serverConfigSection; | ||||
|  | @ -443,11 +348,9 @@ module.exports = React.createClass({ | |||
|                         defaultPhoneCountry={this.state.formVals.phoneCountry} | ||||
|                         defaultPhoneNumber={this.state.formVals.phoneNumber} | ||||
|                         defaultPassword={this.state.formVals.password} | ||||
|                         teamsConfig={this.state.teamsConfig} | ||||
|                         minPasswordLength={MIN_PASSWORD_LENGTH} | ||||
|                         onError={this.onFormValidationFailed} | ||||
|                         onRegisterClick={this.onFormSubmit} | ||||
|                         onTeamSelected={this.onTeamSelected} | ||||
|                         flows={this.state.flows} | ||||
|                     /> | ||||
|                     { serverConfigSection } | ||||
|  | @ -472,12 +375,7 @@ module.exports = React.createClass({ | |||
| 
 | ||||
|         return ( | ||||
|             <AuthPage> | ||||
|                 <AuthHeader | ||||
|                     icon={this.state.teamSelected ? | ||||
|                         this.props.teamServerConfig.teamServerURL + "/static/common/" + | ||||
|                         this.state.teamSelected.domain + "/icon.png" : | ||||
|                         null} | ||||
|                 /> | ||||
|                 <AuthHeader /> | ||||
|                 <AuthBody> | ||||
|                     <h2>{ _t('Create your account') }</h2> | ||||
|                     { registerBody } | ||||
|  |  | |||
|  | @ -46,17 +46,6 @@ module.exports = React.createClass({ | |||
|         defaultPhoneNumber: PropTypes.string, | ||||
|         defaultUsername: PropTypes.string, | ||||
|         defaultPassword: PropTypes.string, | ||||
|         teamsConfig: PropTypes.shape({ | ||||
|             // Email address to request new teams
 | ||||
|             supportEmail: PropTypes.string, | ||||
|             teams: PropTypes.arrayOf(PropTypes.shape({ | ||||
|                 // The displayed name of the team
 | ||||
|                 "name": PropTypes.string, | ||||
|                 // The domain of team email addresses
 | ||||
|                 "domain": PropTypes.string, | ||||
|             })).required, | ||||
|         }), | ||||
| 
 | ||||
|         minPasswordLength: PropTypes.number, | ||||
|         onError: PropTypes.func, | ||||
|         onRegisterClick: PropTypes.func.isRequired, // onRegisterClick(Object) => ?Promise
 | ||||
|  | @ -75,7 +64,6 @@ module.exports = React.createClass({ | |||
|     getInitialState: function() { | ||||
|         return { | ||||
|             fieldValid: {}, | ||||
|             selectedTeam: null, | ||||
|             // The ISO2 country code selected in the phone number entry
 | ||||
|             phoneCountry: this.props.defaultPhoneCountry, | ||||
|         }; | ||||
|  | @ -150,10 +138,6 @@ module.exports = React.createClass({ | |||
|         return true; | ||||
|     }, | ||||
| 
 | ||||
|     _isUniEmail: function(email) { | ||||
|         return email.endsWith('.ac.uk') || email.endsWith('.edu') || email.endsWith('matrix.org'); | ||||
|     }, | ||||
| 
 | ||||
|     validateField: function(fieldID) { | ||||
|         const pwd1 = this.refs.password.value.trim(); | ||||
|         const pwd2 = this.refs.passwordConfirm.value.trim(); | ||||
|  | @ -161,24 +145,6 @@ module.exports = React.createClass({ | |||
|         switch (fieldID) { | ||||
|             case FIELD_EMAIL: { | ||||
|                 const email = this.refs.email.value; | ||||
|                 if (this.props.teamsConfig && this._isUniEmail(email)) { | ||||
|                     const matchingTeam = this.props.teamsConfig.teams.find( | ||||
|                         (team) => { | ||||
|                             return email.split('@').pop() === team.domain; | ||||
|                         }, | ||||
|                     ) || null; | ||||
|                     this.setState({ | ||||
|                         selectedTeam: matchingTeam, | ||||
|                         showSupportEmail: !matchingTeam, | ||||
|                     }); | ||||
|                     this.props.onTeamSelected(matchingTeam); | ||||
|                 } else { | ||||
|                     this.props.onTeamSelected(null); | ||||
|                     this.setState({ | ||||
|                         selectedTeam: null, | ||||
|                         showSupportEmail: false, | ||||
|                     }); | ||||
|                 } | ||||
|                 const emailValid = email === '' || Email.looksValid(email); | ||||
|                 if (this._authStepIsRequired('m.login.email.identity') && (!emailValid || email === '')) { | ||||
|                     this.markFieldValid(fieldID, false, "RegistrationForm.ERR_MISSING_EMAIL"); | ||||
|  | @ -304,30 +270,6 @@ module.exports = React.createClass({ | |||
|                     value={self.state.email} /> | ||||
|             </div> | ||||
|         ); | ||||
|         let belowEmailSection; | ||||
|         if (this.props.teamsConfig) { | ||||
|             if (this.props.teamsConfig.supportEmail && this.state.showSupportEmail) { | ||||
|                 belowEmailSection = ( | ||||
|                     <p className="mx_Login_support"> | ||||
|                         Sorry, but your university is not registered with us just yet.  | ||||
|                         Email us on  | ||||
|                         <a href={"mailto:" + this.props.teamsConfig.supportEmail}> | ||||
|                             { this.props.teamsConfig.supportEmail } | ||||
|                         </a>  | ||||
|                         to get your university signed up. | ||||
|                         Or continue to register with Riot to enjoy our open source platform. | ||||
|                     </p> | ||||
|                 ); | ||||
|             } else if (this.state.selectedTeam) { | ||||
|                 belowEmailSection = ( | ||||
|                     <p className="mx_Login_support"> | ||||
|                         {_t("You are registering with %(SelectedTeamName)s", { | ||||
|                             SelectedTeamName: this.state.selectedTeam.name, | ||||
|                         })} | ||||
|                     </p> | ||||
|                 ); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         const CountryDropdown = sdk.getComponent('views.auth.CountryDropdown'); | ||||
|         let phoneSection; | ||||
|  | @ -369,7 +311,6 @@ module.exports = React.createClass({ | |||
|             <div> | ||||
|                 <form onSubmit={this.onSubmit}> | ||||
|                     { emailSection } | ||||
|                     { belowEmailSection } | ||||
|                     { phoneSection } | ||||
|                     <input type="text" ref="username" | ||||
|                         placeholder={placeholderUserName} defaultValue={this.props.defaultUsername} | ||||
|  |  | |||
|  | @ -193,9 +193,6 @@ export default React.createClass({ | |||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         // XXX Implement RTS /register here
 | ||||
|         const teamToken = null; | ||||
| 
 | ||||
|         this.props.onFinished(true, { | ||||
|             userId: response.user_id, | ||||
|             deviceId: response.device_id, | ||||
|  | @ -203,7 +200,6 @@ export default React.createClass({ | |||
|             identityServerUrl: this._matrixClient.getIdentityServerUrl(), | ||||
|             accessToken: response.access_token, | ||||
|             password: this._generatedPassword, | ||||
|             teamToken: teamToken, | ||||
|         }); | ||||
|     }, | ||||
| 
 | ||||
|  |  | |||
|  | @ -15,7 +15,6 @@ limitations under the License. | |||
| */ | ||||
| 
 | ||||
| import Skinner from './Skinner'; | ||||
| import RtsClient from './RtsClient'; | ||||
| 
 | ||||
| module.exports.loadSkin = function(skinObject) { | ||||
|     Skinner.load(skinObject); | ||||
|  | @ -28,7 +27,3 @@ module.exports.resetSkin = function() { | |||
| module.exports.getComponent = function(componentName) { | ||||
|     return Skinner.getComponent(componentName); | ||||
| }; | ||||
| 
 | ||||
| module.exports.setFetch = function(fetchFunction) { | ||||
|     RtsClient.setFetch(fetchFunction); | ||||
| }; | ||||
|  |  | |||
|  | @ -1,105 +0,0 @@ | |||
| /* | ||||
| Copyright 2017 Vector Creations 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. | ||||
| */ | ||||
| 
 | ||||
| const jest = require('jest-mock'); | ||||
| const React = require('react'); | ||||
| const ReactTestUtils = require('react-addons-test-utils'); | ||||
| const expect = require('expect'); | ||||
| 
 | ||||
| const testUtils = require('test-utils'); | ||||
| 
 | ||||
| const sdk = require('matrix-react-sdk'); | ||||
| const Registration = sdk.getComponent('structures.auth.Registration'); | ||||
| 
 | ||||
| let rtsClient; | ||||
| let client; | ||||
| 
 | ||||
| const TEAM_CONFIG = { | ||||
|     supportEmail: 'support@some.domain', | ||||
|     teamServerURL: 'http://someteamserver.bla', | ||||
| }; | ||||
| 
 | ||||
| const CREDENTIALS = {userId: '@me:here'}; | ||||
| const MOCK_REG_RESPONSE = { | ||||
|     user_id: CREDENTIALS.userId, | ||||
|     device_id: 'mydevice', | ||||
|     access_token: '2234569864534231', | ||||
| }; | ||||
| 
 | ||||
| describe('Registration', function() { | ||||
|     beforeEach(function() { | ||||
|         testUtils.beforeEach(this); | ||||
|         client = testUtils.createTestClient(); | ||||
|         client.credentials = CREDENTIALS; | ||||
| 
 | ||||
|         // Mock an RTS client that supports one team and naively returns team tokens when
 | ||||
|         // tracking by mapping email SIDs to team tokens. This is fine because we only
 | ||||
|         // want to assert the client behaviour such that a user recognised by the
 | ||||
|         // rtsClient (which would normally talk to the RTS server) as a team member is
 | ||||
|         // correctly logged in as one (and other such assertions).
 | ||||
|         rtsClient = testUtils.createTestRtsClient( | ||||
|             { | ||||
|                 'myawesometeam123': { | ||||
|                     name: 'Team Awesome', | ||||
|                     domain: 'team.awesome.net', | ||||
|                 }, | ||||
|             }, | ||||
|             {'someEmailSid1234': 'myawesometeam123'}, | ||||
|         ); | ||||
|     }); | ||||
| 
 | ||||
|     it('should track a referral following successful registration of a team member', function(done) { | ||||
|         const expectedCreds = { | ||||
|             userId: MOCK_REG_RESPONSE.user_id, | ||||
|             deviceId: MOCK_REG_RESPONSE.device_id, | ||||
|             homeserverUrl: client.getHomeserverUrl(), | ||||
|             identityServerUrl: client.getIdentityServerUrl(), | ||||
|             accessToken: MOCK_REG_RESPONSE.access_token, | ||||
|         }; | ||||
|         const onLoggedIn = function(creds, teamToken) { | ||||
|             expect(creds).toEqual(expectedCreds); | ||||
|             expect(teamToken).toBe('myawesometeam123'); | ||||
|             done(); | ||||
|         }; | ||||
| 
 | ||||
|         const res = ReactTestUtils.renderIntoDocument( | ||||
|             <Registration | ||||
|                 teamServerConfig={TEAM_CONFIG} | ||||
|                 onLoggedIn={onLoggedIn} | ||||
|                 rtsClient={rtsClient} | ||||
|             />, | ||||
|         ); | ||||
| 
 | ||||
|         res._onUIAuthFinished(true, MOCK_REG_RESPONSE, {emailSid: 'someEmailSid1234'}); | ||||
|     }); | ||||
| 
 | ||||
|     it('should NOT track a referral following successful registration of a non-team member', function(done) { | ||||
|         const onLoggedIn = jest.fn(function(creds, teamToken) { | ||||
|             expect(teamToken).toBeFalsy(); | ||||
|             done(); | ||||
|         }); | ||||
| 
 | ||||
|         const res = ReactTestUtils.renderIntoDocument( | ||||
|             <Registration | ||||
|                 teamServerConfig={TEAM_CONFIG} | ||||
|                 onLoggedIn={onLoggedIn} | ||||
|                 rtsClient={rtsClient} | ||||
|             />, | ||||
|         ); | ||||
| 
 | ||||
|         res._onUIAuthFinished(true, MOCK_REG_RESPONSE, {emailSid: 'someOtherEmailSid11'}); | ||||
|     }); | ||||
| }); | ||||
|  | @ -1,91 +0,0 @@ | |||
| /* | ||||
| Copyright 2017 Vector Creations 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. | ||||
| */ | ||||
| 
 | ||||
| const jest = require('jest-mock'); | ||||
| const React = require('react'); | ||||
| const ReactTestUtils = require('react-addons-test-utils'); | ||||
| const expect = require('expect'); | ||||
| 
 | ||||
| const testUtils = require('test-utils'); | ||||
| 
 | ||||
| const sdk = require('matrix-react-sdk'); | ||||
| const RegistrationForm = sdk.getComponent('views.auth.RegistrationForm'); | ||||
| 
 | ||||
| const TEAM_CONFIG = { | ||||
|     supportEmail: "support@some.domain", | ||||
|     teams: [ | ||||
|         { name: "The Team Org.", domain: "team.ac.uk" }, | ||||
|         { name: "The Super Team", domain: "superteam.ac.uk" }, | ||||
|     ], | ||||
| }; | ||||
| 
 | ||||
| function doInputEmail(inputEmail, onTeamSelected) { | ||||
|     const res = ReactTestUtils.renderIntoDocument( | ||||
|         <RegistrationForm | ||||
|             teamsConfig={TEAM_CONFIG} | ||||
|             onTeamSelected={onTeamSelected} | ||||
|             flows={[ | ||||
|                 { | ||||
|                     stages: ['m.login.dummy'], | ||||
|                 }, | ||||
|             ]} | ||||
|         />, | ||||
|     ); | ||||
| 
 | ||||
|     const teamInput = res.refs.email; | ||||
|     teamInput.value = inputEmail; | ||||
| 
 | ||||
|     ReactTestUtils.Simulate.change(teamInput); | ||||
|     ReactTestUtils.Simulate.blur(teamInput); | ||||
| 
 | ||||
|     return res; | ||||
| } | ||||
| 
 | ||||
| function expectTeamSelectedFromEmailInput(inputEmail, expectedTeam) { | ||||
|     const onTeamSelected = jest.fn(); | ||||
|     doInputEmail(inputEmail, onTeamSelected); | ||||
| 
 | ||||
|     expect(onTeamSelected).toHaveBeenCalledWith(expectedTeam); | ||||
| } | ||||
| 
 | ||||
| function expectSupportFromEmailInput(inputEmail, isSupportShown) { | ||||
|     const onTeamSelected = jest.fn(); | ||||
|     const res = doInputEmail(inputEmail, onTeamSelected); | ||||
| 
 | ||||
|     expect(res.state.showSupportEmail).toBe(isSupportShown); | ||||
| } | ||||
| 
 | ||||
| describe('RegistrationForm', function() { | ||||
|     beforeEach(function() { | ||||
|         testUtils.beforeEach(this); | ||||
|     }); | ||||
| 
 | ||||
|     it('should select a team when a team email is entered', function() { | ||||
|         expectTeamSelectedFromEmailInput("member@team.ac.uk", TEAM_CONFIG.teams[0]); | ||||
|     }); | ||||
| 
 | ||||
|     it('should not select a team when an unrecognised team email is entered', function() { | ||||
|         expectTeamSelectedFromEmailInput("member@someunknownteam.ac.uk", null); | ||||
|     }); | ||||
| 
 | ||||
|     it('should show support when an unrecognised team email is entered', function() { | ||||
|         expectSupportFromEmailInput("member@someunknownteam.ac.uk", true); | ||||
|     }); | ||||
| 
 | ||||
|     it('should NOT show support when an unrecognised non-team email is entered', function() { | ||||
|         expectSupportFromEmailInput("someone@yahoo.com", false); | ||||
|     }); | ||||
| }); | ||||
|  | @ -104,20 +104,6 @@ export function createTestClient() { | |||
|     }; | ||||
| } | ||||
| 
 | ||||
| export function createTestRtsClient(teamMap, sidMap) { | ||||
|     return { | ||||
|         getTeamsConfig() { | ||||
|             return Promise.resolve(Object.keys(teamMap).map((token) => teamMap[token])); | ||||
|         }, | ||||
|         trackReferral(referrer, emailSid, clientSecret) { | ||||
|             return Promise.resolve({team_token: sidMap[emailSid]}); | ||||
|         }, | ||||
|         getTeam(teamToken) { | ||||
|             return Promise.resolve(teamMap[teamToken]); | ||||
|         }, | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Create an Event. | ||||
|  * @param {Object} opts Values for the event. | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 J. Ryan Stinnett
						J. Ryan Stinnett