Merge pull request #6839 from SimonBrandner/task/rooms-ts

Convert `/src/components/views/rooms` to TS
pull/21833/head
Travis Ralston 2021-09-21 09:13:23 -06:00 committed by GitHub
commit a655dde5eb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 123 additions and 111 deletions

View File

@ -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<IProps> = ({ device, user, onFinished }) =
onFinished={onFinished}
className="mx_UntrustedDeviceDialog"
title={<>
<E2EIcon status="warning" size={24} hideTooltip={true} />
<E2EIcon status={E2EState.Warning} size={24} hideTooltip={true} />
{ _t("Not Trusted") }
</>}
>

View File

@ -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<IProps, IStat
// Element Web doesn't support scanning yet, so assume here we're the client being scanned.
body = <React.Fragment>
<p>{ description }</p>
<E2EIcon isUser={true} status="verified" size={128} hideTooltip={true} />
<E2EIcon isUser={true} status={E2EState.Verified} size={128} hideTooltip={true} />
<div className="mx_VerificationPanel_reciprocateButtons">
<AccessibleButton
kind="danger"
@ -252,7 +252,7 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
<div className="mx_UserInfo_container mx_VerificationPanel_verified_section">
<h3>{ _t("Verified") }</h3>
<p>{ description }</p>
<E2EIcon isUser={true} status="verified" size={128} hideTooltip={true} />
<E2EIcon isUser={true} status={E2EState.Verified} size={128} hideTooltip={true} />
{ text ? <p>{ text }</p> : null }
<AccessibleButton kind="primary" className="mx_UserInfo_wideButton" onClick={this.props.onClose}>
{ _t("Got it") }

View File

@ -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<IProps, IState> {
private resizeContainer: HTMLDivElement;
private resizer: Resizer;
private dispatcherRef: string;
public static defaultProps: Partial<IProps> = {
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 <div />;
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}
>
<div className="mx_AppsContainer" ref={this._collectResizer}>
<div className="mx_AppsContainer" ref={this.collectResizer}>
{ apps.map((app, i) => {
if (i < 1) return app;
return <React.Fragment key={app.key}>
@ -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<IPersistentResizerProps> = ({
room,
minHeight,
maxHeight,
@ -303,7 +317,7 @@ const PersistentVResizer = ({
});
return <Resizable
size={{ height: Math.min(height, maxHeight) }}
size={{ height: Math.min(height, maxHeight), width: null }}
minHeight={minHeight}
maxHeight={maxHeight}
onResizeStart={() => {

View File

@ -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<IProps> = ({ 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
</div>;
};
E2EIcon.propTypes = {
isUser: PropTypes.bool,
status: PropTypes.oneOf(Object.values(E2E_STATE)),
className: PropTypes.string,
size: PropTypes.number,
onClick: PropTypes.func,
};
export default E2EIcon;

View File

@ -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;
}

View File

@ -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<IProps, IState> {
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<IProps, IState> {
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<IProps, IState> {
);
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<IProps, IState> {
// 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 (<E2ePadlockUnauthenticated />);
} else if (this.state.verified === E2E_STATE.UNKNOWN) {
} else if (this.state.verified === E2EState.Unknown) {
return (<E2ePadlockUnknown />);
} else {
return (<E2ePadlockUnverified />);
@ -961,9 +961,9 @@ export default class EventTile extends React.Component<IProps, IState> {
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,

View File

@ -61,10 +61,6 @@ export default class Resizer<C extends IConfig = IConfig> {
},
public readonly config?: C,
) {
if (!container) {
throw new Error("Resizer requires a non-null `container` arg");
}
this.classNames = {
handle: "resizer-handle",
reverse: "resizer-reverse",