diff --git a/src/components/views/dialogs/UntrustedDeviceDialog.tsx b/src/components/views/dialogs/UntrustedDeviceDialog.tsx index 8389757347..8c503e340d 100644 --- a/src/components/views/dialogs/UntrustedDeviceDialog.tsx +++ b/src/components/views/dialogs/UntrustedDeviceDialog.tsx @@ -19,7 +19,7 @@ import { User } from "matrix-js-sdk/src/models/user"; import { _t } from "../../../languageHandler"; import { MatrixClientPeg } from "../../../MatrixClientPeg"; -import E2EIcon from "../rooms/E2EIcon"; +import E2EIcon, { E2EState } from "../rooms/E2EIcon"; import AccessibleButton from "../elements/AccessibleButton"; import BaseDialog from "./BaseDialog"; import { IDialogProps } from "./IDialogProps"; @@ -47,7 +47,7 @@ const UntrustedDeviceDialog: React.FC = ({ device, user, onFinished }) = onFinished={onFinished} className="mx_UntrustedDeviceDialog" title={<> - + { _t("Not Trusted") } } > diff --git a/src/components/views/right_panel/VerificationPanel.tsx b/src/components/views/right_panel/VerificationPanel.tsx index a29bdea90b..8ed56bb2c3 100644 --- a/src/components/views/right_panel/VerificationPanel.tsx +++ b/src/components/views/right_panel/VerificationPanel.tsx @@ -28,7 +28,7 @@ import { SAS } from "matrix-js-sdk/src/crypto/verification/SAS"; import VerificationQRCode from "../elements/crypto/VerificationQRCode"; import { _t } from "../../../languageHandler"; import SdkConfig from "../../../SdkConfig"; -import E2EIcon from "../rooms/E2EIcon"; +import E2EIcon, { E2EState } from "../rooms/E2EIcon"; import { Phase } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; import Spinner from "../elements/Spinner"; import { replaceableComponent } from "../../../utils/replaceableComponent"; @@ -189,7 +189,7 @@ export default class VerificationPanel extends React.PureComponent

{ description }

- +

{ _t("Verified") }

{ description }

