Convert view_user dispatch to prove the conversion works
This is a relatively obvious dispatch action that doesn't require a lot of complicated type definitions, so should be a good candidate to prove the thing works. If for some reason the thing stops working, we've done something wrong. This also adds a bit of generic types to the dispatch call so we don't confuse the tsx parser by using `dis.dispatch(<ViewUserPayload>{...})` as it thinks that's supposed to be a component. We still get type safety, and the thing remains happy with the generics approach.pull/21833/head
parent
a3b4c2dfa0
commit
a5f3318f3b
|
@ -41,6 +41,8 @@ import { parseFragment as parseHtml } from "parse5";
|
||||||
import sendBugReport from "./rageshake/submit-rageshake";
|
import sendBugReport from "./rageshake/submit-rageshake";
|
||||||
import SdkConfig from "./SdkConfig";
|
import SdkConfig from "./SdkConfig";
|
||||||
import { ensureDMExists } from "./createRoom";
|
import { ensureDMExists } from "./createRoom";
|
||||||
|
import { ViewUserPayload } from "./dispatcher/payloads/ViewUserPayload";
|
||||||
|
import { Action } from "./dispatcher/actions";
|
||||||
|
|
||||||
// XXX: workaround for https://github.com/microsoft/TypeScript/issues/31816
|
// XXX: workaround for https://github.com/microsoft/TypeScript/issues/31816
|
||||||
interface HTMLInputEvent extends Event {
|
interface HTMLInputEvent extends Event {
|
||||||
|
@ -943,8 +945,10 @@ export const Commands = [
|
||||||
}
|
}
|
||||||
|
|
||||||
const member = MatrixClientPeg.get().getRoom(roomId).getMember(userId);
|
const member = MatrixClientPeg.get().getRoom(roomId).getMember(userId);
|
||||||
dis.dispatch({
|
dis.dispatch<ViewUserPayload>({
|
||||||
action: 'view_user',
|
action: Action.ViewUser,
|
||||||
|
// XXX: We should be using a real member object and not assuming what the
|
||||||
|
// receiver wants.
|
||||||
member: member || {userId},
|
member: member || {userId},
|
||||||
});
|
});
|
||||||
return success();
|
return success();
|
||||||
|
|
|
@ -22,7 +22,6 @@ import {InvalidStoreError} from "matrix-js-sdk/src/errors";
|
||||||
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
|
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
|
||||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||||
import { isCryptoAvailable } from 'matrix-js-sdk/src/crypto';
|
import { isCryptoAvailable } from 'matrix-js-sdk/src/crypto';
|
||||||
|
|
||||||
// focus-visible is a Polyfill for the :focus-visible CSS pseudo-attribute used by _AccessibleButton.scss
|
// focus-visible is a Polyfill for the :focus-visible CSS pseudo-attribute used by _AccessibleButton.scss
|
||||||
import 'focus-visible';
|
import 'focus-visible';
|
||||||
// what-input helps improve keyboard accessibility
|
// what-input helps improve keyboard accessibility
|
||||||
|
@ -40,7 +39,7 @@ import Notifier from '../../Notifier';
|
||||||
import Modal from "../../Modal";
|
import Modal from "../../Modal";
|
||||||
import Tinter from "../../Tinter";
|
import Tinter from "../../Tinter";
|
||||||
import * as sdk from '../../index';
|
import * as sdk from '../../index';
|
||||||
import { showStartChatInviteDialog, showRoomInviteDialog } from '../../RoomInvite';
|
import { showRoomInviteDialog, showStartChatInviteDialog } from '../../RoomInvite';
|
||||||
import * as Rooms from '../../Rooms';
|
import * as Rooms from '../../Rooms';
|
||||||
import linkifyMatrix from "../../linkify-matrix";
|
import linkifyMatrix from "../../linkify-matrix";
|
||||||
import * as Lifecycle from '../../Lifecycle';
|
import * as Lifecycle from '../../Lifecycle';
|
||||||
|
@ -57,8 +56,7 @@ import ThemeController from "../../settings/controllers/ThemeController";
|
||||||
import { startAnyRegistrationFlow } from "../../Registration.js";
|
import { startAnyRegistrationFlow } from "../../Registration.js";
|
||||||
import { messageForSyncError } from '../../utils/ErrorUtils';
|
import { messageForSyncError } from '../../utils/ErrorUtils';
|
||||||
import ResizeNotifier from "../../utils/ResizeNotifier";
|
import ResizeNotifier from "../../utils/ResizeNotifier";
|
||||||
import { ValidatedServerConfig } from "../../utils/AutoDiscoveryUtils";
|
import AutoDiscoveryUtils, { ValidatedServerConfig } from "../../utils/AutoDiscoveryUtils";
|
||||||
import AutoDiscoveryUtils from "../../utils/AutoDiscoveryUtils";
|
|
||||||
import DMRoomMap from '../../utils/DMRoomMap';
|
import DMRoomMap from '../../utils/DMRoomMap';
|
||||||
import { countRoomsWithNotif } from '../../RoomNotifs';
|
import { countRoomsWithNotif } from '../../RoomNotifs';
|
||||||
import { ThemeWatcher } from "../../theme";
|
import { ThemeWatcher } from "../../theme";
|
||||||
|
@ -67,6 +65,8 @@ import {defer, IDeferred} from "../../utils/promise";
|
||||||
import ToastStore from "../../stores/ToastStore";
|
import ToastStore from "../../stores/ToastStore";
|
||||||
import * as StorageManager from "../../utils/StorageManager";
|
import * as StorageManager from "../../utils/StorageManager";
|
||||||
import type LoggedInViewType from "./LoggedInView";
|
import type LoggedInViewType from "./LoggedInView";
|
||||||
|
import { ViewUserPayload } from "../../dispatcher/payloads/ViewUserPayload";
|
||||||
|
import { Action } from "../../dispatcher/actions";
|
||||||
|
|
||||||
/** constants for MatrixChat.state.view */
|
/** constants for MatrixChat.state.view */
|
||||||
export enum Views {
|
export enum Views {
|
||||||
|
@ -1755,8 +1755,8 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
|
|
||||||
const member = new RoomMember(null, userId);
|
const member = new RoomMember(null, userId);
|
||||||
if (!member) { return; }
|
if (!member) { return; }
|
||||||
dis.dispatch({
|
dis.dispatch<ViewUserPayload>({
|
||||||
action: 'view_user',
|
action: Action.ViewUser,
|
||||||
member: member,
|
member: member,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ import SettingsStore from "../../settings/SettingsStore";
|
||||||
import {RIGHT_PANEL_PHASES, RIGHT_PANEL_PHASES_NO_ARGS} from "../../stores/RightPanelStorePhases";
|
import {RIGHT_PANEL_PHASES, RIGHT_PANEL_PHASES_NO_ARGS} from "../../stores/RightPanelStorePhases";
|
||||||
import RightPanelStore from "../../stores/RightPanelStore";
|
import RightPanelStore from "../../stores/RightPanelStore";
|
||||||
import MatrixClientContext from "../../contexts/MatrixClientContext";
|
import MatrixClientContext from "../../contexts/MatrixClientContext";
|
||||||
|
import {Action} from "../../dispatcher/actions";
|
||||||
|
|
||||||
export default class RightPanel extends React.Component {
|
export default class RightPanel extends React.Component {
|
||||||
static get propTypes() {
|
static get propTypes() {
|
||||||
|
@ -237,7 +238,7 @@ export default class RightPanel extends React.Component {
|
||||||
// within a room, so go back to the member panel if we were in the encryption panel,
|
// within a room, so go back to the member panel if we were in the encryption panel,
|
||||||
// or the member list if we were in the member panel... phew.
|
// or the member list if we were in the member panel... phew.
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: "view_user",
|
action: Action.ViewUser,
|
||||||
member: this.state.phase === RIGHT_PANEL_PHASES.EncryptionPanel ?
|
member: this.state.phase === RIGHT_PANEL_PHASES.EncryptionPanel ?
|
||||||
this.state.member : null,
|
this.state.member : null,
|
||||||
});
|
});
|
||||||
|
@ -266,7 +267,7 @@ export default class RightPanel extends React.Component {
|
||||||
if (SettingsStore.getValue("feature_cross_signing")) {
|
if (SettingsStore.getValue("feature_cross_signing")) {
|
||||||
const onClose = () => {
|
const onClose = () => {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: "view_user",
|
action: Action.ViewUser,
|
||||||
member: null,
|
member: null,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -21,6 +21,7 @@ import createReactClass from 'create-react-class';
|
||||||
import * as Avatar from '../../../Avatar';
|
import * as Avatar from '../../../Avatar';
|
||||||
import * as sdk from "../../../index";
|
import * as sdk from "../../../index";
|
||||||
import dis from "../../../dispatcher/dispatcher";
|
import dis from "../../../dispatcher/dispatcher";
|
||||||
|
import {Action} from "../../../dispatcher/actions";
|
||||||
|
|
||||||
export default createReactClass({
|
export default createReactClass({
|
||||||
displayName: 'MemberAvatar',
|
displayName: 'MemberAvatar',
|
||||||
|
@ -33,7 +34,7 @@ export default createReactClass({
|
||||||
resizeMethod: PropTypes.string,
|
resizeMethod: PropTypes.string,
|
||||||
// The onClick to give the avatar
|
// The onClick to give the avatar
|
||||||
onClick: PropTypes.func,
|
onClick: PropTypes.func,
|
||||||
// Whether the onClick of the avatar should be overriden to dispatch 'view_user'
|
// Whether the onClick of the avatar should be overriden to dispatch `Action.ViewUser`
|
||||||
viewUserOnClick: PropTypes.bool,
|
viewUserOnClick: PropTypes.bool,
|
||||||
title: PropTypes.string,
|
title: PropTypes.string,
|
||||||
},
|
},
|
||||||
|
@ -85,7 +86,7 @@ export default createReactClass({
|
||||||
if (viewUserOnClick) {
|
if (viewUserOnClick) {
|
||||||
onClick = () => {
|
onClick = () => {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'view_user',
|
action: Action.ViewUser,
|
||||||
member: this.props.member,
|
member: this.props.member,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -26,6 +26,7 @@ import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
||||||
import FlairStore from "../../../stores/FlairStore";
|
import FlairStore from "../../../stores/FlairStore";
|
||||||
import {getPrimaryPermalinkEntity} from "../../../utils/permalinks/Permalinks";
|
import {getPrimaryPermalinkEntity} from "../../../utils/permalinks/Permalinks";
|
||||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||||
|
import {Action} from "../../../dispatcher/actions";
|
||||||
|
|
||||||
// For URLs of matrix.to links in the timeline which have been reformatted by
|
// For URLs of matrix.to links in the timeline which have been reformatted by
|
||||||
// HttpUtils transformTags to relative links. This excludes event URLs (with `[^\/]*`)
|
// HttpUtils transformTags to relative links. This excludes event URLs (with `[^\/]*`)
|
||||||
|
@ -191,7 +192,7 @@ const Pill = createReactClass({
|
||||||
|
|
||||||
onUserPillClicked: function() {
|
onUserPillClicked: function() {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'view_user',
|
action: Action.ViewUser,
|
||||||
member: this.state.member,
|
member: this.state.member,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
@ -28,6 +28,7 @@ import GroupStore from '../../../stores/GroupStore';
|
||||||
import AccessibleButton from '../elements/AccessibleButton';
|
import AccessibleButton from '../elements/AccessibleButton';
|
||||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||||
import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
|
import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
|
||||||
|
import {Action} from "../../../dispatcher/actions";
|
||||||
|
|
||||||
export default createReactClass({
|
export default createReactClass({
|
||||||
displayName: 'GroupMemberInfo',
|
displayName: 'GroupMemberInfo',
|
||||||
|
@ -103,7 +104,7 @@ export default createReactClass({
|
||||||
).then(() => {
|
).then(() => {
|
||||||
// return to the user list
|
// return to the user list
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: "view_user",
|
action: Action.ViewUser,
|
||||||
member: null,
|
member: null,
|
||||||
});
|
});
|
||||||
}).catch((e) => {
|
}).catch((e) => {
|
||||||
|
@ -124,7 +125,7 @@ export default createReactClass({
|
||||||
_onCancel: function(e) {
|
_onCancel: function(e) {
|
||||||
// Go back to the user list
|
// Go back to the user list
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: "view_user",
|
action: Action.ViewUser,
|
||||||
member: null,
|
member: null,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
@ -23,6 +23,8 @@ import { _t } from '../../../languageHandler';
|
||||||
import HeaderButton from './HeaderButton';
|
import HeaderButton from './HeaderButton';
|
||||||
import HeaderButtons, {HEADER_KIND_GROUP} from './HeaderButtons';
|
import HeaderButtons, {HEADER_KIND_GROUP} from './HeaderButtons';
|
||||||
import {RIGHT_PANEL_PHASES} from "../../../stores/RightPanelStorePhases";
|
import {RIGHT_PANEL_PHASES} from "../../../stores/RightPanelStorePhases";
|
||||||
|
import {Action} from "../../../dispatcher/actions";
|
||||||
|
import {ActionPayload} from "../../../dispatcher/payloads";
|
||||||
|
|
||||||
const GROUP_PHASES = [
|
const GROUP_PHASES = [
|
||||||
RIGHT_PANEL_PHASES.GroupMemberInfo,
|
RIGHT_PANEL_PHASES.GroupMemberInfo,
|
||||||
|
@ -40,10 +42,10 @@ export default class GroupHeaderButtons extends HeaderButtons {
|
||||||
this._onRoomsClicked = this._onRoomsClicked.bind(this);
|
this._onRoomsClicked = this._onRoomsClicked.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
onAction(payload) {
|
onAction(payload: ActionPayload) {
|
||||||
super.onAction(payload);
|
super.onAction(payload);
|
||||||
|
|
||||||
if (payload.action === "view_user") {
|
if (payload.action === Action.ViewUser) {
|
||||||
if (payload.member) {
|
if (payload.member) {
|
||||||
this.setPhase(RIGHT_PANEL_PHASES.RoomMemberInfo, {member: payload.member});
|
this.setPhase(RIGHT_PANEL_PHASES.RoomMemberInfo, {member: payload.member});
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -23,6 +23,8 @@ import { _t } from '../../../languageHandler';
|
||||||
import HeaderButton from './HeaderButton';
|
import HeaderButton from './HeaderButton';
|
||||||
import HeaderButtons, {HEADER_KIND_ROOM} from './HeaderButtons';
|
import HeaderButtons, {HEADER_KIND_ROOM} from './HeaderButtons';
|
||||||
import {RIGHT_PANEL_PHASES} from "../../../stores/RightPanelStorePhases";
|
import {RIGHT_PANEL_PHASES} from "../../../stores/RightPanelStorePhases";
|
||||||
|
import {Action} from "../../../dispatcher/actions";
|
||||||
|
import {ActionPayload} from "../../../dispatcher/payloads";
|
||||||
|
|
||||||
const MEMBER_PHASES = [
|
const MEMBER_PHASES = [
|
||||||
RIGHT_PANEL_PHASES.RoomMemberList,
|
RIGHT_PANEL_PHASES.RoomMemberList,
|
||||||
|
@ -39,9 +41,9 @@ export default class RoomHeaderButtons extends HeaderButtons {
|
||||||
this._onNotificationsClicked = this._onNotificationsClicked.bind(this);
|
this._onNotificationsClicked = this._onNotificationsClicked.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
onAction(payload) {
|
onAction(payload: ActionPayload) {
|
||||||
super.onAction(payload);
|
super.onAction(payload);
|
||||||
if (payload.action === "view_user") {
|
if (payload.action === Action.ViewUser) {
|
||||||
if (payload.member) {
|
if (payload.member) {
|
||||||
this.setPhase(RIGHT_PANEL_PHASES.RoomMemberInfo, {member: payload.member});
|
this.setPhase(RIGHT_PANEL_PHASES.RoomMemberInfo, {member: payload.member});
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -44,6 +44,7 @@ import {RIGHT_PANEL_PHASES} from "../../../stores/RightPanelStorePhases";
|
||||||
import EncryptionPanel from "./EncryptionPanel";
|
import EncryptionPanel from "./EncryptionPanel";
|
||||||
import { useAsyncMemo } from '../../../hooks/useAsyncMemo';
|
import { useAsyncMemo } from '../../../hooks/useAsyncMemo';
|
||||||
import { verifyUser, legacyVerifyUser, verifyDevice } from '../../../verification';
|
import { verifyUser, legacyVerifyUser, verifyDevice } from '../../../verification';
|
||||||
|
import {Action} from "../../../dispatcher/actions";
|
||||||
|
|
||||||
const _disambiguateDevices = (devices) => {
|
const _disambiguateDevices = (devices) => {
|
||||||
const names = Object.create(null);
|
const names = Object.create(null);
|
||||||
|
@ -841,7 +842,7 @@ const GroupAdminToolsSection = ({children, groupId, groupMember, startUpdating,
|
||||||
cli.removeUserFromGroup(groupId, groupMember.userId).then(() => {
|
cli.removeUserFromGroup(groupId, groupMember.userId).then(() => {
|
||||||
// return to the user list
|
// return to the user list
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: "view_user",
|
action: Action.ViewUser,
|
||||||
member: null,
|
member: null,
|
||||||
});
|
});
|
||||||
}).catch((e) => {
|
}).catch((e) => {
|
||||||
|
|
|
@ -48,6 +48,7 @@ import E2EIcon from "./E2EIcon";
|
||||||
import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
|
import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
|
||||||
import {MatrixClientPeg} from "../../../MatrixClientPeg";
|
import {MatrixClientPeg} from "../../../MatrixClientPeg";
|
||||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||||
|
import {Action} from "../../../dispatcher/actions";
|
||||||
|
|
||||||
export default createReactClass({
|
export default createReactClass({
|
||||||
displayName: 'MemberInfo',
|
displayName: 'MemberInfo',
|
||||||
|
@ -724,7 +725,7 @@ export default createReactClass({
|
||||||
|
|
||||||
onCancel: function(e) {
|
onCancel: function(e) {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: "view_user",
|
action: Action.ViewUser,
|
||||||
member: null,
|
member: null,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
@ -23,6 +23,7 @@ import * as sdk from "../../../index";
|
||||||
import dis from "../../../dispatcher/dispatcher";
|
import dis from "../../../dispatcher/dispatcher";
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
||||||
|
import {Action} from "../../../dispatcher/actions";
|
||||||
|
|
||||||
export default createReactClass({
|
export default createReactClass({
|
||||||
displayName: 'MemberTile',
|
displayName: 'MemberTile',
|
||||||
|
@ -185,7 +186,7 @@ export default createReactClass({
|
||||||
|
|
||||||
onClick: function(e) {
|
onClick: function(e) {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'view_user',
|
action: Action.ViewUser,
|
||||||
member: this.props.member,
|
member: this.props.member,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
@ -19,5 +19,19 @@ export type DispatcherAction = Action | string;
|
||||||
|
|
||||||
export enum Action {
|
export enum Action {
|
||||||
// TODO: Populate with actual actions
|
// TODO: Populate with actual actions
|
||||||
|
// This is lazily generated as it also includes fixing a bunch of references. Work
|
||||||
|
// that we don't really want to take on in a giant chunk. We should always define
|
||||||
|
// new actions here, and ideally when we touch existing ones we take some time to
|
||||||
|
// define them correctly.
|
||||||
|
|
||||||
|
// When defining a new action, please use lower_scored_case with an optional class
|
||||||
|
// name prefix. For example, `RoomListStore.view_room` or `view_user_settings`.
|
||||||
|
// New definitions should also receive an accompanying interface in the payloads
|
||||||
|
// directory.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* View a user's profile. Should be used with a ViewUserPayload.
|
||||||
|
*/
|
||||||
|
ViewUser = "view_user",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ export class MatrixDispatcher extends Dispatcher<ActionPayload> {
|
||||||
* an operation that the browser requires user interaction
|
* an operation that the browser requires user interaction
|
||||||
* for. Default false (async).
|
* for. Default false (async).
|
||||||
*/
|
*/
|
||||||
dispatch(payload: ActionPayload, sync = false) {
|
dispatch<T extends ActionPayload>(payload: T, sync = false) {
|
||||||
if (payload instanceof AsyncActionPayload) {
|
if (payload instanceof AsyncActionPayload) {
|
||||||
payload.fn((action: ActionPayload) => {
|
payload.fn((action: ActionPayload) => {
|
||||||
this.dispatch(action, sync);
|
this.dispatch(action, sync);
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
Copyright 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.
|
||||||
|
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 { RoomMember } from "matrix-js-sdk/src/models/room-member";
|
||||||
|
import { ActionPayload } from "../payloads";
|
||||||
|
import { Action } from "../actions";
|
||||||
|
|
||||||
|
export interface ViewUserPayload extends ActionPayload {
|
||||||
|
action: Action.ViewUser,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The member to view. May be null or falsy to indicate that no member
|
||||||
|
* should be shown (hide whichever relevant components).
|
||||||
|
*/
|
||||||
|
member?: RoomMember;
|
||||||
|
}
|
Loading…
Reference in New Issue