From 76104edeca3b8ba37d4a24903fa1e0193dad56c2 Mon Sep 17 00:00:00 2001 From: Aaron Raimist Date: Tue, 24 Mar 2020 21:35:17 -0500 Subject: [PATCH 01/12] Mark room as read when escape is pressed Signed-off-by: Aaron Raimist --- src/components/structures/RoomView.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 5fd5f42f78..9405a26ab2 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -560,6 +560,12 @@ export default createReactClass({ handled = true; } break; + + case Key.ESCAPE: + this._messagePanel.forgetReadMarker(); + this.jumpToLiveTimeline(); + handled = true; + break; } if (handled) { From 69382ff8e59bc01f9ce10daaedf240a02e20fc8b Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 14 Apr 2020 15:24:41 +0100 Subject: [PATCH 02/12] Fix handler to use react bubbling rather than a native global onKeyDown Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/structures/RoomView.js | 32 +++++++++++++++++++++------ 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 9405a26ab2..6454136164 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -422,7 +422,7 @@ export default createReactClass({ } this.onResize(); - document.addEventListener("keydown", this.onKeyDown); + document.addEventListener("keydown", this.onNativeKeyDown); }, shouldComponentUpdate: function(nextProps, nextState) { @@ -500,7 +500,7 @@ export default createReactClass({ this.props.resizeNotifier.removeListener("middlePanelResized", this.onResize); } - document.removeEventListener("keydown", this.onKeyDown); + document.removeEventListener("keydown", this.onNativeKeyDown); // Remove RoomStore listener if (this._roomStoreToken) { @@ -542,7 +542,8 @@ export default createReactClass({ } }, - onKeyDown: function(ev) { + // we register global shortcuts here, they *must not conflict* with local shortcuts elsewhere or both will fire + onNativeKeyDown: function(ev) { let handled = false; const ctrlCmdOnly = isOnlyCtrlOrCmdKeyEvent(ev); @@ -560,11 +561,24 @@ export default createReactClass({ handled = true; } break; + } + if (handled) { + ev.stopPropagation(); + ev.preventDefault(); + } + }, + + onReactKeyDown: function(ev) { + let handled = false; + + switch (ev.key) { case Key.ESCAPE: - this._messagePanel.forgetReadMarker(); - this.jumpToLiveTimeline(); - handled = true; + if (!ev.altKey && !ev.ctrlKey && !ev.shiftKey && !ev.metaKey) { + this._messagePanel.forgetReadMarker(); + this.jumpToLiveTimeline(); + handled = true; + } break; } @@ -2030,9 +2044,13 @@ export default createReactClass({ mx_RoomView_timeline_rr_enabled: this.state.showReadReceipts, }); + const mainClasses = classNames("mx_RoomView", { + mx_RoomView_inCall: inCall, + }); + return ( -
+
Date: Wed, 22 Apr 2020 11:55:03 +0100 Subject: [PATCH 03/12] 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 04/12] 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 05/12] 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 06/12] 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 07/12] 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 08/12] 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 09/12] 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 10/12] 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 11/12] 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) { From d3ce4072d481cf935025fbc4f58ba53edbdcaf44 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Thu, 30 Apr 2020 15:41:49 +0100 Subject: [PATCH 12/12] Only show key backup reminder when confirmed by server to be missing The key backup reminder was being shown too eagerly in cases when we hadn't actually checked with the homeserver on key backup status. This changes to only show the reminder when we're sure a backup doesn't exist. Fixes https://github.com/vector-im/riot-web/issues/13404 --- src/components/structures/RoomView.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 179e0aa2e9..4f9c0a7c91 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -1768,7 +1768,7 @@ export default createReactClass({ const showRoomRecoveryReminder = ( SettingsStore.getValue("showRoomRecoveryReminder") && this.context.isRoomEncrypted(this.state.room.roomId) && - !this.context.getKeyBackupEnabled() + this.context.getKeyBackupEnabled() === false ); const hiddenHighlightCount = this._getHiddenHighlightCount();