- + { text ?

{ text }

: null } { _t("Got it") } diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.tsx similarity index 76% rename from src/components/views/rooms/AppsDrawer.js rename to src/components/views/rooms/AppsDrawer.tsx index 2866780255..5a14e6b315 100644 --- a/src/components/views/rooms/AppsDrawer.js +++ b/src/components/views/rooms/AppsDrawer.tsx @@ -16,7 +16,6 @@ limitations under the License. */ import React from 'react'; -import PropTypes from 'prop-types'; import classNames from 'classnames'; import { Resizable } from "re-resizable"; @@ -26,8 +25,6 @@ import * as sdk from '../../../index'; import * as ScalarMessaging from '../../../ScalarMessaging'; import WidgetUtils from '../../../utils/WidgetUtils'; import WidgetEchoStore from "../../../stores/WidgetEchoStore"; -import { IntegrationManagers } from "../../../integrations/IntegrationManagers"; -import SettingsStore from "../../../settings/SettingsStore"; import ResizeNotifier from "../../../utils/ResizeNotifier"; import ResizeHandle from "../elements/ResizeHandle"; import Resizer from "../../../resizer/resizer"; @@ -37,60 +34,74 @@ import { clamp, percentageOf, percentageWithin } from "../../../utils/numbers"; import { useStateCallback } from "../../../hooks/useStateCallback"; import { replaceableComponent } from "../../../utils/replaceableComponent"; import UIStore from "../../../stores/UIStore"; +import { Room } from "matrix-js-sdk/src/models/room"; +import { IApp } from "../../../stores/WidgetStore"; +import { ActionPayload } from "../../../dispatcher/payloads"; + +interface IProps { + userId: string; + room: Room; + resizeNotifier: ResizeNotifier; + showApps?: boolean; // Should apps be rendered + maxHeight: number; +} + +interface IState { + apps: IApp[]; + resizingVertical: boolean; // true when changing the height of the apps drawer + resizingHorizontal: boolean; // true when chagning the distribution of the width between widgets + resizing: boolean; +} @replaceableComponent("views.rooms.AppsDrawer") -export default class AppsDrawer extends React.Component { - static propTypes = { - userId: PropTypes.string.isRequired, - room: PropTypes.object.isRequired, - resizeNotifier: PropTypes.instanceOf(ResizeNotifier).isRequired, - showApps: PropTypes.bool, // Should apps be rendered - }; - - static defaultProps = { +export default class AppsDrawer extends React.Component { + private resizeContainer: HTMLDivElement; + private resizer: Resizer; + private dispatcherRef: string; + public static defaultProps: Partial = { showApps: true, }; - constructor(props) { + constructor(props: IProps) { super(props); this.state = { - apps: this._getApps(), - resizingVertical: false, // true when changing the height of the apps drawer - resizingHorizontal: false, // true when chagning the distribution of the width between widgets + apps: this.getApps(), + resizingVertical: false, + resizingHorizontal: false, + resizing: false, }; - this._resizeContainer = null; - this.resizer = this._createResizer(); + this.resizer = this.createResizer(); this.props.resizeNotifier.on("isResizing", this.onIsResizing); } - componentDidMount() { + public componentDidMount(): void { ScalarMessaging.startListening(); - WidgetLayoutStore.instance.on(WidgetLayoutStore.emissionForRoom(this.props.room), this._updateApps); + WidgetLayoutStore.instance.on(WidgetLayoutStore.emissionForRoom(this.props.room), this.updateApps); this.dispatcherRef = dis.register(this.onAction); } - componentWillUnmount() { + public componentWillUnmount(): void { ScalarMessaging.stopListening(); - WidgetLayoutStore.instance.off(WidgetLayoutStore.emissionForRoom(this.props.room), this._updateApps); + WidgetLayoutStore.instance.off(WidgetLayoutStore.emissionForRoom(this.props.room), this.updateApps); if (this.dispatcherRef) dis.unregister(this.dispatcherRef); - if (this._resizeContainer) { + if (this.resizeContainer) { this.resizer.detach(); } this.props.resizeNotifier.off("isResizing", this.onIsResizing); } - onIsResizing = (resizing) => { + private onIsResizing = (resizing: boolean): void => { // This one is the vertical, ie. change height of apps drawer this.setState({ resizingVertical: resizing }); if (!resizing) { - this._relaxResizer(); + this.relaxResizer(); } }; - _createResizer() { + private createResizer(): Resizer { // This is the horizontal one, changing the distribution of the width between the app tiles // (ie. a vertical resize handle because, the handle itself is vertical...) const classNames = { @@ -100,11 +111,11 @@ export default class AppsDrawer extends React.Component { }; const collapseConfig = { onResizeStart: () => { - this._resizeContainer.classList.add("mx_AppsDrawer_resizing"); + this.resizeContainer.classList.add("mx_AppsDrawer_resizing"); this.setState({ resizingHorizontal: true }); }, onResizeStop: () => { - this._resizeContainer.classList.remove("mx_AppsDrawer_resizing"); + this.resizeContainer.classList.remove("mx_AppsDrawer_resizing"); WidgetLayoutStore.instance.setResizerDistributions( this.props.room, Container.Top, this.state.apps.slice(1).map((_, i) => this.resizer.forHandleAt(i).size), @@ -113,13 +124,13 @@ export default class AppsDrawer extends React.Component { }, }; // pass a truthy container for now, we won't call attach until we update it - const resizer = new Resizer({}, PercentageDistributor, collapseConfig); + const resizer = new Resizer(null, PercentageDistributor, collapseConfig); resizer.setClassNames(classNames); return resizer; } - _collectResizer = (ref) => { - if (this._resizeContainer) { + private collectResizer = (ref: HTMLDivElement): void => { + if (this.resizeContainer) { this.resizer.detach(); } @@ -127,22 +138,22 @@ export default class AppsDrawer extends React.Component { this.resizer.container = ref; this.resizer.attach(); } - this._resizeContainer = ref; - this._loadResizerPreferences(); + this.resizeContainer = ref; + this.loadResizerPreferences(); }; - _getAppsHash = (apps) => apps.map(app => app.id).join("~"); + private getAppsHash = (apps: IApp[]): string => apps.map(app => app.id).join("~"); - componentDidUpdate(prevProps, prevState) { + public componentDidUpdate(prevProps: IProps, prevState: IState): void { if (prevProps.userId !== this.props.userId || prevProps.room !== this.props.room) { // Room has changed, update apps - this._updateApps(); - } else if (this._getAppsHash(this.state.apps) !== this._getAppsHash(prevState.apps)) { - this._loadResizerPreferences(); + this.updateApps(); + } else if (this.getAppsHash(this.state.apps) !== this.getAppsHash(prevState.apps)) { + this.loadResizerPreferences(); } } - _relaxResizer = () => { + private relaxResizer = (): void => { const distributors = this.resizer.getDistributors(); // relax all items if they had any overconstrained flexboxes @@ -150,7 +161,7 @@ export default class AppsDrawer extends React.Component { distributors.forEach(d => d.finish()); }; - _loadResizerPreferences = () => { + private loadResizerPreferences = (): void => { const distributions = WidgetLayoutStore.instance.getResizerDistributions(this.props.room, Container.Top); if (this.state.apps && (this.state.apps.length - 1) === distributions.length) { distributions.forEach((size, i) => { @@ -168,11 +179,11 @@ export default class AppsDrawer extends React.Component { } }; - isResizing() { + private isResizing(): boolean { return this.state.resizingVertical || this.state.resizingHorizontal; } - onAction = (action) => { + private onAction = (action: ActionPayload): void => { const hideWidgetKey = this.props.room.roomId + '_hide_widget_drawer'; switch (action.action) { case 'appsDrawer': @@ -190,23 +201,15 @@ export default class AppsDrawer extends React.Component { } }; - _getApps = () => WidgetLayoutStore.instance.getContainerWidgets(this.props.room, Container.Top); + private getApps = (): IApp[] => WidgetLayoutStore.instance.getContainerWidgets(this.props.room, Container.Top); - _updateApps = () => { + private updateApps = (): void => { this.setState({ - apps: this._getApps(), + apps: this.getApps(), }); }; - _launchManageIntegrations() { - if (SettingsStore.getValue("feature_many_integration_managers")) { - IntegrationManagers.sharedInstance().openAll(); - } else { - IntegrationManagers.sharedInstance().getPrimaryManager().open(this.props.room, 'add_integ'); - } - } - - render() { + public render(): JSX.Element { if (!this.props.showApps) return
; const apps = this.state.apps.map((app, index, arr) => { @@ -257,7 +260,7 @@ export default class AppsDrawer extends React.Component { className="mx_AppsContainer_resizer" resizeNotifier={this.props.resizeNotifier} > -
+
{ apps.map((app, i) => { if (i < 1) return app; return @@ -273,7 +276,18 @@ export default class AppsDrawer extends React.Component { } } -const PersistentVResizer = ({ +interface IPersistentResizerProps { + room: Room; + minHeight: number; + maxHeight: number; + className: string; + handleWrapperClass: string; + handleClass: string; + resizeNotifier: ResizeNotifier; + children: React.ReactNode; +} + +const PersistentVResizer: React.FC = ({ room, minHeight, maxHeight, @@ -303,7 +317,7 @@ const PersistentVResizer = ({ }); return { diff --git a/src/components/views/rooms/E2EIcon.js b/src/components/views/rooms/E2EIcon.tsx similarity index 60% rename from src/components/views/rooms/E2EIcon.js rename to src/components/views/rooms/E2EIcon.tsx index 7425af6060..1a6db4606c 100644 --- a/src/components/views/rooms/E2EIcon.js +++ b/src/components/views/rooms/E2EIcon.tsx @@ -16,41 +16,51 @@ limitations under the License. */ import React, { useState } from "react"; -import PropTypes from "prop-types"; import classNames from 'classnames'; import { _t, _td } from '../../../languageHandler'; import AccessibleButton from "../elements/AccessibleButton"; import Tooltip from "../elements/Tooltip"; +import { E2EStatus } from "../../../utils/ShieldUtils"; -export const E2E_STATE = { - VERIFIED: "verified", - WARNING: "warning", - UNKNOWN: "unknown", - NORMAL: "normal", - UNAUTHENTICATED: "unauthenticated", +export enum E2EState { + Verified = "verified", + Warning = "warning", + Unknown = "unknown", + Normal = "normal", + Unauthenticated = "unauthenticated", +} + +const crossSigningUserTitles: { [key in E2EState]?: string } = { + [E2EState.Warning]: _td("This user has not verified all of their sessions."), + [E2EState.Normal]: _td("You have not verified this user."), + [E2EState.Verified]: _td("You have verified this user. This user has verified all of their sessions."), +}; +const crossSigningRoomTitles: { [key in E2EState]?: string } = { + [E2EState.Warning]: _td("Someone is using an unknown session"), + [E2EState.Normal]: _td("This room is end-to-end encrypted"), + [E2EState.Verified]: _td("Everyone in this room is verified"), }; -const crossSigningUserTitles = { - [E2E_STATE.WARNING]: _td("This user has not verified all of their sessions."), - [E2E_STATE.NORMAL]: _td("You have not verified this user."), - [E2E_STATE.VERIFIED]: _td("You have verified this user. This user has verified all of their sessions."), -}; -const crossSigningRoomTitles = { - [E2E_STATE.WARNING]: _td("Someone is using an unknown session"), - [E2E_STATE.NORMAL]: _td("This room is end-to-end encrypted"), - [E2E_STATE.VERIFIED]: _td("Everyone in this room is verified"), -}; +interface IProps { + isUser?: boolean; + status?: E2EState | E2EStatus; + className?: string; + size?: number; + onClick?: () => void; + hideTooltip?: boolean; + bordered?: boolean; +} -const E2EIcon = ({ isUser, status, className, size, onClick, hideTooltip, bordered }) => { +const E2EIcon: React.FC = ({ isUser, status, className, size, onClick, hideTooltip, bordered }) => { const [hover, setHover] = useState(false); const classes = classNames({ mx_E2EIcon: true, mx_E2EIcon_bordered: bordered, - mx_E2EIcon_warning: status === E2E_STATE.WARNING, - mx_E2EIcon_normal: status === E2E_STATE.NORMAL, - mx_E2EIcon_verified: status === E2E_STATE.VERIFIED, + mx_E2EIcon_warning: status === E2EState.Warning, + mx_E2EIcon_normal: status === E2EState.Normal, + mx_E2EIcon_verified: status === E2EState.Verified, }, className); let e2eTitle; @@ -92,12 +102,4 @@ const E2EIcon = ({ isUser, status, className, size, onClick, hideTooltip, border
; }; -E2EIcon.propTypes = { - isUser: PropTypes.bool, - status: PropTypes.oneOf(Object.values(E2E_STATE)), - className: PropTypes.string, - size: PropTypes.number, - onClick: PropTypes.func, -}; - export default E2EIcon; diff --git a/src/components/views/rooms/EntityTile.tsx b/src/components/views/rooms/EntityTile.tsx index 88c54468d8..d7dd4b1092 100644 --- a/src/components/views/rooms/EntityTile.tsx +++ b/src/components/views/rooms/EntityTile.tsx @@ -20,7 +20,7 @@ import React from 'react'; import AccessibleButton from '../elements/AccessibleButton'; import { _td } from '../../../languageHandler'; import classNames from "classnames"; -import E2EIcon from './E2EIcon'; +import E2EIcon, { E2EState } from './E2EIcon'; import { replaceableComponent } from "../../../utils/replaceableComponent"; import BaseAvatar from '../avatars/BaseAvatar'; import PresenceLabel from "./PresenceLabel"; @@ -75,7 +75,7 @@ interface IProps { suppressOnHover?: boolean; showPresence?: boolean; subtextLabel?: string; - e2eStatus?: string; + e2eStatus?: E2EState; powerStatus?: PowerStatus; } diff --git a/src/components/views/rooms/EventTile.tsx b/src/components/views/rooms/EventTile.tsx index 671d925638..e1f0eb5368 100644 --- a/src/components/views/rooms/EventTile.tsx +++ b/src/components/views/rooms/EventTile.tsx @@ -33,7 +33,7 @@ import { formatTime } from "../../../DateUtils"; import { MatrixClientPeg } from '../../../MatrixClientPeg'; import { ALL_RULE_TYPES } from "../../../mjolnir/BanList"; import MatrixClientContext from "../../../contexts/MatrixClientContext"; -import { E2E_STATE } from "./E2EIcon"; +import { E2EState } from "./E2EIcon"; import { toRem } from "../../../utils/units"; import { WidgetType } from "../../../widgets/WidgetType"; import RoomAvatar from "../avatars/RoomAvatar"; @@ -605,7 +605,7 @@ export default class EventTile extends React.Component { if (encryptionInfo.mismatchedSender) { // something definitely wrong is going on here this.setState({ - verified: E2E_STATE.WARNING, + verified: E2EState.Warning, }, this.props.onHeightChanged); // Decryption may have caused a change in size return; } @@ -613,7 +613,7 @@ export default class EventTile extends React.Component { if (!userTrust.isCrossSigningVerified()) { // user is not verified, so default to everything is normal this.setState({ - verified: E2E_STATE.NORMAL, + verified: E2EState.Normal, }, this.props.onHeightChanged); // Decryption may have caused a change in size return; } @@ -623,27 +623,27 @@ export default class EventTile extends React.Component { ); if (!eventSenderTrust) { this.setState({ - verified: E2E_STATE.UNKNOWN, + verified: E2EState.Unknown, }, this.props.onHeightChanged); // Decryption may have caused a change in size return; } if (!eventSenderTrust.isVerified()) { this.setState({ - verified: E2E_STATE.WARNING, + verified: E2EState.Warning, }, this.props.onHeightChanged); // Decryption may have caused a change in size return; } if (!encryptionInfo.authenticated) { this.setState({ - verified: E2E_STATE.UNAUTHENTICATED, + verified: E2EState.Unauthenticated, }, this.props.onHeightChanged); // Decryption may have caused a change in size return; } this.setState({ - verified: E2E_STATE.VERIFIED, + verified: E2EState.Verified, }, this.props.onHeightChanged); // Decryption may have caused a change in size } @@ -850,13 +850,13 @@ export default class EventTile extends React.Component { // event is encrypted, display padlock corresponding to whether or not it is verified if (ev.isEncrypted()) { - if (this.state.verified === E2E_STATE.NORMAL) { + if (this.state.verified === E2EState.Normal) { return; // no icon if we've not even cross-signed the user - } else if (this.state.verified === E2E_STATE.VERIFIED) { + } else if (this.state.verified === E2EState.Verified) { return; // no icon for verified - } else if (this.state.verified === E2E_STATE.UNAUTHENTICATED) { + } else if (this.state.verified === E2EState.Unauthenticated) { return (); - } else if (this.state.verified === E2E_STATE.UNKNOWN) { + } else if (this.state.verified === E2EState.Unknown) { return (); } else { return (); @@ -961,9 +961,9 @@ export default class EventTile extends React.Component { mx_EventTile_lastInSection: this.props.lastInSection, mx_EventTile_contextual: this.props.contextual, mx_EventTile_actionBarFocused: this.state.actionBarFocused, - mx_EventTile_verified: !isBubbleMessage && this.state.verified === E2E_STATE.VERIFIED, - mx_EventTile_unverified: !isBubbleMessage && this.state.verified === E2E_STATE.WARNING, - mx_EventTile_unknown: !isBubbleMessage && this.state.verified === E2E_STATE.UNKNOWN, + mx_EventTile_verified: !isBubbleMessage && this.state.verified === E2EState.Verified, + mx_EventTile_unverified: !isBubbleMessage && this.state.verified === E2EState.Warning, + mx_EventTile_unknown: !isBubbleMessage && this.state.verified === E2EState.Unknown, mx_EventTile_bad: isEncryptionFailure, mx_EventTile_emote: msgtype === 'm.emote', mx_EventTile_noSender: this.props.hideSender, diff --git a/src/resizer/resizer.ts b/src/resizer/resizer.ts index 0db13e1af5..aa1fb09e0c 100644 --- a/src/resizer/resizer.ts +++ b/src/resizer/resizer.ts @@ -61,10 +61,6 @@ export default class Resizer { }, public readonly config?: C, ) { - if (!container) { - throw new Error("Resizer requires a non-null `container` arg"); - } - this.classNames = { handle: "resizer-handle", reverse: "resizer-reverse",