From 89345c9e8bfdb47d49fce92aef881f7357abe0ad Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 22 Apr 2020 11:55:03 +0100 Subject: [PATCH 1/9] Convert MatrixChat to an ES6 Class Component Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/MatrixChat.js | 367 ++++++++++++------------ 1 file changed, 179 insertions(+), 188 deletions(-) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 1293ccc7e9..e93b81109a 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -112,15 +112,11 @@ const ONBOARDING_FLOW_STARTERS = [ 'view_create_group', ]; -export default createReactClass({ - // we export this so that the integration tests can use it :-S - statics: { - VIEWS: VIEWS, - }, +export default class MatrixChat extends React.PureComponent { + static VIEWS = VIEWS; // we export this so that the integration tests can use it :-S + static displayName = "MatrixChat"; - displayName: 'MatrixChat', - - propTypes: { + static propTypes = { config: PropTypes.object, serverConfig: PropTypes.instanceOf(ValidatedServerConfig), ConferenceHandler: PropTypes.any, @@ -150,10 +146,19 @@ export default createReactClass({ // A function that makes a registration URL makeRegistrationUrl: PropTypes.func.isRequired, - }, + }; - getInitialState: function() { - const s = { + static defaultProps = { + realQueryParams: {}, + startingFragmentQueryParams: {}, + config: {}, + onTokenLoginCompleted: () => {}, + }; + + constructor(props, context) { + super(props, context); + + this.state = { // the master view we are showing. view: VIEWS.LOADING, @@ -194,35 +199,7 @@ export default createReactClass({ resizeNotifier: new ResizeNotifier(), showNotifierToolbar: false, }; - return s; - }, - getDefaultProps: function() { - return { - realQueryParams: {}, - startingFragmentQueryParams: {}, - config: {}, - onTokenLoginCompleted: () => {}, - }; - }, - - getFallbackHsUrl: function() { - if (this.props.serverConfig && this.props.serverConfig.isDefault) { - return this.props.config.fallback_hs_url; - } else { - return null; - } - }, - - getServerProperties() { - let props = this.state.serverConfig; - if (!props) props = this.props.serverConfig; // for unit tests - if (!props) props = SdkConfig.get()["validated_server_config"]; - return {serverConfig: props}; - }, - - // TODO: [REACT-WARNING] Move this to constructor - UNSAFE_componentWillMount: function() { SdkConfig.put(this.props.config); // Used by _viewRoom before getting state from sync @@ -325,9 +302,53 @@ export default createReactClass({ if (SettingsStore.getValue("analyticsOptIn")) { Analytics.enable(); } - }, + } - _loadSession: function() { + // TODO: [REACT-WARNING] Replace with appropriate lifecycle stage + UNSAFE_componentWillUpdate(props, state) { + if (this.shouldTrackPageChange(this.state, state)) { + this.startPageChangeTimer(); + } + } + + componentDidUpdate(prevProps, prevState) { + if (this.shouldTrackPageChange(prevState, this.state)) { + const durationMs = this.stopPageChangeTimer(); + Analytics.trackPageChange(durationMs); + } + if (this.focusComposer) { + dis.dispatch({action: 'focus_composer'}); + this.focusComposer = false; + } + } + + componentWillUnmount() { + Lifecycle.stopMatrixClient(); + dis.unregister(this.dispatcherRef); + this._themeWatcher.stop(); + window.removeEventListener("focus", this.onFocus); + window.removeEventListener('resize', this.handleResize); + this.state.resizeNotifier.removeListener("middlePanelResized", this._dispatchTimelineResize); + + if (this._accountPasswordTimer !== null) clearTimeout(this._accountPasswordTimer); + } + + getFallbackHsUrl() { + if (this.props.serverConfig && this.props.serverConfig.isDefault) { + return this.props.config.fallback_hs_url; + } else { + return null; + } + } + + getServerProperties() { + let props = this.state.serverConfig; + if (!props) props = this.props.serverConfig; // for unit tests + if (!props) props = SdkConfig.get()["validated_server_config"]; + return {serverConfig: props}; + } + + _loadSession() { // the extra Promise.resolve() ensures that synchronous exceptions hit the same codepath as // asynchronous ones. return Promise.resolve().then(() => { @@ -347,36 +368,7 @@ export default createReactClass({ // Note we don't catch errors from this: we catch everything within // loadSession as there's logic there to ask the user if they want // to try logging out. - }, - - componentWillUnmount: function() { - Lifecycle.stopMatrixClient(); - dis.unregister(this.dispatcherRef); - this._themeWatcher.stop(); - window.removeEventListener("focus", this.onFocus); - window.removeEventListener('resize', this.handleResize); - this.state.resizeNotifier.removeListener("middlePanelResized", this._dispatchTimelineResize); - - if (this._accountPasswordTimer !== null) clearTimeout(this._accountPasswordTimer); - }, - - // TODO: [REACT-WARNING] Replace with appropriate lifecycle stage - UNSAFE_componentWillUpdate: function(props, state) { - if (this.shouldTrackPageChange(this.state, state)) { - this.startPageChangeTimer(); - } - }, - - componentDidUpdate: function(prevProps, prevState) { - if (this.shouldTrackPageChange(prevState, this.state)) { - const durationMs = this.stopPageChangeTimer(); - Analytics.trackPageChange(durationMs); - } - if (this.focusComposer) { - dis.dispatch({action: 'focus_composer'}); - this.focusComposer = false; - } - }, + } startPageChangeTimer() { // Tor doesn't support performance @@ -390,7 +382,7 @@ export default createReactClass({ } this._pageChanging = true; performance.mark('riot_MatrixChat_page_change_start'); - }, + } stopPageChangeTimer() { // Tor doesn't support performance @@ -415,15 +407,15 @@ export default createReactClass({ if (!measurement) return null; return measurement.duration; - }, + } shouldTrackPageChange(prevState, state) { return prevState.currentRoomId !== state.currentRoomId || prevState.view !== state.view || prevState.page_type !== state.page_type; - }, + } - setStateForNewView: function(state) { + setStateForNewView(state) { if (state.view === undefined) { throw new Error("setStateForNewView with no view!"); } @@ -432,9 +424,9 @@ export default createReactClass({ }; Object.assign(newState, state); this.setState(newState); - }, + } - onAction: function(payload) { + onAction = (payload) => { // console.log(`MatrixClientPeg.onAction: ${payload.action}`); const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); @@ -596,8 +588,8 @@ export default createReactClass({ case 'view_create_group': { const CreateGroupDialog = sdk.getComponent("dialogs.CreateGroupDialog"); Modal.createTrackedDialog('Create Community', '', CreateGroupDialog); + break; } - break; case 'view_room_directory': { const RoomDirectory = sdk.getComponent("structures.RoomDirectory"); Modal.createTrackedDialog('Room directory', '', RoomDirectory, {}, @@ -605,8 +597,8 @@ export default createReactClass({ // View the welcome or home page if we need something to look at this._viewSomethingBehindModal(); + break; } - break; case 'view_my_groups': this._setPage(PageTypes.MyGroups); this.notifyNewScreen('groups'); @@ -648,9 +640,8 @@ export default createReactClass({ dis.dispatch({action: 'view_my_groups'}); } break; - case 'notifier_enabled': { - this.setState({showNotifierToolbar: Notifier.shouldShowToolbar()}); - } + case 'notifier_enabled': + this.setState({showNotifierToolbar: Notifier.shouldShowToolbar()}); break; case 'hide_left_panel': this.setState({ @@ -739,15 +730,15 @@ export default createReactClass({ }); break; } - }, + }; - _setPage: function(pageType) { + _setPage(pageType) { this.setState({ page_type: pageType, }); - }, + } - _startRegistration: async function(params) { + async _startRegistration(params) { const newState = { view: VIEWS.REGISTER, }; @@ -773,10 +764,10 @@ export default createReactClass({ ThemeController.isLogin = true; this._themeWatcher.recheck(); this.notifyNewScreen('register'); - }, + } // TODO: Move to RoomViewStore - _viewNextRoom: function(roomIndexDelta) { + _viewNextRoom(roomIndexDelta) { const allRooms = RoomListSorter.mostRecentActivityFirst( MatrixClientPeg.get().getRooms(), ); @@ -802,10 +793,10 @@ export default createReactClass({ action: 'view_room', room_id: allRooms[roomIndex].roomId, }); - }, + } // TODO: Move to RoomViewStore - _viewIndexedRoom: function(roomIndex) { + _viewIndexedRoom(roomIndex) { const allRooms = RoomListSorter.mostRecentActivityFirst( MatrixClientPeg.get().getRooms(), ); @@ -815,7 +806,7 @@ export default createReactClass({ room_id: allRooms[roomIndex].roomId, }); } - }, + } // switch view to the given room // @@ -834,7 +825,7 @@ export default createReactClass({ // @param {Object=} roomInfo.oob_data Object of additional data about the room // that has been passed out-of-band (eg. // room name and avatar from an invite email) - _viewRoom: function(roomInfo) { + _viewRoom(roomInfo) { this.focusComposer = true; const newState = { @@ -895,9 +886,9 @@ export default createReactClass({ this.notifyNewScreen('room/' + presentedId); }); }); - }, + } - _viewGroup: function(payload) { + _viewGroup(payload) { const groupId = payload.group_id; this.setState({ currentGroupId: groupId, @@ -905,7 +896,7 @@ export default createReactClass({ }); this._setPage(PageTypes.GroupView); this.notifyNewScreen('group/' + groupId); - }, + } _viewSomethingBehindModal() { if (this.state.view !== VIEWS.LOGGED_IN) { @@ -915,7 +906,7 @@ export default createReactClass({ if (!this.state.currentGroupId && !this.state.currentRoomId) { this._viewHome(); } - }, + } _viewWelcome() { this.setStateForNewView({ @@ -924,9 +915,9 @@ export default createReactClass({ this.notifyNewScreen('welcome'); ThemeController.isLogin = true; this._themeWatcher.recheck(); - }, + } - _viewHome: function() { + _viewHome() { // The home page requires the "logged in" view, so we'll set that. this.setStateForNewView({ view: VIEWS.LOGGED_IN, @@ -935,9 +926,9 @@ export default createReactClass({ this.notifyNewScreen('home'); ThemeController.isLogin = false; this._themeWatcher.recheck(); - }, + } - _viewUser: function(userId, subAction) { + _viewUser(userId, subAction) { // Wait for the first sync so that `getRoom` gives us a room object if it's // in the sync response const waitForSync = this.firstSyncPromise ? @@ -951,9 +942,9 @@ export default createReactClass({ this.setState({currentUserId: userId}); this._setPage(PageTypes.UserView); }); - }, + } - _setMxId: function(payload) { + _setMxId(payload) { const SetMxIdDialog = sdk.getComponent('views.dialogs.SetMxIdDialog'); const close = Modal.createTrackedDialog('Set MXID', '', SetMxIdDialog, { homeserverUrl: MatrixClientPeg.get().getHomeserverUrl(), @@ -981,9 +972,9 @@ export default createReactClass({ close(); }, }).close; - }, + } - _createRoom: async function() { + async _createRoom() { const CreateRoomDialog = sdk.getComponent('dialogs.CreateRoomDialog'); const modal = Modal.createTrackedDialog('Create Room', '', CreateRoomDialog); @@ -991,9 +982,9 @@ export default createReactClass({ if (shouldCreate) { createRoom(opts); } - }, + } - _chatCreateOrReuse: function(userId) { + _chatCreateOrReuse(userId) { // Use a deferred action to reshow the dialog once the user has registered if (MatrixClientPeg.get().isGuest()) { // No point in making 2 DMs with welcome bot. This assumes view_set_mxid will @@ -1039,9 +1030,9 @@ export default createReactClass({ user_id: userId, }); } - }, + } - _leaveRoomWarnings: function(roomId) { + _leaveRoomWarnings(roomId) { const roomToLeave = MatrixClientPeg.get().getRoom(roomId); // Show a warning if there are additional complications. const joinRules = roomToLeave.currentState.getStateEvents('m.room.join_rules', ''); @@ -1058,9 +1049,9 @@ export default createReactClass({ } } return warnings; - }, + } - _leaveRoom: function(roomId) { + _leaveRoom(roomId) { const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); const roomToLeave = MatrixClientPeg.get().getRoom(roomId); @@ -1071,7 +1062,7 @@ export default createReactClass({ description: ( { _t("Are you sure you want to leave the room '%(roomName)s'?", {roomName: roomToLeave.name}) } - { warnings } + { warnings } ), button: _t("Leave"), @@ -1124,7 +1115,7 @@ export default createReactClass({ } }, }); - }, + } /** * Starts a chat with the welcome user, if the user doesn't already have one @@ -1179,12 +1170,12 @@ export default createReactClass({ return roomId; } return null; - }, + } /** * Called when a new logged in session has started */ - _onLoggedIn: async function() { + async _onLoggedIn() { ThemeController.isLogin = false; this.setStateForNewView({ view: VIEWS.LOGGED_IN }); // If a specific screen is set to be shown after login, show that above @@ -1215,9 +1206,9 @@ export default createReactClass({ } StorageManager.tryPersistStorage(); - }, + } - _showScreenAfterLogin: function() { + _showScreenAfterLogin() { // If screenAfterLogin is set, use that, then null it so that a second login will // result in view_home_page, _user_settings or _room_directory if (this._screenAfterLogin && this._screenAfterLogin.screen) { @@ -1240,19 +1231,19 @@ export default createReactClass({ }); } } - }, + } - _viewLastRoom: function() { + _viewLastRoom() { dis.dispatch({ action: 'view_room', room_id: localStorage.getItem('mx_last_room_id'), }); - }, + } /** * Called when the session is logged out */ - _onLoggedOut: function() { + _onLoggedOut() { this.notifyNewScreen('login'); this.setStateForNewView({ view: VIEWS.LOGIN, @@ -1264,12 +1255,12 @@ export default createReactClass({ this._setPageSubtitle(); ThemeController.isLogin = true; this._themeWatcher.recheck(); - }, + } /** * Called when the session is softly logged out */ - _onSoftLogout: function() { + _onSoftLogout() { this.notifyNewScreen('soft_logout'); this.setStateForNewView({ view: VIEWS.SOFT_LOGOUT, @@ -1279,7 +1270,7 @@ export default createReactClass({ }); this.subTitleStatus = ''; this._setPageSubtitle(); - }, + } /** * Called just before the matrix client is started @@ -1383,10 +1374,10 @@ export default createReactClass({ title: _t('Terms and Conditions'), description:

{ _t( - 'To continue using the %(homeserverDomain)s homeserver ' + - 'you must review and agree to our terms and conditions.', - { homeserverDomain: cli.getDomain() }, - ) } + 'To continue using the %(homeserverDomain)s homeserver ' + + 'you must review and agree to our terms and conditions.', + { homeserverDomain: cli.getDomain() }, + ) }

