From fb6a6370e7e37c3c5eef48c3208b3cb9d5915b22 Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Sat, 14 Aug 2021 11:17:19 +0200 Subject: [PATCH] Migrate DevicesPanel to TypeScript --- .../{DevicesPanel.js => DevicesPanel.tsx} | 89 +++++++++---------- 1 file changed, 41 insertions(+), 48 deletions(-) rename src/components/views/settings/{DevicesPanel.js => DevicesPanel.tsx} (82%) diff --git a/src/components/views/settings/DevicesPanel.js b/src/components/views/settings/DevicesPanel.tsx similarity index 82% rename from src/components/views/settings/DevicesPanel.js rename to src/components/views/settings/DevicesPanel.tsx index 0f052332ee..6fd7f90c8e 100644 --- a/src/components/views/settings/DevicesPanel.js +++ b/src/components/views/settings/DevicesPanel.tsx @@ -1,6 +1,7 @@ /* Copyright 2016 OpenMarket Ltd Copyright 2019 The Matrix.org Foundation C.I.C. +Copyright 2021 New Vector Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,8 +17,8 @@ limitations under the License. */ import React from 'react'; -import PropTypes from 'prop-types'; import classNames from 'classnames'; +import { IMyDevice } from "matrix-js-sdk/src/client"; import * as sdk from '../../../index'; import { MatrixClientPeg } from '../../../MatrixClientPeg'; @@ -26,42 +27,37 @@ import Modal from '../../../Modal'; import { SSOAuthEntry } from "../auth/InteractiveAuthEntryComponents"; import { replaceableComponent } from "../../../utils/replaceableComponent"; +interface IProps { + className?: string; +} + +interface IState { + devices: IMyDevice[]; + deviceLoadError?: string; + selectedDevices?: string[]; + deleting?: boolean; +} + @replaceableComponent("views.settings.DevicesPanel") -export default class DevicesPanel extends React.Component { - constructor(props) { - super(props); +export default class DevicesPanel extends React.Component { + private unmounted = false; - this.state = { - devices: undefined, - deviceLoadError: undefined, - - selectedDevices: [], - deleting: false, - }; - - this._unmounted = false; - - this._renderDevice = this._renderDevice.bind(this); - this._onDeviceSelectionToggled = this._onDeviceSelectionToggled.bind(this); - this._onDeleteClick = this._onDeleteClick.bind(this); + public componentDidMount(): void { + this.loadDevices(); } - componentDidMount() { - this._loadDevices(); + public componentWillUnmount(): void { + this.unmounted = true; } - componentWillUnmount() { - this._unmounted = true; - } - - _loadDevices() { + private loadDevices(): void { MatrixClientPeg.get().getDevices().then( (resp) => { - if (this._unmounted) { return; } + if (this.unmounted) { return; } this.setState({ devices: resp.devices || [] }); }, (error) => { - if (this._unmounted) { return; } + if (this.unmounted) { return; } let errtxt; if (error.httpStatus == 404) { // 404 probably means the HS doesn't yet support the API. @@ -79,7 +75,7 @@ export default class DevicesPanel extends React.Component { * compare two devices, sorting from most-recently-seen to least-recently-seen * (and then, for stability, by device id) */ - _deviceCompare(a, b) { + private deviceCompare(a: IMyDevice, b: IMyDevice): number { // return < 0 if a comes before b, > 0 if a comes after b. const lastSeenDelta = (b.last_seen_ts || 0) - (a.last_seen_ts || 0); @@ -91,8 +87,8 @@ export default class DevicesPanel extends React.Component { return (idA < idB) ? -1 : (idA > idB) ? 1 : 0; } - _onDeviceSelectionToggled(device) { - if (this._unmounted) { return; } + private onDeviceSelectionToggled = (device: IMyDevice): void => { + if (this.unmounted) { return; } const deviceId = device.device_id; this.setState((state, props) => { @@ -108,15 +104,15 @@ export default class DevicesPanel extends React.Component { return { selectedDevices }; }); - } + }; - _onDeleteClick() { + private onDeleteClick = (): void => { this.setState({ deleting: true, }); - this._makeDeleteRequest(null).catch((error) => { - if (this._unmounted) { return; } + this.makeDeleteRequest(null).catch((error) => { + if (this.unmounted) { return; } if (error.httpStatus !== 401 || !error.data || !error.data.flows) { // doesn't look like an interactive-auth failure throw error; @@ -148,7 +144,7 @@ export default class DevicesPanel extends React.Component { title: _t("Authentication"), matrixClient: MatrixClientPeg.get(), authData: error.data, - makeRequest: this._makeDeleteRequest.bind(this), + makeRequest: this.makeDeleteRequest.bind(this), aestheticsForStagePhases: { [SSOAuthEntry.LOGIN_TYPE]: dialogAesthetics, [SSOAuthEntry.UNSTABLE_LOGIN_TYPE]: dialogAesthetics, @@ -156,15 +152,16 @@ export default class DevicesPanel extends React.Component { }); }).catch((e) => { console.error("Error deleting sessions", e); - if (this._unmounted) { return; } + if (this.unmounted) { return; } }).finally(() => { this.setState({ deleting: false, }); }); - } + }; - _makeDeleteRequest(auth) { + // TODO: proper typing for auth + private makeDeleteRequest(auth?: any): Promise { return MatrixClientPeg.get().deleteMultipleDevices(this.state.selectedDevices, auth).then( () => { // Remove the deleted devices from `devices`, reset selection to [] @@ -178,17 +175,17 @@ export default class DevicesPanel extends React.Component { ); } - _renderDevice(device) { + private renderDevice = (device: IMyDevice): JSX.Element => { const DevicesPanelEntry = sdk.getComponent('settings.DevicesPanelEntry'); return ; - } + }; - render() { + public render(): JSX.Element { const Spinner = sdk.getComponent("elements.Spinner"); const AccessibleButton = sdk.getComponent("elements.AccessibleButton"); @@ -208,11 +205,11 @@ export default class DevicesPanel extends React.Component { return ; } - devices.sort(this._deviceCompare); + devices.sort(this.deviceCompare); const deleteButton = this.state.deleting ? : - + { _t("Delete %(count)s sessions", { count: this.state.selectedDevices.length }) } ; @@ -227,12 +224,8 @@ export default class DevicesPanel extends React.Component { { this.state.selectedDevices.length > 0 ? deleteButton : null } - { devices.map(this._renderDevice) } + { devices.map(this.renderDevice) } ); } } - -DevicesPanel.propTypes = { - className: PropTypes.string, -};