, button: _t('Review terms and conditions'), @@ -1533,14 +1524,14 @@ export default createReactClass({ // A later sync can/will correct the tint to be the right value for the user const colorScheme = SettingsStore.getValue("roomColor"); Tinter.tint(colorScheme.primary_color, colorScheme.secondary_color); - }, + } /** * Called shortly after the matrix client has started. Useful for * setting up anything that requires the client to be started. * @private */ - _onClientStarted: function() { + _onClientStarted() { const cli = MatrixClientPeg.get(); if (cli.isCryptoEnabled()) { @@ -1559,9 +1550,9 @@ export default createReactClass({ !SettingsStore.getValue("feature_cross_signing"), ); } - }, + } - showScreen: function(screen, params) { + showScreen(screen, params) { if (screen == 'register') { dis.dispatch({ action: 'start_registration', @@ -1706,21 +1697,21 @@ export default createReactClass({ } else { console.info("Ignoring showScreen for '%s'", screen); } - }, + } - notifyNewScreen: function(screen) { + notifyNewScreen(screen) { if (this.props.onNewScreen) { this.props.onNewScreen(screen); } this._setPageSubtitle(); - }, + } - onAliasClick: function(event, alias) { + onAliasClick(event, alias) { event.preventDefault(); dis.dispatch({action: 'view_room', room_alias: alias}); - }, + } - onUserClick: function(event, userId) { + onUserClick(event, userId) { event.preventDefault(); const member = new Matrix.RoomMember(null, userId); @@ -1729,22 +1720,22 @@ export default createReactClass({ action: 'view_user', member: member, }); - }, + } - onGroupClick: function(event, groupId) { + onGroupClick(event, groupId) { event.preventDefault(); dis.dispatch({action: 'view_group', group_id: groupId}); - }, + } - onLogoutClick: function(event) { + onLogoutClick(event) { dis.dispatch({ action: 'logout', }); event.stopPropagation(); event.preventDefault(); - }, + } - handleResize: function(e) { + handleResize = (e) => { const hideLhsThreshold = 1000; const showLhsThreshold = 1000; @@ -1757,49 +1748,49 @@ export default createReactClass({ this.state.resizeNotifier.notifyWindowResized(); this._windowWidth = window.innerWidth; - }, + }; _dispatchTimelineResize() { dis.dispatch({ action: 'timeline_resize' }); - }, + } - onRoomCreated: function(roomId) { + onRoomCreated(roomId) { dis.dispatch({ action: "view_room", room_id: roomId, }); - }, + } - onRegisterClick: function() { + onRegisterClick = () => { this.showScreen("register"); - }, + }; - onLoginClick: function() { + onLoginClick = () => { this.showScreen("login"); - }, + }; - onForgotPasswordClick: function() { + onForgotPasswordClick = () => { this.showScreen("forgot_password"); - }, + }; - onRegisterFlowComplete: function(credentials, password) { + onRegisterFlowComplete = (credentials, password) => { return this.onUserCompletedLoginFlow(credentials, password); - }, + }; // returns a promise which resolves to the new MatrixClient - onRegistered: function(credentials) { + onRegistered(credentials) { return Lifecycle.setLoggedIn(credentials); - }, + } - onFinishPostRegistration: function() { + onFinishPostRegistration = () => { // Don't confuse this with "PageType" which is the middle window to show this.setState({ view: VIEWS.LOGGED_IN, }); this.showScreen("settings"); - }, + }; - onVersion: function(current, latest, releaseNotes) { + onVersion(current, latest, releaseNotes) { this.setState({ version: current, newVersion: latest, @@ -1807,9 +1798,9 @@ export default createReactClass({ newVersionReleaseNotes: releaseNotes, checkingForUpdate: null, }); - }, + } - onSendEvent: function(roomId, event) { + onSendEvent(roomId, event) { const cli = MatrixClientPeg.get(); if (!cli) { dis.dispatch({action: 'message_send_failed'}); @@ -1821,9 +1812,9 @@ export default createReactClass({ }, (err) => { dis.dispatch({action: 'message_send_failed'}); }); - }, + } - _setPageSubtitle: function(subtitle='') { + _setPageSubtitle(subtitle='') { if (this.state.currentRoomId) { const client = MatrixClientPeg.get(); const room = client && client.getRoom(this.state.currentRoomId); @@ -1834,9 +1825,9 @@ export default createReactClass({ subtitle = `${this.subTitleStatus} ${subtitle}`; } document.title = `${SdkConfig.get().brand || 'Riot'} ${subtitle}`; - }, + } - updateStatusIndicator: function(state, prevState) { + updateStatusIndicator(state, prevState) { const notifCount = countRoomsWithNotif(MatrixClientPeg.get().getRooms()).count; if (PlatformPeg.get()) { @@ -1853,28 +1844,28 @@ export default createReactClass({ } this._setPageSubtitle(); - }, + } onCloseAllSettings() { dis.dispatch({ action: 'close_settings' }); - }, + } - onServerConfigChange(config) { + onServerConfigChange = (config) => { this.setState({serverConfig: config}); - }, + }; - _makeRegistrationUrl: function(params) { + _makeRegistrationUrl = (params) => { if (this.props.startingFragmentQueryParams.referrer) { params.referrer = this.props.startingFragmentQueryParams.referrer; } return this.props.makeRegistrationUrl(params); - }, + }; - _collectLoggedInView: function(ref) { + _collectLoggedInView = (ref) => { this._loggedInView = ref; - }, + }; - async onUserCompletedLoginFlow(credentials, password) { + onUserCompletedLoginFlow = async (credentials, password) => { this._accountPassword = password; // self-destruct the password after 5mins if (this._accountPasswordTimer !== null) clearTimeout(this._accountPasswordTimer); @@ -1937,14 +1928,14 @@ export default createReactClass({ this.setState({ pendingInitialSync: false }); return setLoggedInPromise; - }, + }; // complete security / e2e setup has finished - onCompleteSecurityE2eSetupFinished() { + onCompleteSecurityE2eSetupFinished = () => { this._onLoggedIn(); - }, + }; - render: function() { + render() { // console.log(`Rendering MatrixChat with view ${this.state.view}`); let view; @@ -1994,13 +1985,13 @@ export default createReactClass({ const LoggedInView = sdk.getComponent('structures.LoggedInView'); view = ( ); } else { @@ -2082,5 +2073,5 @@ export default createReactClass({ return {view} ; - }, -}); + } +} From d915e613dc6e9ebb59d1d6480d44e8b2d0fb40eb Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 22 Apr 2020 12:27:39 +0100 Subject: [PATCH 2/9] Convert promise utility module to tyepscript Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/utils/{promise.js => promise.ts} | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) rename src/utils/{promise.js => promise.ts} (65%) diff --git a/src/utils/promise.js b/src/utils/promise.ts similarity index 65% rename from src/utils/promise.js rename to src/utils/promise.ts index d7e8d2eae1..553e1e1959 100644 --- a/src/utils/promise.js +++ b/src/utils/promise.ts @@ -1,5 +1,5 @@ /* -Copyright 2019 The Matrix.org Foundation C.I.C. +Copyright 2019, 2020 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,15 +14,15 @@ See the License for the specific language governing permissions and limitations under the License. */ -// @flow - // Returns a promise which resolves with a given value after the given number of ms -export const sleep = (ms: number, value: any): Promise => new Promise((resolve => { setTimeout(resolve, ms, value); })); +export function sleep(ms: number, value: T): Promise { + return new Promise((resolve => { setTimeout(resolve, ms, value); })); +} // Returns a promise which resolves when the input promise resolves with its value // or when the timeout of ms is reached with the value of given timeoutValue -export async function timeout(promise: Promise, timeoutValue: any, ms: number): Promise { - const timeoutPromise = new Promise((resolve) => { +export async function timeout(promise: Promise, timeoutValue: T, ms: number): Promise { + const timeoutPromise = new Promise((resolve) => { const timeoutId = setTimeout(resolve, ms, timeoutValue); promise.then(() => { clearTimeout(timeoutId); @@ -32,12 +32,18 @@ export async function timeout(promise: Promise, timeoutValue: any, ms: number): return Promise.race([promise, timeoutPromise]); } +export interface IDeferred { + resolve: (T) => void; + reject: (any) => void; + promise: Promise; +} + // Returns a Deferred -export function defer(): {resolve: () => {}, reject: () => {}, promise: Promise} { +export function defer(): IDeferred { let resolve; let reject; - const promise = new Promise((_resolve, _reject) => { + const promise = new Promise((_resolve, _reject) => { resolve = _resolve; reject = _reject; }); @@ -46,11 +52,12 @@ export function defer(): {resolve: () => {}, reject: () => {}, promise: Promise} } // Promise.allSettled polyfill until browser support is stable in Firefox -export function allSettled(promises: Promise[]): {status: string, value?: any, reason?: any}[] { +export function allSettled(promises: Promise[]): Promise | ISettledRejected>> { if (Promise.allSettled) { - return Promise.allSettled(promises); + return Promise.allSettled(promises); } + // @ts-ignore - typescript isn't smart enough to see the disjoint here return Promise.all(promises.map((promise) => { return promise.then(value => ({ status: "fulfilled", From 01abb61e9afa513533919ed21c96d73907739192 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 22 Apr 2020 13:08:33 +0100 Subject: [PATCH 3/9] C Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/@types/global.d.ts | 13 + .../{MatrixChat.js => MatrixChat.tsx} | 518 ++++++++++-------- src/utils/promise.ts | 2 +- 3 files changed, 291 insertions(+), 242 deletions(-) rename src/components/structures/{MatrixChat.js => MatrixChat.tsx} (86%) diff --git a/src/@types/global.d.ts b/src/@types/global.d.ts index 1931c0b1d0..e6e339d067 100644 --- a/src/@types/global.d.ts +++ b/src/@types/global.d.ts @@ -37,4 +37,17 @@ declare global { interface StorageEstimate { usageDetails?: {[key: string]: number}; } + + export interface ISettledFulfilled { + status: "fulfilled"; + value: T; + } + export interface ISettledRejected { + status: "rejected"; + reason: any; + } + + interface PromiseConstructor { + allSettled(promises: Promise[]): Promise | ISettledRejected>>; + } } diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.tsx similarity index 86% rename from src/components/structures/MatrixChat.js rename to src/components/structures/MatrixChat.tsx index e93b81109a..576f92408a 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.tsx @@ -17,10 +17,10 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; -import createReactClass from 'create-react-class'; -import PropTypes from 'prop-types'; -import * as Matrix from "matrix-js-sdk"; +import React, {createRef} from 'react'; +import {InvalidStoreError} from "matrix-js-sdk/src/errors"; +import {RoomMember} from "matrix-js-sdk/src/models/room-member"; +import {MatrixEvent} from "matrix-js-sdk/src/models/event"; import { isCryptoAvailable } from 'matrix-js-sdk/src/crypto'; // focus-visible is a Polyfill for the :focus-visible CSS pseudo-attribute used by _AccessibleButton.scss @@ -63,44 +63,45 @@ import DMRoomMap from '../../utils/DMRoomMap'; import { countRoomsWithNotif } from '../../RoomNotifs'; import { ThemeWatcher } from "../../theme"; import { storeRoomAliasInCache } from '../../RoomAliasCache'; -import { defer } from "../../utils/promise"; +import {defer, IDeferred} from "../../utils/promise"; import ToastStore from "../../stores/ToastStore"; import * as StorageManager from "../../utils/StorageManager"; +import type LoggedInView from "./LoggedInView"; /** constants for MatrixChat.state.view */ -export const VIEWS = { +export enum VIEWS { // a special initial state which is only used at startup, while we are // trying to re-animate a matrix client or register as a guest. - LOADING: 0, + LOADING = 0, // we are showing the welcome view - WELCOME: 1, + WELCOME = 1, // we are showing the login view - LOGIN: 2, + LOGIN = 2, // we are showing the registration view - REGISTER: 3, + REGISTER = 3, // completing the registration flow - POST_REGISTRATION: 4, + POST_REGISTRATION = 4, // showing the 'forgot password' view - FORGOT_PASSWORD: 5, + FORGOT_PASSWORD = 5, // showing flow to trust this new device with cross-signing - COMPLETE_SECURITY: 6, + COMPLETE_SECURITY = 6, // flow to setup SSSS / cross-signing on this account - E2E_SETUP: 7, + E2E_SETUP = 7, // we are logged in with an active matrix client. - LOGGED_IN: 8, + LOGGED_IN = 8, // We are logged out (invalid token) but have our local state again. The user // should log back in to rehydrate the client. - SOFT_LOGOUT: 9, -}; + SOFT_LOGOUT = 9, +} // Actions that are redirected through the onboarding process prior to being // re-dispatched. NOTE: some actions are non-trivial and would require @@ -112,42 +113,89 @@ const ONBOARDING_FLOW_STARTERS = [ 'view_create_group', ]; -export default class MatrixChat extends React.PureComponent { +interface IScreen { + screen: string; + params?: object; +} + +interface IRoomInfo { + room_id?: string; + room_alias?: string; + event_id?: string; + + auto_join?: boolean; + highlighted?: boolean; + third_party_invite?: object; + oob_data?: object; + via_servers?: string[]; +} + +interface IProps { // TODO type things better + config: Record; + serverConfig?: ValidatedServerConfig; + ConferenceHandler?: any; + onNewScreen: (string) => void; + enableGuest?: boolean; + // the queryParams extracted from the [real] query-string of the URI + realQueryParams?: Record; + // the initial queryParams extracted from the hash-fragment of the URI + startingFragmentQueryParams?: Record; + // called when we have completed a token login + onTokenLoginCompleted?: () => void; + // Represents the screen to display as a result of parsing the initial window.location + initialScreenAfterLogin?: IScreen; + // displayname, if any, to set on the device when logging in/registering. + defaultDeviceDisplayName?: string, + // A function that makes a registration URL + makeRegistrationUrl: (object) => string, +} + +interface IState { + // the master view we are showing. + view: VIEWS; + // What the LoggedInView would be showing if visible + page_type?: PageTypes; + // The ID of the room we're viewing. This is either populated directly + // in the case where we view a room by ID or by RoomView when it resolves + // what ID an alias points at. + currentRoomId?: string; + currentGroupId?: string; + currentGroupIsNew?: boolean; + // If we're trying to just view a user ID (i.e. /user URL), this is it + currentUserId?: string; + // this is persisted as mx_lhs_size, loaded in LoggedInView + collapseLhs: boolean; + leftDisabled: boolean; + middleDisabled: boolean; + // the right panel's disabled state is tracked in its store. + version?: string; + newVersion?: string; + hasNewVersion: boolean; + newVersionReleaseNotes?: string; + checkingForUpdate?: string; // updateCheckStatusEnum + showCookieBar: boolean; + // Parameters used in the registration dance with the IS + register_client_secret?: string; + register_session_id?: string; + register_id_sid?: string; + // When showing Modal dialogs we need to set aria-hidden on the root app element + // and disable it when there are no dialogs + hideToSRUsers: boolean; + syncError?: Error; + resizeNotifier: ResizeNotifier; + showNotifierToolbar: boolean; + serverConfig?: ValidatedServerConfig; + ready: boolean; + thirdPartyInvite?: object; + roomOobData?: object; + viaServers?: string[]; + pendingInitialSync?: boolean; +} + +export default class MatrixChat extends React.PureComponent { static VIEWS = VIEWS; // we export this so that the integration tests can use it :-S static displayName = "MatrixChat"; - static propTypes = { - config: PropTypes.object, - serverConfig: PropTypes.instanceOf(ValidatedServerConfig), - ConferenceHandler: PropTypes.any, - onNewScreen: PropTypes.func, - registrationUrl: PropTypes.string, - enableGuest: PropTypes.bool, - - // the queryParams extracted from the [real] query-string of the URI - realQueryParams: PropTypes.object, - - // the initial queryParams extracted from the hash-fragment of the URI - startingFragmentQueryParams: PropTypes.object, - - // called when we have completed a token login - onTokenLoginCompleted: PropTypes.func, - - // Represents the screen to display as a result of parsing the initial - // window.location - initialScreenAfterLogin: PropTypes.shape({ - screen: PropTypes.string.isRequired, - params: PropTypes.object, - }), - - // displayname, if any, to set on the device when logging - // in/registering. - defaultDeviceDisplayName: PropTypes.string, - - // A function that makes a registration URL - makeRegistrationUrl: PropTypes.func.isRequired, - }; - static defaultProps = { realQueryParams: {}, startingFragmentQueryParams: {}, @@ -155,51 +203,46 @@ export default class MatrixChat extends React.PureComponent { onTokenLoginCompleted: () => {}, }; + firstSyncComplete: boolean; + firstSyncPromise: IDeferred; + + private screenAfterLogin?: IScreen; + private windowWidth: number; + private pageChanging: boolean; + private accountPassword?: string; + private accountPasswordTimer?: NodeJS.Timeout; + private focusComposer: boolean; + private subTitleStatus: string; + + private readonly loggedInView: React.RefObject; + private readonly dispatcherRef: any; + private readonly themeWatcher: ThemeWatcher; + constructor(props, context) { super(props, context); this.state = { - // the master view we are showing. view: VIEWS.LOADING, - - // What the LoggedInView would be showing if visible - page_type: null, - - // The ID of the room we're viewing. This is either populated directly - // in the case where we view a room by ID or by RoomView when it resolves - // what ID an alias points at. - currentRoomId: null, - - // If we're trying to just view a user ID (i.e. /user URL), this is it - viewUserId: null, - // this is persisted as mx_lhs_size, loaded in LoggedInView collapseLhs: false, leftDisabled: false, middleDisabled: false, - // the right panel's disabled state is tracked in its store. - version: null, - newVersion: null, hasNewVersion: false, newVersionReleaseNotes: null, checkingForUpdate: null, showCookieBar: false, - // Parameters used in the registration dance with the IS - register_client_secret: null, - register_session_id: null, - register_id_sid: null, - - // When showing Modal dialogs we need to set aria-hidden on the root app element - // and disable it when there are no dialogs hideToSRUsers: false, syncError: null, // If the current syncing status is ERROR, the error object, otherwise null. resizeNotifier: new ResizeNotifier(), showNotifierToolbar: false, + ready: false, }; + this.loggedInView = createRef(); + SdkConfig.put(this.props.config); // Used by _viewRoom before getting state from sync @@ -213,13 +256,13 @@ export default class MatrixChat extends React.PureComponent { // a thing to call showScreen with once login completes. this is kept // outside this.state because updating it should never trigger a // rerender. - this._screenAfterLogin = this.props.initialScreenAfterLogin; + this.screenAfterLogin = this.props.initialScreenAfterLogin; - this._windowWidth = 10000; + this.windowWidth = 10000; this.handleResize(); window.addEventListener('resize', this.handleResize); - this._pageChanging = false; + this.pageChanging = false; // check we have the right tint applied for this theme. // N.B. we don't call the whole of setTheme() here as we may be @@ -227,7 +270,7 @@ export default class MatrixChat extends React.PureComponent { Tinter.tint(); // For PersistentElement - this.state.resizeNotifier.on("middlePanelResized", this._dispatchTimelineResize); + this.state.resizeNotifier.on("middlePanelResized", this.dispatchTimelineResize); // Force users to go through the soft logout page if they're soft logged out if (Lifecycle.isSoftLogout()) { @@ -237,12 +280,12 @@ export default class MatrixChat extends React.PureComponent { Lifecycle.loadSession({}); } - this._accountPassword = null; - this._accountPasswordTimer = null; + this.accountPassword = null; + this.accountPasswordTimer = null; this.dispatcherRef = dis.register(this.onAction); - this._themeWatcher = new ThemeWatcher(); - this._themeWatcher.start(); + this.themeWatcher = new ThemeWatcher(); + this.themeWatcher.start(); this.focusComposer = false; @@ -279,17 +322,16 @@ export default class MatrixChat extends React.PureComponent { // if the user has followed a login or register link, don't reanimate // the old creds, but rather go straight to the relevant page - const firstScreen = this._screenAfterLogin ? - this._screenAfterLogin.screen : null; + const firstScreen = this.screenAfterLogin ? this.screenAfterLogin.screen : null; if (firstScreen === 'login' || firstScreen === 'register' || firstScreen === 'forgot_password') { - this._showScreenAfterLogin(); + this.showScreenAfterLogin(); return; } - return this._loadSession(); + return this.loadSession(); }); } @@ -325,12 +367,11 @@ export default class MatrixChat extends React.PureComponent { componentWillUnmount() { Lifecycle.stopMatrixClient(); dis.unregister(this.dispatcherRef); - this._themeWatcher.stop(); - window.removeEventListener("focus", this.onFocus); + this.themeWatcher.stop(); window.removeEventListener('resize', this.handleResize); - this.state.resizeNotifier.removeListener("middlePanelResized", this._dispatchTimelineResize); + this.state.resizeNotifier.removeListener("middlePanelResized", this.dispatchTimelineResize); - if (this._accountPasswordTimer !== null) clearTimeout(this._accountPasswordTimer); + if (this.accountPasswordTimer !== null) clearTimeout(this.accountPasswordTimer); } getFallbackHsUrl() { @@ -348,7 +389,7 @@ export default class MatrixChat extends React.PureComponent { return {serverConfig: props}; } - _loadSession() { + private loadSession() { // the extra Promise.resolve() ensures that synchronous exceptions hit the same codepath as // asynchronous ones. return Promise.resolve().then(() => { @@ -376,11 +417,11 @@ export default class MatrixChat extends React.PureComponent { // This shouldn't happen because UNSAFE_componentWillUpdate and componentDidUpdate // are used. - if (this._pageChanging) { + if (this.pageChanging) { console.warn('MatrixChat.startPageChangeTimer: timer already started'); return; } - this._pageChanging = true; + this.pageChanging = true; performance.mark('riot_MatrixChat_page_change_start'); } @@ -388,11 +429,11 @@ export default class MatrixChat extends React.PureComponent { // Tor doesn't support performance if (!performance || !performance.mark) return null; - if (!this._pageChanging) { + if (!this.pageChanging) { console.warn('MatrixChat.stopPageChangeTimer: timer not started'); return; } - this._pageChanging = false; + this.pageChanging = false; performance.mark('riot_MatrixChat_page_change_stop'); performance.measure( 'riot_MatrixChat_page_change_delta', @@ -409,18 +450,18 @@ export default class MatrixChat extends React.PureComponent { return measurement.duration; } - shouldTrackPageChange(prevState, state) { + shouldTrackPageChange(prevState: IState, state: IState) { return prevState.currentRoomId !== state.currentRoomId || prevState.view !== state.view || prevState.page_type !== state.page_type; } - setStateForNewView(state) { + setStateForNewView(state: Partial) { if (state.view === undefined) { throw new Error("setStateForNewView with no view!"); } const newState = { - viewUserId: null, + currentUserId: null, }; Object.assign(newState, state); this.setState(newState); @@ -479,29 +520,29 @@ export default class MatrixChat extends React.PureComponent { break; case 'start_registration': if (Lifecycle.isSoftLogout()) { - this._onSoftLogout(); + this.onSoftLogout(); break; } // This starts the full registration flow if (payload.screenAfterLogin) { - this._screenAfterLogin = payload.screenAfterLogin; + this.screenAfterLogin = payload.screenAfterLogin; } - this._startRegistration(payload.params || {}); + this.startRegistration(payload.params || {}); break; case 'start_login': if (Lifecycle.isSoftLogout()) { - this._onSoftLogout(); + this.onSoftLogout(); break; } if (payload.screenAfterLogin) { - this._screenAfterLogin = payload.screenAfterLogin; + this.screenAfterLogin = payload.screenAfterLogin; } this.setStateForNewView({ view: VIEWS.LOGIN, }); this.notifyNewScreen('login'); ThemeController.isLogin = true; - this._themeWatcher.recheck(); + this.themeWatcher.recheck(); break; case 'start_post_registration': this.setState({ @@ -520,7 +561,7 @@ export default class MatrixChat extends React.PureComponent { }); break; case 'leave_room': - this._leaveRoom(payload.room_id); + this.leaveRoom(payload.room_id); break; case 'reject_invite': Modal.createTrackedDialog('Reject invitation', '', QuestionDialog, { @@ -549,14 +590,14 @@ export default class MatrixChat extends React.PureComponent { }); break; case 'view_user_info': - this._viewUser(payload.userId, payload.subAction); + this.viewUser(payload.userId, payload.subAction); break; case 'view_room': { // Takes either a room ID or room alias: if switching to a room the client is already // known to be in (eg. user clicks on a room in the recents panel), supply the ID // If the user is clicking on a room in the context of the alias being presented // to them, supply the room alias. If both are supplied, the room ID will be ignored. - const promise = this._viewRoom(payload); + const promise = this.viewRoom(payload); if (payload.deferred_action) { promise.then(() => { dis.dispatch(payload.deferred_action); @@ -565,13 +606,13 @@ export default class MatrixChat extends React.PureComponent { break; } case 'view_prev_room': - this._viewNextRoom(-1); + this.viewNextRoom(-1); break; case 'view_next_room': - this._viewNextRoom(1); + this.viewNextRoom(1); break; case 'view_indexed_room': - this._viewIndexedRoom(payload.roomIndex); + this.viewIndexedRoom(payload.roomIndex); break; case 'view_user_settings': { const UserSettingsDialog = sdk.getComponent("dialogs.UserSettingsDialog"); @@ -579,11 +620,11 @@ export default class MatrixChat extends React.PureComponent { /*className=*/null, /*isPriority=*/false, /*isStatic=*/true); // View the welcome or home page if we need something to look at - this._viewSomethingBehindModal(); + this.viewSomethingBehindModal(); break; } case 'view_create_room': - this._createRoom(); + this.createRoom(); break; case 'view_create_group': { const CreateGroupDialog = sdk.getComponent("dialogs.CreateGroupDialog"); @@ -596,27 +637,27 @@ export default class MatrixChat extends React.PureComponent { 'mx_RoomDirectory_dialogWrapper', false, true); // View the welcome or home page if we need something to look at - this._viewSomethingBehindModal(); + this.viewSomethingBehindModal(); break; } case 'view_my_groups': - this._setPage(PageTypes.MyGroups); + this.setPage(PageTypes.MyGroups); this.notifyNewScreen('groups'); break; case 'view_group': - this._viewGroup(payload); + this.viewGroup(payload); break; case 'view_welcome_page': - this._viewWelcome(); + this.viewWelcome(); break; case 'view_home_page': - this._viewHome(); + this.viewHome(); break; case 'view_set_mxid': - this._setMxId(payload); + this.setMxId(payload); break; case 'view_start_chat_or_reuse': - this._chatCreateOrReuse(payload.user_id); + this.chatCreateOrReuse(payload.user_id); break; case 'view_create_chat': showStartChatInviteDialog(); @@ -629,7 +670,7 @@ export default class MatrixChat extends React.PureComponent { // the last room we were looking at or some reasonable default/guess. We don't // have to worry about email invites or similar being re-triggered because the // function will have cleared that state and not execute that path. - this._showScreenAfterLogin(); + this.showScreenAfterLogin(); break; case 'toggle_my_groups': // We just dispatch the page change rather than have to worry about @@ -670,25 +711,25 @@ export default class MatrixChat extends React.PureComponent { this.state.view !== VIEWS.COMPLETE_SECURITY && this.state.view !== VIEWS.E2E_SETUP ) { - this._onLoggedIn(); + this.onLoggedIn(); } break; case 'on_client_not_viable': - this._onSoftLogout(); + this.onSoftLogout(); break; case 'on_logged_out': - this._onLoggedOut(); + this.onLoggedOut(); break; case 'will_start_client': this.setState({ready: false}, () => { // if the client is about to start, we are, by definition, not ready. // Set ready to false now, then it'll be set to true when the sync // listener we set below fires. - this._onWillStartClient(); + this.onWillStartClient(); }); break; case 'client_started': - this._onClientStarted(); + this.onClientStarted(); break; case 'new_version': this.onVersion( @@ -732,14 +773,14 @@ export default class MatrixChat extends React.PureComponent { } }; - _setPage(pageType) { + private setPage(pageType: string) { this.setState({ page_type: pageType, }); } - async _startRegistration(params) { - const newState = { + private async startRegistration(params: {[key: string]: string}) { + const newState: Partial = { view: VIEWS.REGISTER, }; @@ -762,12 +803,12 @@ export default class MatrixChat extends React.PureComponent { this.setStateForNewView(newState); ThemeController.isLogin = true; - this._themeWatcher.recheck(); + this.themeWatcher.recheck(); this.notifyNewScreen('register'); } // TODO: Move to RoomViewStore - _viewNextRoom(roomIndexDelta) { + private viewNextRoom(roomIndexDelta: number) { const allRooms = RoomListSorter.mostRecentActivityFirst( MatrixClientPeg.get().getRooms(), ); @@ -796,7 +837,7 @@ export default class MatrixChat extends React.PureComponent { } // TODO: Move to RoomViewStore - _viewIndexedRoom(roomIndex) { + private viewIndexedRoom(roomIndex: number) { const allRooms = RoomListSorter.mostRecentActivityFirst( MatrixClientPeg.get().getRooms(), ); @@ -825,18 +866,9 @@ export default class MatrixChat extends React.PureComponent { // @param {Object=} roomInfo.oob_data Object of additional data about the room // that has been passed out-of-band (eg. // room name and avatar from an invite email) - _viewRoom(roomInfo) { + private viewRoom(roomInfo: IRoomInfo) { this.focusComposer = true; - const newState = { - view: VIEWS.LOGGED_IN, - currentRoomId: roomInfo.room_id || null, - page_type: PageTypes.RoomView, - thirdPartyInvite: roomInfo.third_party_invite, - roomOobData: roomInfo.oob_data, - viaServers: roomInfo.via_servers, - }; - if (roomInfo.room_alias) { console.log( `Switching to room alias ${roomInfo.room_alias} at event ` + @@ -881,70 +913,77 @@ export default class MatrixChat extends React.PureComponent { if (roomInfo.event_id && roomInfo.highlighted) { presentedId += "/" + roomInfo.event_id; } - newState.ready = true; - this.setState(newState, () => { + this.setState({ + view: VIEWS.LOGGED_IN, + currentRoomId: roomInfo.room_id || null, + page_type: PageTypes.RoomView, + thirdPartyInvite: roomInfo.third_party_invite, + roomOobData: roomInfo.oob_data, + viaServers: roomInfo.via_servers, + ready: true, + }, () => { this.notifyNewScreen('room/' + presentedId); }); }); } - _viewGroup(payload) { + private viewGroup(payload) { const groupId = payload.group_id; this.setState({ currentGroupId: groupId, currentGroupIsNew: payload.group_is_new, }); - this._setPage(PageTypes.GroupView); + this.setPage(PageTypes.GroupView); this.notifyNewScreen('group/' + groupId); } - _viewSomethingBehindModal() { + private viewSomethingBehindModal() { if (this.state.view !== VIEWS.LOGGED_IN) { - this._viewWelcome(); + this.viewWelcome(); return; } if (!this.state.currentGroupId && !this.state.currentRoomId) { - this._viewHome(); + this.viewHome(); } } - _viewWelcome() { + private viewWelcome() { this.setStateForNewView({ view: VIEWS.WELCOME, }); this.notifyNewScreen('welcome'); ThemeController.isLogin = true; - this._themeWatcher.recheck(); + this.themeWatcher.recheck(); } - _viewHome() { + private viewHome() { // The home page requires the "logged in" view, so we'll set that. this.setStateForNewView({ view: VIEWS.LOGGED_IN, }); - this._setPage(PageTypes.HomePage); + this.setPage(PageTypes.HomePage); this.notifyNewScreen('home'); ThemeController.isLogin = false; - this._themeWatcher.recheck(); + this.themeWatcher.recheck(); } - _viewUser(userId, subAction) { + private viewUser(userId: string, subAction: string) { // Wait for the first sync so that `getRoom` gives us a room object if it's // in the sync response const waitForSync = this.firstSyncPromise ? this.firstSyncPromise.promise : Promise.resolve(); waitForSync.then(() => { if (subAction === 'chat') { - this._chatCreateOrReuse(userId); + this.chatCreateOrReuse(userId); return; } this.notifyNewScreen('user/' + userId); this.setState({currentUserId: userId}); - this._setPage(PageTypes.UserView); + this.setPage(PageTypes.UserView); }); } - _setMxId(payload) { + private setMxId(payload) { const SetMxIdDialog = sdk.getComponent('views.dialogs.SetMxIdDialog'); const close = Modal.createTrackedDialog('Set MXID', '', SetMxIdDialog, { homeserverUrl: MatrixClientPeg.get().getHomeserverUrl(), @@ -974,7 +1013,7 @@ export default class MatrixChat extends React.PureComponent { }).close; } - async _createRoom() { + private async createRoom() { const CreateRoomDialog = sdk.getComponent('dialogs.CreateRoomDialog'); const modal = Modal.createTrackedDialog('Create Room', '', CreateRoomDialog); @@ -984,7 +1023,7 @@ export default class MatrixChat extends React.PureComponent { } } - _chatCreateOrReuse(userId) { + private chatCreateOrReuse(userId: string) { // Use a deferred action to reshow the dialog once the user has registered if (MatrixClientPeg.get().isGuest()) { // No point in making 2 DMs with welcome bot. This assumes view_set_mxid will @@ -1032,7 +1071,7 @@ export default class MatrixChat extends React.PureComponent { } } - _leaveRoomWarnings(roomId) { + private leaveRoomWarnings(roomId: string) { const roomToLeave = MatrixClientPeg.get().getRoom(roomId); // Show a warning if there are additional complications. const joinRules = roomToLeave.currentState.getStateEvents('m.room.join_rules', ''); @@ -1051,11 +1090,11 @@ export default class MatrixChat extends React.PureComponent { return warnings; } - _leaveRoom(roomId) { + private leaveRoom(roomId: string) { const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); const roomToLeave = MatrixClientPeg.get().getRoom(roomId); - const warnings = this._leaveRoomWarnings(roomId); + const warnings = this.leaveRoomWarnings(roomId); Modal.createTrackedDialog('Leave room', '', QuestionDialog, { title: _t("Leave room"), @@ -1121,7 +1160,7 @@ export default class MatrixChat extends React.PureComponent { * Starts a chat with the welcome user, if the user doesn't already have one * @returns {string} The room ID of the new room, or null if no room was created */ - async _startWelcomeUserChat() { + private async startWelcomeUserChat() { // We can end up with multiple tabs post-registration where the user // might then end up with a session and we don't want them all making // a chat with the welcome user: try to de-dupe. @@ -1175,22 +1214,22 @@ export default class MatrixChat extends React.PureComponent { /** * Called when a new logged in session has started */ - async _onLoggedIn() { + private async onLoggedIn() { ThemeController.isLogin = false; this.setStateForNewView({ view: VIEWS.LOGGED_IN }); // If a specific screen is set to be shown after login, show that above // all else, as it probably means the user clicked on something already. - if (this._screenAfterLogin && this._screenAfterLogin.screen) { + if (this.screenAfterLogin && this.screenAfterLogin.screen) { this.showScreen( - this._screenAfterLogin.screen, - this._screenAfterLogin.params, + this.screenAfterLogin.screen, + this.screenAfterLogin.params, ); - this._screenAfterLogin = null; + this.screenAfterLogin = null; } else if (MatrixClientPeg.currentUserIsJustRegistered()) { MatrixClientPeg.setJustRegisteredUserId(null); if (this.props.config.welcomeUserId && getCurrentLanguage().startsWith("en")) { - const welcomeUserRoom = await this._startWelcomeUserChat(); + const welcomeUserRoom = await this.startWelcomeUserChat(); if (welcomeUserRoom === null) { // We didn't redirect to the welcome user room, so show // the homepage. @@ -1202,24 +1241,24 @@ export default class MatrixChat extends React.PureComponent { dis.dispatch({action: 'view_home_page'}); } } else { - this._showScreenAfterLogin(); + this.showScreenAfterLogin(); } StorageManager.tryPersistStorage(); } - _showScreenAfterLogin() { + private showScreenAfterLogin() { // If screenAfterLogin is set, use that, then null it so that a second login will // result in view_home_page, _user_settings or _room_directory - if (this._screenAfterLogin && this._screenAfterLogin.screen) { + if (this.screenAfterLogin && this.screenAfterLogin.screen) { this.showScreen( - this._screenAfterLogin.screen, - this._screenAfterLogin.params, + this.screenAfterLogin.screen, + this.screenAfterLogin.params, ); - this._screenAfterLogin = null; + this.screenAfterLogin = null; } else if (localStorage && localStorage.getItem('mx_last_room_id')) { // Before defaulting to directory, show the last viewed room - this._viewLastRoom(); + this.viewLastRoom(); } else { if (MatrixClientPeg.get().isGuest()) { dis.dispatch({action: 'view_welcome_page'}); @@ -1233,7 +1272,7 @@ export default class MatrixChat extends React.PureComponent { } } - _viewLastRoom() { + private viewLastRoom() { dis.dispatch({ action: 'view_room', room_id: localStorage.getItem('mx_last_room_id'), @@ -1243,7 +1282,7 @@ export default class MatrixChat extends React.PureComponent { /** * Called when the session is logged out */ - _onLoggedOut() { + private onLoggedOut() { this.notifyNewScreen('login'); this.setStateForNewView({ view: VIEWS.LOGIN, @@ -1252,15 +1291,15 @@ export default class MatrixChat extends React.PureComponent { currentRoomId: null, }); this.subTitleStatus = ''; - this._setPageSubtitle(); + this.setPageSubtitle(); ThemeController.isLogin = true; - this._themeWatcher.recheck(); + this.themeWatcher.recheck(); } /** * Called when the session is softly logged out */ - _onSoftLogout() { + private onSoftLogout() { this.notifyNewScreen('soft_logout'); this.setStateForNewView({ view: VIEWS.SOFT_LOGOUT, @@ -1269,16 +1308,14 @@ export default class MatrixChat extends React.PureComponent { currentRoomId: null, }); this.subTitleStatus = ''; - this._setPageSubtitle(); + this.setPageSubtitle(); } /** * Called just before the matrix client is started * (useful for setting listeners) */ - _onWillStartClient() { - const self = this; - + private onWillStartClient() { // reset the 'have completed first sync' flag, // since we're about to start the client and therefore about // to do the first sync @@ -1292,9 +1329,9 @@ export default class MatrixChat extends React.PureComponent { // particularly noticeable when there are lots of 'limited' /sync responses // such as when laptops unsleep. // https://github.com/vector-im/riot-web/issues/3307#issuecomment-282895568 - cli.setCanResetTimelineCallback(function(roomId) { - console.log("Request to reset timeline in room ", roomId, " viewing:", self.state.currentRoomId); - if (roomId !== self.state.currentRoomId) { + cli.setCanResetTimelineCallback((roomId) => { + console.log("Request to reset timeline in room ", roomId, " viewing:", this.state.currentRoomId); + if (roomId !== this.state.currentRoomId) { // It is safe to remove events from rooms we are not viewing. return true; } @@ -1302,13 +1339,13 @@ export default class MatrixChat extends React.PureComponent { // this if we are not scrolled up in the view. To find out, delegate to // the timeline panel. If the timeline panel doesn't exist, then we assume // it is safe to reset the timeline. - if (!self._loggedInView || !self._loggedInView.child) { + if (!this.loggedInView.current || !this.loggedInView.current) { return true; } - return self._loggedInView.child.canResetTimelineInRoom(roomId); + return this.loggedInView.current.canResetTimelineInRoom(roomId); }); - cli.on('sync', function(state, prevState, data) { + cli.on('sync', (state, prevState, data) => { // LifecycleStore and others cannot directly subscribe to matrix client for // events because flux only allows store state changes during flux dispatches. // So dispatch directly from here. Ideally we'd use a SyncStateStore that @@ -1317,26 +1354,26 @@ export default class MatrixChat extends React.PureComponent { dis.dispatch({action: 'sync_state', prevState, state}); if (state === "ERROR" || state === "RECONNECTING") { - if (data.error instanceof Matrix.InvalidStoreError) { + if (data.error instanceof InvalidStoreError) { Lifecycle.handleInvalidStoreError(data.error); } - self.setState({syncError: data.error || true}); - } else if (self.state.syncError) { - self.setState({syncError: null}); + this.setState({syncError: data.error || true}); + } else if (this.state.syncError) { + this.setState({syncError: null}); } - self.updateStatusIndicator(state, prevState); + this.updateStatusIndicator(state, prevState); if (state === "SYNCING" && prevState === "SYNCING") { return; } console.info("MatrixClient sync state => %s", state); if (state !== "PREPARED") { return; } - self.firstSyncComplete = true; - self.firstSyncPromise.resolve(); + this.firstSyncComplete = true; + this.firstSyncPromise.resolve(); dis.dispatch({action: 'focus_composer'}); - self.setState({ + this.setState({ ready: true, showNotifierToolbar: Notifier.shouldShowToolbar(), }); @@ -1531,7 +1568,7 @@ export default class MatrixChat extends React.PureComponent { * setting up anything that requires the client to be started. * @private */ - _onClientStarted() { + private onClientStarted() { const cli = MatrixClientPeg.get(); if (cli.isCryptoEnabled()) { @@ -1552,7 +1589,7 @@ export default class MatrixChat extends React.PureComponent { } } - showScreen(screen, params) { + showScreen(screen: string, params?: {[key: string]: any}) { if (screen == 'register') { dis.dispatch({ action: 'start_registration', @@ -1571,7 +1608,7 @@ export default class MatrixChat extends React.PureComponent { } else if (screen === 'soft_logout') { if (MatrixClientPeg.get() && MatrixClientPeg.get().getUserId() && !Lifecycle.isSoftLogout()) { // Logged in - visit a room - this._viewLastRoom(); + this.viewLastRoom(); } else { // Ultimately triggers soft_logout if needed dis.dispatch({ @@ -1670,6 +1707,8 @@ export default class MatrixChat extends React.PureComponent { highlighted: Boolean(eventId), third_party_invite: thirdPartyInvite, oob_data: oobData, + room_alias: undefined, + room_id: undefined, }; if (roomString[0] == '#') { payload.room_alias = roomString; @@ -1699,22 +1738,22 @@ export default class MatrixChat extends React.PureComponent { } } - notifyNewScreen(screen) { + notifyNewScreen(screen: string) { if (this.props.onNewScreen) { this.props.onNewScreen(screen); } - this._setPageSubtitle(); + this.setPageSubtitle(); } - onAliasClick(event, alias) { + onAliasClick(event: MouseEvent, alias: string) { event.preventDefault(); dis.dispatch({action: 'view_room', room_alias: alias}); } - onUserClick(event, userId) { + onUserClick(event: MouseEvent, userId: string) { event.preventDefault(); - const member = new Matrix.RoomMember(null, userId); + const member = new RoomMember(null, userId); if (!member) { return; } dis.dispatch({ action: 'view_user', @@ -1722,12 +1761,12 @@ export default class MatrixChat extends React.PureComponent { }); } - onGroupClick(event, groupId) { + onGroupClick(event: MouseEvent, groupId: string) { event.preventDefault(); dis.dispatch({action: 'view_group', group_id: groupId}); } - onLogoutClick(event) { + onLogoutClick(event: React.MouseEvent) { dis.dispatch({ action: 'logout', }); @@ -1735,26 +1774,26 @@ export default class MatrixChat extends React.PureComponent { event.preventDefault(); } - handleResize = (e) => { + handleResize = () => { const hideLhsThreshold = 1000; const showLhsThreshold = 1000; - if (this._windowWidth > hideLhsThreshold && window.innerWidth <= hideLhsThreshold) { + if (this.windowWidth > hideLhsThreshold && window.innerWidth <= hideLhsThreshold) { dis.dispatch({ action: 'hide_left_panel' }); } - if (this._windowWidth <= showLhsThreshold && window.innerWidth > showLhsThreshold) { + if (this.windowWidth <= showLhsThreshold && window.innerWidth > showLhsThreshold) { dis.dispatch({ action: 'show_left_panel' }); } this.state.resizeNotifier.notifyWindowResized(); - this._windowWidth = window.innerWidth; + this.windowWidth = window.innerWidth; }; - _dispatchTimelineResize() { + private dispatchTimelineResize() { dis.dispatch({ action: 'timeline_resize' }); } - onRoomCreated(roomId) { + onRoomCreated(roomId: string) { dis.dispatch({ action: "view_room", room_id: roomId, @@ -1773,12 +1812,12 @@ export default class MatrixChat extends React.PureComponent { this.showScreen("forgot_password"); }; - onRegisterFlowComplete = (credentials, password) => { + onRegisterFlowComplete = (credentials: object, password: string) => { return this.onUserCompletedLoginFlow(credentials, password); }; // returns a promise which resolves to the new MatrixClient - onRegistered(credentials) { + onRegistered(credentials: object) { return Lifecycle.setLoggedIn(credentials); } @@ -1790,7 +1829,7 @@ export default class MatrixChat extends React.PureComponent { this.showScreen("settings"); }; - onVersion(current, latest, releaseNotes) { + onVersion(current: string, latest: string, releaseNotes?: string) { this.setState({ version: current, newVersion: latest, @@ -1800,7 +1839,7 @@ export default class MatrixChat extends React.PureComponent { }); } - onSendEvent(roomId, event) { + onSendEvent(roomId: string, event: MatrixEvent) { const cli = MatrixClientPeg.get(); if (!cli) { dis.dispatch({action: 'message_send_failed'}); @@ -1814,7 +1853,7 @@ export default class MatrixChat extends React.PureComponent { }); } - _setPageSubtitle(subtitle='') { + private setPageSubtitle(subtitle='') { if (this.state.currentRoomId) { const client = MatrixClientPeg.get(); const room = client && client.getRoom(this.state.currentRoomId); @@ -1827,7 +1866,7 @@ export default class MatrixChat extends React.PureComponent { document.title = `${SdkConfig.get().brand || 'Riot'} ${subtitle}`; } - updateStatusIndicator(state, prevState) { + updateStatusIndicator(state: string, prevState: string) { const notifCount = countRoomsWithNotif(MatrixClientPeg.get().getRooms()).count; if (PlatformPeg.get()) { @@ -1843,35 +1882,31 @@ export default class MatrixChat extends React.PureComponent { this.subTitleStatus += `[${notifCount}]`; } - this._setPageSubtitle(); + this.setPageSubtitle(); } onCloseAllSettings() { dis.dispatch({ action: 'close_settings' }); } - onServerConfigChange = (config) => { - this.setState({serverConfig: config}); + onServerConfigChange = (serverConfig: ValidatedServerConfig) => { + this.setState({serverConfig}); }; - _makeRegistrationUrl = (params) => { + private makeRegistrationUrl = (params: {[key: string]: string}) => { if (this.props.startingFragmentQueryParams.referrer) { params.referrer = this.props.startingFragmentQueryParams.referrer; } return this.props.makeRegistrationUrl(params); }; - _collectLoggedInView = (ref) => { - this._loggedInView = ref; - }; - - onUserCompletedLoginFlow = async (credentials, password) => { - this._accountPassword = password; + onUserCompletedLoginFlow = async (credentials: object, password: string) => { + this.accountPassword = password; // self-destruct the password after 5mins - if (this._accountPasswordTimer !== null) clearTimeout(this._accountPasswordTimer); - this._accountPasswordTimer = setTimeout(() => { - this._accountPassword = null; - this._accountPasswordTimer = null; + if (this.accountPasswordTimer !== null) clearTimeout(this.accountPasswordTimer); + this.accountPasswordTimer = setTimeout(() => { + this.accountPassword = null; + this.accountPasswordTimer = null; }, 60 * 5 * 1000); // Wait for the client to be logged in (but not started) @@ -1895,7 +1930,7 @@ export default class MatrixChat extends React.PureComponent { // because the client hasn't been started yet. const cryptoAvailable = isCryptoAvailable(); if (!cryptoAvailable) { - this._onLoggedIn(); + this.onLoggedIn(); } this.setState({ pendingInitialSync: true }); @@ -1923,7 +1958,7 @@ export default class MatrixChat extends React.PureComponent { // labs flag on. this.setStateForNewView({ view: VIEWS.E2E_SETUP }); } else { - this._onLoggedIn(); + this.onLoggedIn(); } this.setState({ pendingInitialSync: false }); @@ -1932,7 +1967,7 @@ export default class MatrixChat extends React.PureComponent { // complete security / e2e setup has finished onCompleteSecurityE2eSetupFinished = () => { - this._onLoggedIn(); + this.onLoggedIn(); }; render() { @@ -1959,7 +1994,7 @@ export default class MatrixChat extends React.PureComponent { view = ( ); } else if (this.state.view === VIEWS.POST_REGISTRATION) { @@ -1972,7 +2007,7 @@ export default class MatrixChat extends React.PureComponent { } else if (this.state.view === VIEWS.LOGGED_IN) { // store errors stop the client syncing and require user intervention, so we'll // be showing a dialog. Don't show anything else. - const isStoreError = this.state.syncError && this.state.syncError instanceof Matrix.InvalidStoreError; + const isStoreError = this.state.syncError && this.state.syncError instanceof InvalidStoreError; // `ready` and `view==LOGGED_IN` may be set before `page_type` (because the // latter is set via the dispatcher). If we don't yet have a `page_type`, @@ -1984,7 +2019,8 @@ export default class MatrixChat extends React.PureComponent { */ const LoggedInView = sdk.getComponent('structures.LoggedInView'); view = ( - (promise: Promise, timeoutValue: T, ms: numbe } export interface IDeferred { - resolve: (T) => void; + resolve: (value: T) => void; reject: (any) => void; promise: Promise; } From 54e976f5a8fae3447ede6d14879a2828e5850876 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 22 Apr 2020 13:22:33 +0100 Subject: [PATCH 4/9] delint Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/MatrixChat.tsx | 52 ++++++++++++------------ 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index 576f92408a..be2021b2ba 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -66,7 +66,7 @@ import { storeRoomAliasInCache } from '../../RoomAliasCache'; import {defer, IDeferred} from "../../utils/promise"; import ToastStore from "../../stores/ToastStore"; import * as StorageManager from "../../utils/StorageManager"; -import type LoggedInView from "./LoggedInView"; +import type LoggedInViewType from "./LoggedInView"; /** constants for MatrixChat.state.view */ export enum VIEWS { @@ -214,7 +214,7 @@ export default class MatrixChat extends React.PureComponent { private focusComposer: boolean; private subTitleStatus: string; - private readonly loggedInView: React.RefObject; + private readonly loggedInView: React.RefObject; private readonly dispatcherRef: any; private readonly themeWatcher: ThemeWatcher; @@ -823,7 +823,7 @@ export default class MatrixChat extends React.PureComponent { } let roomIndex = -1; for (let i = 0; i < allRooms.length; ++i) { - if (allRooms[i].roomId == this.state.currentRoomId) { + if (allRooms[i].roomId === this.state.currentRoomId) { roomIndex = i; break; } @@ -1194,7 +1194,7 @@ export default class MatrixChat extends React.PureComponent { // the saved sync to be loaded). const saveWelcomeUser = (ev) => { if ( - ev.getType() == 'm.direct' && + ev.getType() === 'm.direct' && ev.getContent() && ev.getContent()[this.props.config.welcomeUserId] ) { @@ -1482,12 +1482,12 @@ export default class MatrixChat extends React.PureComponent { Modal.createTrackedDialog('Crypto migrated', '', ErrorDialog, { title: _t('Old cryptography data detected'), description: _t( - "Data from an older version of Riot has been detected. "+ - "This will have caused end-to-end cryptography to malfunction "+ - "in the older version. End-to-end encrypted messages exchanged "+ - "recently whilst using the older version may not be decryptable "+ - "in this version. This may also cause messages exchanged with this "+ - "version to fail. If you experience problems, log out and back in "+ + "Data from an older version of Riot has been detected. " + + "This will have caused end-to-end cryptography to malfunction " + + "in the older version. End-to-end encrypted messages exchanged " + + "recently whilst using the older version may not be decryptable " + + "in this version. This may also cause messages exchanged with this " + + "version to fail. If you experience problems, log out and back in " + "again. To retain message history, export and re-import your keys.", ), }); @@ -1590,17 +1590,17 @@ export default class MatrixChat extends React.PureComponent { } showScreen(screen: string, params?: {[key: string]: any}) { - if (screen == 'register') { + if (screen === 'register') { dis.dispatch({ action: 'start_registration', params: params, }); - } else if (screen == 'login') { + } else if (screen === 'login') { dis.dispatch({ action: 'start_login', params: params, }); - } else if (screen == 'forgot_password') { + } else if (screen === 'forgot_password') { dis.dispatch({ action: 'start_password_recovery', params: params, @@ -1616,32 +1616,32 @@ export default class MatrixChat extends React.PureComponent { params: params, }); } - } else if (screen == 'new') { + } else if (screen === 'new') { dis.dispatch({ action: 'view_create_room', }); - } else if (screen == 'settings') { + } else if (screen === 'settings') { dis.dispatch({ action: 'view_user_settings', }); - } else if (screen == 'welcome') { + } else if (screen === 'welcome') { dis.dispatch({ action: 'view_welcome_page', }); - } else if (screen == 'home') { + } else if (screen === 'home') { dis.dispatch({ action: 'view_home_page', }); - } else if (screen == 'start') { + } else if (screen === 'start') { this.showScreen('home'); dis.dispatch({ action: 'require_registration', }); - } else if (screen == 'directory') { + } else if (screen === 'directory') { dis.dispatch({ action: 'view_room_directory', }); - } else if (screen == 'groups') { + } else if (screen === 'groups') { dis.dispatch({ action: 'view_my_groups', }); @@ -1649,11 +1649,11 @@ export default class MatrixChat extends React.PureComponent { dis.dispatch({ action: 'start_complete_security', }); - } else if (screen == 'post_registration') { + } else if (screen === 'post_registration') { dis.dispatch({ action: 'start_post_registration', }); - } else if (screen.indexOf('room/') == 0) { + } else if (screen.indexOf('room/') === 0) { // Rooms can have the following formats: // #room_alias:domain or !opaque_id:domain const room = screen.substring(5); @@ -1710,21 +1710,21 @@ export default class MatrixChat extends React.PureComponent { room_alias: undefined, room_id: undefined, }; - if (roomString[0] == '#') { + if (roomString[0] === '#') { payload.room_alias = roomString; } else { payload.room_id = roomString; } dis.dispatch(payload); - } else if (screen.indexOf('user/') == 0) { + } else if (screen.indexOf('user/') === 0) { const userId = screen.substring(5); dis.dispatch({ action: 'view_user_info', userId: userId, subAction: params.action, }); - } else if (screen.indexOf('group/') == 0) { + } else if (screen.indexOf('group/') === 0) { const groupId = screen.substring(6); // TODO: Check valid group ID @@ -1853,7 +1853,7 @@ export default class MatrixChat extends React.PureComponent { }); } - private setPageSubtitle(subtitle='') { + private setPageSubtitle(subtitle = '') { if (this.state.currentRoomId) { const client = MatrixClientPeg.get(); const room = client && client.getRoom(this.state.currentRoomId); From 4cf234197b810eff25b9f052fdb171854fd05d75 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 23 Apr 2020 10:12:50 +0100 Subject: [PATCH 5/9] iterate PR Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/MatrixChat.tsx | 82 ++++++++++++------------ 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index be2021b2ba..e117d8f76b 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -69,7 +69,7 @@ import * as StorageManager from "../../utils/StorageManager"; import type LoggedInViewType from "./LoggedInView"; /** constants for MatrixChat.state.view */ -export enum VIEWS { +export enum Views { // a special initial state which is only used at startup, while we are // trying to re-animate a matrix client or register as a guest. LOADING = 0, @@ -152,7 +152,7 @@ interface IProps { // TODO type things better interface IState { // the master view we are showing. - view: VIEWS; + view: Views; // What the LoggedInView would be showing if visible page_type?: PageTypes; // The ID of the room we're viewing. This is either populated directly @@ -193,7 +193,6 @@ interface IState { } export default class MatrixChat extends React.PureComponent { - static VIEWS = VIEWS; // we export this so that the integration tests can use it :-S static displayName = "MatrixChat"; static defaultProps = { @@ -222,7 +221,7 @@ export default class MatrixChat extends React.PureComponent { super(props, context); this.state = { - view: VIEWS.LOADING, + view: Views.LOADING, collapseLhs: false, leftDisabled: false, middleDisabled: false, @@ -538,7 +537,7 @@ export default class MatrixChat extends React.PureComponent { this.screenAfterLogin = payload.screenAfterLogin; } this.setStateForNewView({ - view: VIEWS.LOGIN, + view: Views.LOGIN, }); this.notifyNewScreen('login'); ThemeController.isLogin = true; @@ -546,12 +545,12 @@ export default class MatrixChat extends React.PureComponent { break; case 'start_post_registration': this.setState({ - view: VIEWS.POST_REGISTRATION, + view: Views.POST_REGISTRATION, }); break; case 'start_password_recovery': this.setStateForNewView({ - view: VIEWS.FORGOT_PASSWORD, + view: Views.FORGOT_PASSWORD, }); this.notifyNewScreen('forgot_password'); break; @@ -706,10 +705,10 @@ export default class MatrixChat extends React.PureComponent { case 'on_logged_in': if ( !Lifecycle.isSoftLogout() && - this.state.view !== VIEWS.LOGIN && - this.state.view !== VIEWS.REGISTER && - this.state.view !== VIEWS.COMPLETE_SECURITY && - this.state.view !== VIEWS.E2E_SETUP + this.state.view !== Views.LOGIN && + this.state.view !== Views.REGISTER && + this.state.view !== Views.COMPLETE_SECURITY && + this.state.view !== Views.E2E_SETUP ) { this.onLoggedIn(); } @@ -781,7 +780,7 @@ export default class MatrixChat extends React.PureComponent { private async startRegistration(params: {[key: string]: string}) { const newState: Partial = { - view: VIEWS.REGISTER, + view: Views.REGISTER, }; // Only honour params if they are all present, otherwise we reset @@ -914,7 +913,7 @@ export default class MatrixChat extends React.PureComponent { presentedId += "/" + roomInfo.event_id; } this.setState({ - view: VIEWS.LOGGED_IN, + view: Views.LOGGED_IN, currentRoomId: roomInfo.room_id || null, page_type: PageTypes.RoomView, thirdPartyInvite: roomInfo.third_party_invite, @@ -938,7 +937,7 @@ export default class MatrixChat extends React.PureComponent { } private viewSomethingBehindModal() { - if (this.state.view !== VIEWS.LOGGED_IN) { + if (this.state.view !== Views.LOGGED_IN) { this.viewWelcome(); return; } @@ -949,7 +948,7 @@ export default class MatrixChat extends React.PureComponent { private viewWelcome() { this.setStateForNewView({ - view: VIEWS.WELCOME, + view: Views.WELCOME, }); this.notifyNewScreen('welcome'); ThemeController.isLogin = true; @@ -959,7 +958,7 @@ export default class MatrixChat extends React.PureComponent { private viewHome() { // The home page requires the "logged in" view, so we'll set that. this.setStateForNewView({ - view: VIEWS.LOGGED_IN, + view: Views.LOGGED_IN, }); this.setPage(PageTypes.HomePage); this.notifyNewScreen('home'); @@ -1216,7 +1215,7 @@ export default class MatrixChat extends React.PureComponent { */ private async onLoggedIn() { ThemeController.isLogin = false; - this.setStateForNewView({ view: VIEWS.LOGGED_IN }); + this.setStateForNewView({ view: Views.LOGGED_IN }); // If a specific screen is set to be shown after login, show that above // all else, as it probably means the user clicked on something already. if (this.screenAfterLogin && this.screenAfterLogin.screen) { @@ -1285,7 +1284,7 @@ export default class MatrixChat extends React.PureComponent { private onLoggedOut() { this.notifyNewScreen('login'); this.setStateForNewView({ - view: VIEWS.LOGIN, + view: Views.LOGIN, ready: false, collapseLhs: false, currentRoomId: null, @@ -1302,7 +1301,7 @@ export default class MatrixChat extends React.PureComponent { private onSoftLogout() { this.notifyNewScreen('soft_logout'); this.setStateForNewView({ - view: VIEWS.SOFT_LOGOUT, + view: Views.SOFT_LOGOUT, ready: false, collapseLhs: false, currentRoomId: null, @@ -1824,7 +1823,7 @@ export default class MatrixChat extends React.PureComponent { onFinishPostRegistration = () => { // Don't confuse this with "PageType" which is the middle window to show this.setState({ - view: VIEWS.LOGGED_IN, + view: Views.LOGGED_IN, }); this.showScreen("settings"); }; @@ -1948,7 +1947,7 @@ export default class MatrixChat extends React.PureComponent { // Auto-enable cross-signing for the new session when key found in // secret storage. SettingsStore.setValue("feature_cross_signing", null, SettingLevel.DEVICE, true); - this.setStateForNewView({ view: VIEWS.COMPLETE_SECURITY }); + this.setStateForNewView({ view: Views.COMPLETE_SECURITY }); } else if ( SettingsStore.getValue("feature_cross_signing") && await cli.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing") @@ -1956,7 +1955,7 @@ export default class MatrixChat extends React.PureComponent { // This will only work if the feature is set to 'enable' in the config, // since it's too early in the lifecycle for users to have turned the // labs flag on. - this.setStateForNewView({ view: VIEWS.E2E_SETUP }); + this.setStateForNewView({ view: Views.E2E_SETUP }); } else { this.onLoggedIn(); } @@ -1975,21 +1974,21 @@ export default class MatrixChat extends React.PureComponent { let view; - if (this.state.view === VIEWS.LOADING) { + if (this.state.view === Views.LOADING) { const Spinner = sdk.getComponent('elements.Spinner'); view = (
); - } else if (this.state.view === VIEWS.COMPLETE_SECURITY) { + } else if (this.state.view === Views.COMPLETE_SECURITY) { const CompleteSecurity = sdk.getComponent('structures.auth.CompleteSecurity'); view = ( ); - } else if (this.state.view === VIEWS.E2E_SETUP) { + } else if (this.state.view === Views.E2E_SETUP) { const E2eSetup = sdk.getComponent('structures.auth.E2eSetup'); view = ( { accountPassword={this.accountPassword} /> ); - } else if (this.state.view === VIEWS.POST_REGISTRATION) { + } else if (this.state.view === Views.POST_REGISTRATION) { // needs to be before normal PageTypes as you are logged in technically const PostRegistration = sdk.getComponent('structures.auth.PostRegistration'); view = ( ); - } else if (this.state.view === VIEWS.LOGGED_IN) { + } else if (this.state.view === Views.LOGGED_IN) { // store errors stop the client syncing and require user intervention, so we'll // be showing a dialog. Don't show anything else. const isStoreError = this.state.syncError && this.state.syncError instanceof InvalidStoreError; @@ -2019,15 +2018,16 @@ export default class MatrixChat extends React.PureComponent { */ const LoggedInView = sdk.getComponent('structures.LoggedInView'); view = ( - ); } else { @@ -2049,10 +2049,10 @@ export default class MatrixChat extends React.PureComponent { ); } - } else if (this.state.view === VIEWS.WELCOME) { + } else if (this.state.view === Views.WELCOME) { const Welcome = sdk.getComponent('auth.Welcome'); view = ; - } else if (this.state.view === VIEWS.REGISTER) { + } else if (this.state.view === Views.REGISTER) { const Registration = sdk.getComponent('structures.auth.Registration'); view = ( { {...this.getServerProperties()} /> ); - } else if (this.state.view === VIEWS.FORGOT_PASSWORD) { + } else if (this.state.view === Views.FORGOT_PASSWORD) { const ForgotPassword = sdk.getComponent('structures.auth.ForgotPassword'); view = ( { {...this.getServerProperties()} /> ); - } else if (this.state.view === VIEWS.LOGIN) { + } else if (this.state.view === Views.LOGIN) { const Login = sdk.getComponent('structures.auth.Login'); view = ( { {...this.getServerProperties()} /> ); - } else if (this.state.view === VIEWS.SOFT_LOGOUT) { + } else if (this.state.view === Views.SOFT_LOGOUT) { const SoftLogout = sdk.getComponent('structures.auth.SoftLogout'); view = ( Date: Thu, 23 Apr 2020 10:44:35 +0100 Subject: [PATCH 6/9] Make Screens an enum Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/MatrixChat.tsx | 326 +++++++++++++---------- 1 file changed, 179 insertions(+), 147 deletions(-) diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index e117d8f76b..02a79f9684 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -103,6 +103,22 @@ export enum Views { SOFT_LOGOUT = 9, } +export enum Screens { + REGISTER = "register", + LOGIN = "login", + FORGOT_PASSWORD = "forgot_password", + SOFT_LOGOUT = "soft_logout", + NEW = "new", // new room + SETTINGS = "settings", + WELCOME = "welcome", + HOME = "home", + START = "start", + DIRECTORY = "directory", + GROUPS = "groups", + COMPLETE_SECURITY = "complete_security", + POST_REGISTRATION = "post_registration", +} + // Actions that are redirected through the onboarding process prior to being // re-dispatched. NOTE: some actions are non-trivial and would require // re-factoring to be included in this list in future. @@ -114,7 +130,7 @@ const ONBOARDING_FLOW_STARTERS = [ ]; interface IScreen { - screen: string; + screen: Screens | string; params?: object; } @@ -323,9 +339,9 @@ export default class MatrixChat extends React.PureComponent { // the old creds, but rather go straight to the relevant page const firstScreen = this.screenAfterLogin ? this.screenAfterLogin.screen : null; - if (firstScreen === 'login' || - firstScreen === 'register' || - firstScreen === 'forgot_password') { + if (firstScreen === Screens.LOGIN || + firstScreen === Screens.REGISTER || + firstScreen === Screens.FORGOT_PASSWORD) { this.showScreenAfterLogin(); return; } @@ -539,7 +555,7 @@ export default class MatrixChat extends React.PureComponent { this.setStateForNewView({ view: Views.LOGIN, }); - this.notifyNewScreen('login'); + this.notifyNewScreen(Screens.LOGIN); ThemeController.isLogin = true; this.themeWatcher.recheck(); break; @@ -552,7 +568,7 @@ export default class MatrixChat extends React.PureComponent { this.setStateForNewView({ view: Views.FORGOT_PASSWORD, }); - this.notifyNewScreen('forgot_password'); + this.notifyNewScreen(Screens.FORGOT_PASSWORD); break; case 'start_chat': createRoom({ @@ -803,7 +819,7 @@ export default class MatrixChat extends React.PureComponent { this.setStateForNewView(newState); ThemeController.isLogin = true; this.themeWatcher.recheck(); - this.notifyNewScreen('register'); + this.notifyNewScreen(Screens.REGISTER); } // TODO: Move to RoomViewStore @@ -1282,7 +1298,7 @@ export default class MatrixChat extends React.PureComponent { * Called when the session is logged out */ private onLoggedOut() { - this.notifyNewScreen('login'); + this.notifyNewScreen(Screens.LOGIN); this.setStateForNewView({ view: Views.LOGIN, ready: false, @@ -1299,7 +1315,7 @@ export default class MatrixChat extends React.PureComponent { * Called when the session is softly logged out */ private onSoftLogout() { - this.notifyNewScreen('soft_logout'); + this.notifyNewScreen(Screens.SOFT_LOGOUT); this.setStateForNewView({ view: Views.SOFT_LOGOUT, ready: false, @@ -1588,152 +1604,168 @@ export default class MatrixChat extends React.PureComponent { } } - showScreen(screen: string, params?: {[key: string]: any}) { - if (screen === 'register') { - dis.dispatch({ - action: 'start_registration', - params: params, - }); - } else if (screen === 'login') { - dis.dispatch({ - action: 'start_login', - params: params, - }); - } else if (screen === 'forgot_password') { - dis.dispatch({ - action: 'start_password_recovery', - params: params, - }); - } else if (screen === 'soft_logout') { - if (MatrixClientPeg.get() && MatrixClientPeg.get().getUserId() && !Lifecycle.isSoftLogout()) { - // Logged in - visit a room - this.viewLastRoom(); - } else { - // Ultimately triggers soft_logout if needed + showScreen(screen: Screens | string, params?: {[key: string]: any}) { + switch (screen) { + case Screens.REGISTER: + dis.dispatch({ + action: 'start_registration', + params: params, + }); + break; + case Screens.LOGIN: dis.dispatch({ action: 'start_login', params: params, }); - } - } else if (screen === 'new') { - dis.dispatch({ - action: 'view_create_room', - }); - } else if (screen === 'settings') { - dis.dispatch({ - action: 'view_user_settings', - }); - } else if (screen === 'welcome') { - dis.dispatch({ - action: 'view_welcome_page', - }); - } else if (screen === 'home') { - dis.dispatch({ - action: 'view_home_page', - }); - } else if (screen === 'start') { - this.showScreen('home'); - dis.dispatch({ - action: 'require_registration', - }); - } else if (screen === 'directory') { - dis.dispatch({ - action: 'view_room_directory', - }); - } else if (screen === 'groups') { - dis.dispatch({ - action: 'view_my_groups', - }); - } else if (screen === 'complete_security') { - dis.dispatch({ - action: 'start_complete_security', - }); - } else if (screen === 'post_registration') { - dis.dispatch({ - action: 'start_post_registration', - }); - } else if (screen.indexOf('room/') === 0) { - // Rooms can have the following formats: - // #room_alias:domain or !opaque_id:domain - const room = screen.substring(5); - const domainOffset = room.indexOf(':') + 1; // 0 in case room does not contain a : - let eventOffset = room.length; - // room aliases can contain slashes only look for slash after domain - if (room.substring(domainOffset).indexOf('/') > -1) { - eventOffset = domainOffset + room.substring(domainOffset).indexOf('/'); - } - const roomString = room.substring(0, eventOffset); - let eventId = room.substring(eventOffset + 1); // empty string if no event id given + break; + case Screens.FORGOT_PASSWORD: + dis.dispatch({ + action: 'start_password_recovery', + params: params, + }); + break; + case Screens.SOFT_LOGOUT: + if (MatrixClientPeg.get() && MatrixClientPeg.get().getUserId() && !Lifecycle.isSoftLogout()) { + // Logged in - visit a room + this.viewLastRoom(); + } else { + // Ultimately triggers soft_logout if needed + dis.dispatch({ + action: 'start_login', + params: params, + }); + } + break; + case Screens.NEW: + dis.dispatch({ + action: 'view_create_room', + }); + break; + case Screens.SETTINGS: + dis.dispatch({ + action: 'view_user_settings', + }); + break; + case Screens.WELCOME: + dis.dispatch({ + action: 'view_welcome_page', + }); + break; + case Screens.HOME: + dis.dispatch({ + action: 'view_home_page', + }); + break; + case Screens.START: + this.showScreen(Screens.HOME); + dis.dispatch({ + action: 'require_registration', + }); + break; + case Screens.DIRECTORY: + dis.dispatch({ + action: 'view_room_directory', + }); + break; + case Screens.GROUPS: + dis.dispatch({ + action: 'view_my_groups', + }); + break; + case Screens.COMPLETE_SECURITY: + dis.dispatch({ + action: 'start_complete_security', + }); + break; + case Screens.POST_REGISTRATION: + dis.dispatch({ + action: 'start_post_registration', + }); + break; + default: + if (screen.startsWith('room/')) { + // Rooms can have the following formats: + // #room_alias:domain or !opaque_id:domain + const room = screen.substring(5); + const domainOffset = room.indexOf(':') + 1; // 0 in case room does not contain a : + let eventOffset = room.length; + // room aliases can contain slashes only look for slash after domain + if (room.substring(domainOffset).indexOf('/') > -1) { + eventOffset = domainOffset + room.substring(domainOffset).indexOf('/'); + } + const roomString = room.substring(0, eventOffset); + let eventId = room.substring(eventOffset + 1); // empty string if no event id given - // Previously we pulled the eventID from the segments in such a way - // where if there was no eventId then we'd get undefined. However, we - // now do a splice and join to handle v3 event IDs which results in - // an empty string. To maintain our potential contract with the rest - // of the app, we coerce the eventId to be undefined where applicable. - if (!eventId) eventId = undefined; + // Previously we pulled the eventID from the segments in such a way + // where if there was no eventId then we'd get undefined. However, we + // now do a splice and join to handle v3 event IDs which results in + // an empty string. To maintain our potential contract with the rest + // of the app, we coerce the eventId to be undefined where applicable. + if (!eventId) eventId = undefined; - // TODO: Handle encoded room/event IDs: https://github.com/vector-im/riot-web/issues/9149 + // TODO: Handle encoded room/event IDs: https://github.com/vector-im/riot-web/issues/9149 - // FIXME: sort_out caseConsistency - const thirdPartyInvite = { - inviteSignUrl: params.signurl, - invitedEmail: params.email, - }; - const oobData = { - name: params.room_name, - avatarUrl: params.room_avatar_url, - inviterName: params.inviter_name, - }; + // FIXME: sort_out caseConsistency + const thirdPartyInvite = { + inviteSignUrl: params.signurl, + invitedEmail: params.email, + }; + const oobData = { + name: params.room_name, + avatarUrl: params.room_avatar_url, + inviterName: params.inviter_name, + }; - // on our URLs there might be a ?via=matrix.org or similar to help - // joins to the room succeed. We'll pass these through as an array - // to other levels. If there's just one ?via= then params.via is a - // single string. If someone does something like ?via=one.com&via=two.com - // then params.via is an array of strings. - let via = []; - if (params.via) { - if (typeof(params.via) === 'string') via = [params.via]; - else via = params.via; - } + // on our URLs there might be a ?via=matrix.org or similar to help + // joins to the room succeed. We'll pass these through as an array + // to other levels. If there's just one ?via= then params.via is a + // single string. If someone does something like ?via=one.com&via=two.com + // then params.via is an array of strings. + let via = []; + if (params.via) { + if (typeof(params.via) === 'string') via = [params.via]; + else via = params.via; + } - const payload = { - action: 'view_room', - event_id: eventId, - via_servers: via, - // If an event ID is given in the URL hash, notify RoomViewStore to mark - // it as highlighted, which will propagate to RoomView and highlight the - // associated EventTile. - highlighted: Boolean(eventId), - third_party_invite: thirdPartyInvite, - oob_data: oobData, - room_alias: undefined, - room_id: undefined, - }; - if (roomString[0] === '#') { - payload.room_alias = roomString; - } else { - payload.room_id = roomString; - } + const payload = { + action: 'view_room', + event_id: eventId, + via_servers: via, + // If an event ID is given in the URL hash, notify RoomViewStore to mark + // it as highlighted, which will propagate to RoomView and highlight the + // associated EventTile. + highlighted: Boolean(eventId), + third_party_invite: thirdPartyInvite, + oob_data: oobData, + room_alias: undefined, + room_id: undefined, + }; + if (roomString[0] === '#') { + payload.room_alias = roomString; + } else { + payload.room_id = roomString; + } - dis.dispatch(payload); - } else if (screen.indexOf('user/') === 0) { - const userId = screen.substring(5); - dis.dispatch({ - action: 'view_user_info', - userId: userId, - subAction: params.action, - }); - } else if (screen.indexOf('group/') === 0) { - const groupId = screen.substring(6); + dis.dispatch(payload); + } else if (screen.startsWith('user/')) { + const userId = screen.substring(5); + dis.dispatch({ + action: 'view_user_info', + userId: userId, + subAction: params.action, + }); + } else if (screen.startsWith('group/')) { + const groupId = screen.substring(6); - // TODO: Check valid group ID + // TODO: Check valid group ID - dis.dispatch({ - action: 'view_group', - group_id: groupId, - }); - } else { - console.info("Ignoring showScreen for '%s'", screen); + dis.dispatch({ + action: 'view_group', + group_id: groupId, + }); + } else { + console.info("Ignoring showScreen for '%s'", screen); + } } } @@ -1800,15 +1832,15 @@ export default class MatrixChat extends React.PureComponent { } onRegisterClick = () => { - this.showScreen("register"); + this.showScreen(Screens.REGISTER); }; onLoginClick = () => { - this.showScreen("login"); + this.showScreen(Screens.LOGIN); }; onForgotPasswordClick = () => { - this.showScreen("forgot_password"); + this.showScreen(Screens.FORGOT_PASSWORD); }; onRegisterFlowComplete = (credentials: object, password: string) => { @@ -1825,7 +1857,7 @@ export default class MatrixChat extends React.PureComponent { this.setState({ view: Views.LOGGED_IN, }); - this.showScreen("settings"); + this.showScreen(Screens.SETTINGS); }; onVersion(current: string, latest: string, releaseNotes?: string) { From e3ba9c9b38779c75e57cd0e7caf93a26cdaefb4f Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 23 Apr 2020 16:11:29 +0100 Subject: [PATCH 7/9] Discard unused CompatibilityPage.js in react-sdk Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- res/css/_components.scss | 1 - res/css/structures/_CompatibilityPage.scss | 19 ----- .../structures/CompatibilityPage.js | 78 ------------------- 3 files changed, 98 deletions(-) delete mode 100644 res/css/structures/_CompatibilityPage.scss delete mode 100644 src/components/structures/CompatibilityPage.js diff --git a/res/css/_components.scss b/res/css/_components.scss index 0ba2b609e8..7bf1f88ec9 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -2,7 +2,6 @@ @import "./_common.scss"; @import "./_font-sizes.scss"; @import "./structures/_AutoHideScrollbar.scss"; -@import "./structures/_CompatibilityPage.scss"; @import "./structures/_ContextualMenu.scss"; @import "./structures/_CreateRoom.scss"; @import "./structures/_CustomRoomTagPanel.scss"; diff --git a/res/css/structures/_CompatibilityPage.scss b/res/css/structures/_CompatibilityPage.scss deleted file mode 100644 index 26354ed124..0000000000 --- a/res/css/structures/_CompatibilityPage.scss +++ /dev/null @@ -1,19 +0,0 @@ -.mx_CompatibilityPage { - width: 100%; - height: 100%; - background-color: #e55; -} - -.mx_CompatibilityPage_box { - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; - margin: auto; - width: 500px; - height: 300px; - border: 1px solid; - padding: 10px; - background-color: #fcc; -} diff --git a/src/components/structures/CompatibilityPage.js b/src/components/structures/CompatibilityPage.js deleted file mode 100644 index 9a3fdb5f39..0000000000 --- a/src/components/structures/CompatibilityPage.js +++ /dev/null @@ -1,78 +0,0 @@ -/* -Copyright 2015, 2016 OpenMarket Ltd -Copyright 2019 Michael Telatynski <7t3chguy@gmail.com> -Copyright 2019 The Matrix.org Foundation C.I.C. - -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. -*/ - -import React from 'react'; -import createReactClass from 'create-react-class'; -import PropTypes from 'prop-types'; -import { _t } from '../../languageHandler'; - -export default createReactClass({ - displayName: 'CompatibilityPage', - propTypes: { - onAccept: PropTypes.func, - }, - - getDefaultProps: function() { - return { - onAccept: function() {}, // NOP - }; - }, - - onAccept: function() { - this.props.onAccept(); - }, - - render: function() { - return ( -
-
-

{ _t("Sorry, your browser is not able to run Riot.", {}, { 'b': (sub) => {sub} }) }

-

- { _t( - "Riot uses many advanced browser features, some of which are not available " + - "or experimental in your current browser.", - ) } -

-

- { _t( - 'Please install Chrome, Firefox, ' + - 'or Safari for the best experience.', - {}, - { - 'chromeLink': (sub) => {sub}, - 'firefoxLink': (sub) => {sub}, - 'safariLink': (sub) => {sub}, - }, - )} -

-

- { _t( - "With your current browser, the look and feel of the application may be " + - "completely incorrect, and some or all features may not function. " + - "If you want to try it anyway you can continue, but you are on your own in terms " + - "of any issues you may encounter!", - ) } -

- -
-
- ); - }, -}); From 531de19fa4464ea04cce372b327340339a73bb0a Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 23 Apr 2020 16:11:58 +0100 Subject: [PATCH 8/9] Revert "Discard unused CompatibilityPage.js in react-sdk" This reverts commit e3ba9c9b Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- res/css/_components.scss | 1 + res/css/structures/_CompatibilityPage.scss | 19 +++++ .../structures/CompatibilityPage.js | 78 +++++++++++++++++++ 3 files changed, 98 insertions(+) create mode 100644 res/css/structures/_CompatibilityPage.scss create mode 100644 src/components/structures/CompatibilityPage.js diff --git a/res/css/_components.scss b/res/css/_components.scss index 7bf1f88ec9..0ba2b609e8 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -2,6 +2,7 @@ @import "./_common.scss"; @import "./_font-sizes.scss"; @import "./structures/_AutoHideScrollbar.scss"; +@import "./structures/_CompatibilityPage.scss"; @import "./structures/_ContextualMenu.scss"; @import "./structures/_CreateRoom.scss"; @import "./structures/_CustomRoomTagPanel.scss"; diff --git a/res/css/structures/_CompatibilityPage.scss b/res/css/structures/_CompatibilityPage.scss new file mode 100644 index 0000000000..26354ed124 --- /dev/null +++ b/res/css/structures/_CompatibilityPage.scss @@ -0,0 +1,19 @@ +.mx_CompatibilityPage { + width: 100%; + height: 100%; + background-color: #e55; +} + +.mx_CompatibilityPage_box { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + margin: auto; + width: 500px; + height: 300px; + border: 1px solid; + padding: 10px; + background-color: #fcc; +} diff --git a/src/components/structures/CompatibilityPage.js b/src/components/structures/CompatibilityPage.js new file mode 100644 index 0000000000..9a3fdb5f39 --- /dev/null +++ b/src/components/structures/CompatibilityPage.js @@ -0,0 +1,78 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd +Copyright 2019 Michael Telatynski <7t3chguy@gmail.com> +Copyright 2019 The Matrix.org Foundation C.I.C. + +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. +*/ + +import React from 'react'; +import createReactClass from 'create-react-class'; +import PropTypes from 'prop-types'; +import { _t } from '../../languageHandler'; + +export default createReactClass({ + displayName: 'CompatibilityPage', + propTypes: { + onAccept: PropTypes.func, + }, + + getDefaultProps: function() { + return { + onAccept: function() {}, // NOP + }; + }, + + onAccept: function() { + this.props.onAccept(); + }, + + render: function() { + return ( +
+
+

{ _t("Sorry, your browser is not able to run Riot.", {}, { 'b': (sub) => {sub} }) }

+

+ { _t( + "Riot uses many advanced browser features, some of which are not available " + + "or experimental in your current browser.", + ) } +

+

+ { _t( + 'Please install Chrome, Firefox, ' + + 'or Safari for the best experience.', + {}, + { + 'chromeLink': (sub) => {sub}, + 'firefoxLink': (sub) => {sub}, + 'safariLink': (sub) => {sub}, + }, + )} +

+

+ { _t( + "With your current browser, the look and feel of the application may be " + + "completely incorrect, and some or all features may not function. " + + "If you want to try it anyway you can continue, but you are on your own in terms " + + "of any issues you may encounter!", + ) } +

+ +
+
+ ); + }, +}); From 2792988ad1a0a37ba413273128d50557e495c446 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 27 Apr 2020 23:59:07 +0100 Subject: [PATCH 9/9] Revert "Make Screens an enum" This reverts commit f6492918 Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/MatrixChat.tsx | 326 ++++++++++------------- 1 file changed, 147 insertions(+), 179 deletions(-) diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index 02a79f9684..e117d8f76b 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -103,22 +103,6 @@ export enum Views { SOFT_LOGOUT = 9, } -export enum Screens { - REGISTER = "register", - LOGIN = "login", - FORGOT_PASSWORD = "forgot_password", - SOFT_LOGOUT = "soft_logout", - NEW = "new", // new room - SETTINGS = "settings", - WELCOME = "welcome", - HOME = "home", - START = "start", - DIRECTORY = "directory", - GROUPS = "groups", - COMPLETE_SECURITY = "complete_security", - POST_REGISTRATION = "post_registration", -} - // Actions that are redirected through the onboarding process prior to being // re-dispatched. NOTE: some actions are non-trivial and would require // re-factoring to be included in this list in future. @@ -130,7 +114,7 @@ const ONBOARDING_FLOW_STARTERS = [ ]; interface IScreen { - screen: Screens | string; + screen: string; params?: object; } @@ -339,9 +323,9 @@ export default class MatrixChat extends React.PureComponent { // the old creds, but rather go straight to the relevant page const firstScreen = this.screenAfterLogin ? this.screenAfterLogin.screen : null; - if (firstScreen === Screens.LOGIN || - firstScreen === Screens.REGISTER || - firstScreen === Screens.FORGOT_PASSWORD) { + if (firstScreen === 'login' || + firstScreen === 'register' || + firstScreen === 'forgot_password') { this.showScreenAfterLogin(); return; } @@ -555,7 +539,7 @@ export default class MatrixChat extends React.PureComponent { this.setStateForNewView({ view: Views.LOGIN, }); - this.notifyNewScreen(Screens.LOGIN); + this.notifyNewScreen('login'); ThemeController.isLogin = true; this.themeWatcher.recheck(); break; @@ -568,7 +552,7 @@ export default class MatrixChat extends React.PureComponent { this.setStateForNewView({ view: Views.FORGOT_PASSWORD, }); - this.notifyNewScreen(Screens.FORGOT_PASSWORD); + this.notifyNewScreen('forgot_password'); break; case 'start_chat': createRoom({ @@ -819,7 +803,7 @@ export default class MatrixChat extends React.PureComponent { this.setStateForNewView(newState); ThemeController.isLogin = true; this.themeWatcher.recheck(); - this.notifyNewScreen(Screens.REGISTER); + this.notifyNewScreen('register'); } // TODO: Move to RoomViewStore @@ -1298,7 +1282,7 @@ export default class MatrixChat extends React.PureComponent { * Called when the session is logged out */ private onLoggedOut() { - this.notifyNewScreen(Screens.LOGIN); + this.notifyNewScreen('login'); this.setStateForNewView({ view: Views.LOGIN, ready: false, @@ -1315,7 +1299,7 @@ export default class MatrixChat extends React.PureComponent { * Called when the session is softly logged out */ private onSoftLogout() { - this.notifyNewScreen(Screens.SOFT_LOGOUT); + this.notifyNewScreen('soft_logout'); this.setStateForNewView({ view: Views.SOFT_LOGOUT, ready: false, @@ -1604,168 +1588,152 @@ export default class MatrixChat extends React.PureComponent { } } - showScreen(screen: Screens | string, params?: {[key: string]: any}) { - switch (screen) { - case Screens.REGISTER: - dis.dispatch({ - action: 'start_registration', - params: params, - }); - break; - case Screens.LOGIN: + showScreen(screen: string, params?: {[key: string]: any}) { + if (screen === 'register') { + dis.dispatch({ + action: 'start_registration', + params: params, + }); + } else if (screen === 'login') { + dis.dispatch({ + action: 'start_login', + params: params, + }); + } else if (screen === 'forgot_password') { + dis.dispatch({ + action: 'start_password_recovery', + params: params, + }); + } else if (screen === 'soft_logout') { + if (MatrixClientPeg.get() && MatrixClientPeg.get().getUserId() && !Lifecycle.isSoftLogout()) { + // Logged in - visit a room + this.viewLastRoom(); + } else { + // Ultimately triggers soft_logout if needed dis.dispatch({ action: 'start_login', params: params, }); - break; - case Screens.FORGOT_PASSWORD: - dis.dispatch({ - action: 'start_password_recovery', - params: params, - }); - break; - case Screens.SOFT_LOGOUT: - if (MatrixClientPeg.get() && MatrixClientPeg.get().getUserId() && !Lifecycle.isSoftLogout()) { - // Logged in - visit a room - this.viewLastRoom(); - } else { - // Ultimately triggers soft_logout if needed - dis.dispatch({ - action: 'start_login', - params: params, - }); - } - break; - case Screens.NEW: - dis.dispatch({ - action: 'view_create_room', - }); - break; - case Screens.SETTINGS: - dis.dispatch({ - action: 'view_user_settings', - }); - break; - case Screens.WELCOME: - dis.dispatch({ - action: 'view_welcome_page', - }); - break; - case Screens.HOME: - dis.dispatch({ - action: 'view_home_page', - }); - break; - case Screens.START: - this.showScreen(Screens.HOME); - dis.dispatch({ - action: 'require_registration', - }); - break; - case Screens.DIRECTORY: - dis.dispatch({ - action: 'view_room_directory', - }); - break; - case Screens.GROUPS: - dis.dispatch({ - action: 'view_my_groups', - }); - break; - case Screens.COMPLETE_SECURITY: - dis.dispatch({ - action: 'start_complete_security', - }); - break; - case Screens.POST_REGISTRATION: - dis.dispatch({ - action: 'start_post_registration', - }); - break; - default: - if (screen.startsWith('room/')) { - // Rooms can have the following formats: - // #room_alias:domain or !opaque_id:domain - const room = screen.substring(5); - const domainOffset = room.indexOf(':') + 1; // 0 in case room does not contain a : - let eventOffset = room.length; - // room aliases can contain slashes only look for slash after domain - if (room.substring(domainOffset).indexOf('/') > -1) { - eventOffset = domainOffset + room.substring(domainOffset).indexOf('/'); - } - const roomString = room.substring(0, eventOffset); - let eventId = room.substring(eventOffset + 1); // empty string if no event id given + } + } else if (screen === 'new') { + dis.dispatch({ + action: 'view_create_room', + }); + } else if (screen === 'settings') { + dis.dispatch({ + action: 'view_user_settings', + }); + } else if (screen === 'welcome') { + dis.dispatch({ + action: 'view_welcome_page', + }); + } else if (screen === 'home') { + dis.dispatch({ + action: 'view_home_page', + }); + } else if (screen === 'start') { + this.showScreen('home'); + dis.dispatch({ + action: 'require_registration', + }); + } else if (screen === 'directory') { + dis.dispatch({ + action: 'view_room_directory', + }); + } else if (screen === 'groups') { + dis.dispatch({ + action: 'view_my_groups', + }); + } else if (screen === 'complete_security') { + dis.dispatch({ + action: 'start_complete_security', + }); + } else if (screen === 'post_registration') { + dis.dispatch({ + action: 'start_post_registration', + }); + } else if (screen.indexOf('room/') === 0) { + // Rooms can have the following formats: + // #room_alias:domain or !opaque_id:domain + const room = screen.substring(5); + const domainOffset = room.indexOf(':') + 1; // 0 in case room does not contain a : + let eventOffset = room.length; + // room aliases can contain slashes only look for slash after domain + if (room.substring(domainOffset).indexOf('/') > -1) { + eventOffset = domainOffset + room.substring(domainOffset).indexOf('/'); + } + const roomString = room.substring(0, eventOffset); + let eventId = room.substring(eventOffset + 1); // empty string if no event id given - // Previously we pulled the eventID from the segments in such a way - // where if there was no eventId then we'd get undefined. However, we - // now do a splice and join to handle v3 event IDs which results in - // an empty string. To maintain our potential contract with the rest - // of the app, we coerce the eventId to be undefined where applicable. - if (!eventId) eventId = undefined; + // Previously we pulled the eventID from the segments in such a way + // where if there was no eventId then we'd get undefined. However, we + // now do a splice and join to handle v3 event IDs which results in + // an empty string. To maintain our potential contract with the rest + // of the app, we coerce the eventId to be undefined where applicable. + if (!eventId) eventId = undefined; - // TODO: Handle encoded room/event IDs: https://github.com/vector-im/riot-web/issues/9149 + // TODO: Handle encoded room/event IDs: https://github.com/vector-im/riot-web/issues/9149 - // FIXME: sort_out caseConsistency - const thirdPartyInvite = { - inviteSignUrl: params.signurl, - invitedEmail: params.email, - }; - const oobData = { - name: params.room_name, - avatarUrl: params.room_avatar_url, - inviterName: params.inviter_name, - }; + // FIXME: sort_out caseConsistency + const thirdPartyInvite = { + inviteSignUrl: params.signurl, + invitedEmail: params.email, + }; + const oobData = { + name: params.room_name, + avatarUrl: params.room_avatar_url, + inviterName: params.inviter_name, + }; - // on our URLs there might be a ?via=matrix.org or similar to help - // joins to the room succeed. We'll pass these through as an array - // to other levels. If there's just one ?via= then params.via is a - // single string. If someone does something like ?via=one.com&via=two.com - // then params.via is an array of strings. - let via = []; - if (params.via) { - if (typeof(params.via) === 'string') via = [params.via]; - else via = params.via; - } + // on our URLs there might be a ?via=matrix.org or similar to help + // joins to the room succeed. We'll pass these through as an array + // to other levels. If there's just one ?via= then params.via is a + // single string. If someone does something like ?via=one.com&via=two.com + // then params.via is an array of strings. + let via = []; + if (params.via) { + if (typeof(params.via) === 'string') via = [params.via]; + else via = params.via; + } - const payload = { - action: 'view_room', - event_id: eventId, - via_servers: via, - // If an event ID is given in the URL hash, notify RoomViewStore to mark - // it as highlighted, which will propagate to RoomView and highlight the - // associated EventTile. - highlighted: Boolean(eventId), - third_party_invite: thirdPartyInvite, - oob_data: oobData, - room_alias: undefined, - room_id: undefined, - }; - if (roomString[0] === '#') { - payload.room_alias = roomString; - } else { - payload.room_id = roomString; - } + const payload = { + action: 'view_room', + event_id: eventId, + via_servers: via, + // If an event ID is given in the URL hash, notify RoomViewStore to mark + // it as highlighted, which will propagate to RoomView and highlight the + // associated EventTile. + highlighted: Boolean(eventId), + third_party_invite: thirdPartyInvite, + oob_data: oobData, + room_alias: undefined, + room_id: undefined, + }; + if (roomString[0] === '#') { + payload.room_alias = roomString; + } else { + payload.room_id = roomString; + } - dis.dispatch(payload); - } else if (screen.startsWith('user/')) { - const userId = screen.substring(5); - dis.dispatch({ - action: 'view_user_info', - userId: userId, - subAction: params.action, - }); - } else if (screen.startsWith('group/')) { - const groupId = screen.substring(6); + dis.dispatch(payload); + } else if (screen.indexOf('user/') === 0) { + const userId = screen.substring(5); + dis.dispatch({ + action: 'view_user_info', + userId: userId, + subAction: params.action, + }); + } else if (screen.indexOf('group/') === 0) { + const groupId = screen.substring(6); - // TODO: Check valid group ID + // TODO: Check valid group ID - dis.dispatch({ - action: 'view_group', - group_id: groupId, - }); - } else { - console.info("Ignoring showScreen for '%s'", screen); - } + dis.dispatch({ + action: 'view_group', + group_id: groupId, + }); + } else { + console.info("Ignoring showScreen for '%s'", screen); } } @@ -1832,15 +1800,15 @@ export default class MatrixChat extends React.PureComponent { } onRegisterClick = () => { - this.showScreen(Screens.REGISTER); + this.showScreen("register"); }; onLoginClick = () => { - this.showScreen(Screens.LOGIN); + this.showScreen("login"); }; onForgotPasswordClick = () => { - this.showScreen(Screens.FORGOT_PASSWORD); + this.showScreen("forgot_password"); }; onRegisterFlowComplete = (credentials: object, password: string) => { @@ -1857,7 +1825,7 @@ export default class MatrixChat extends React.PureComponent { this.setState({ view: Views.LOGGED_IN, }); - this.showScreen(Screens.SETTINGS); + this.showScreen("settings"); }; onVersion(current: string, latest: string, releaseNotes?: string) {