diff --git a/src/DeviceListener.ts b/src/DeviceListener.ts index 486cf140aa..cf5e493992 100644 --- a/src/DeviceListener.ts +++ b/src/DeviceListener.ts @@ -17,7 +17,7 @@ limitations under the License. import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import { logger } from "matrix-js-sdk/src/logger"; import { CryptoEvent } from "matrix-js-sdk/src/crypto"; -import { ClientEvent, RoomStateEvent } from "matrix-js-sdk/src/matrix"; +import { ClientEvent, EventType, RoomStateEvent } from "matrix-js-sdk/src/matrix"; import { MatrixClientPeg } from './MatrixClientPeg'; import dis from "./dispatcher/dispatcher"; @@ -184,9 +184,7 @@ export default class DeviceListener { }; private onRoomStateEvents = (ev: MatrixEvent) => { - if (ev.getType() !== "m.room.encryption") { - return; - } + if (ev.getType() !== EventType.RoomEncryption) return; // If a room changes to encrypted, re-check as it may be our first // encrypted room. This also catches encrypted room creation as well. diff --git a/src/autocomplete/UserProvider.tsx b/src/autocomplete/UserProvider.tsx index 21b318229d..faf0eed4fb 100644 --- a/src/autocomplete/UserProvider.tsx +++ b/src/autocomplete/UserProvider.tsx @@ -61,13 +61,13 @@ export default class UserProvider extends AutocompleteProvider { }); MatrixClientPeg.get().on(RoomEvent.Timeline, this.onRoomTimeline); - MatrixClientPeg.get().on(RoomStateEvent.Members, this.onRoomStateMember); + MatrixClientPeg.get().on(RoomStateEvent.Update, this.onRoomStateUpdate); } destroy() { if (MatrixClientPeg.get()) { MatrixClientPeg.get().removeListener(RoomEvent.Timeline, this.onRoomTimeline); - MatrixClientPeg.get().removeListener(RoomStateEvent.Members, this.onRoomStateMember); + MatrixClientPeg.get().removeListener(RoomStateEvent.Update, this.onRoomStateUpdate); } } @@ -93,11 +93,9 @@ export default class UserProvider extends AutocompleteProvider { this.onUserSpoke(ev.sender); }; - private onRoomStateMember = (ev: MatrixEvent, state: RoomState, member: RoomMember) => { - // ignore members in other rooms - if (member.roomId !== this.room.roomId) { - return; - } + private onRoomStateUpdate = (state: RoomState) => { + // ignore updates in other rooms + if (state.roomId !== this.room.roomId) return; // blow away the users cache this.users = null; diff --git a/src/components/structures/GroupFilterPanel.tsx b/src/components/structures/GroupFilterPanel.tsx index 8db78f030c..c86e243fa9 100644 --- a/src/components/structures/GroupFilterPanel.tsx +++ b/src/components/structures/GroupFilterPanel.tsx @@ -17,6 +17,7 @@ limitations under the License. import React from 'react'; import classNames from 'classnames'; +import { ClientEvent } from "matrix-js-sdk/src/client"; import type { EventSubscription } from "fbemitter"; import GroupFilterOrderStore from '../../stores/GroupFilterOrderStore'; @@ -51,6 +52,7 @@ interface IGroupFilterPanelState { @replaceableComponent("structures.GroupFilterPanel") class GroupFilterPanel extends React.Component { public static contextType = MatrixClientContext; + public context!: React.ContextType; public state = { orderedTags: [], @@ -63,8 +65,8 @@ class GroupFilterPanel extends React.Component { if (this.unmounted) { @@ -82,8 +84,8 @@ class GroupFilterPanel extends React.Component { private onRoomStateEvents = (ev: MatrixEvent): void => { const serverNoticeList = RoomListStore.instance.orderedLists[DefaultTagID.ServerNotice]; - if (serverNoticeList && serverNoticeList.some(r => r.roomId === ev.getRoomId())) { + if (serverNoticeList?.some(r => r.roomId === ev.getRoomId())) { this.updateServerNoticeEvents(); } }; diff --git a/src/components/structures/MessagePanel.tsx b/src/components/structures/MessagePanel.tsx index 87131d622f..7407ff220f 100644 --- a/src/components/structures/MessagePanel.tsx +++ b/src/components/structures/MessagePanel.tsx @@ -23,7 +23,6 @@ import { MatrixEvent } from 'matrix-js-sdk/src/models/event'; import { Relations } from "matrix-js-sdk/src/models/relations"; import { logger } from 'matrix-js-sdk/src/logger'; import { RoomStateEvent } from "matrix-js-sdk/src/models/room-state"; -import { throttle } from "lodash"; import shouldHideEvent from '../../shouldHideEvent'; import { wantsDateSeparator } from '../../DateUtils'; @@ -277,13 +276,13 @@ export default class MessagePanel extends React.Component { componentDidMount() { this.calculateRoomMembersCount(); - this.props.room?.currentState.on(RoomStateEvent.Members, this.onRoomMembers); + this.props.room?.currentState.on(RoomStateEvent.Update, this.calculateRoomMembersCount); this.isMounted = true; } componentWillUnmount() { this.isMounted = false; - this.props.room?.currentState.off(RoomStateEvent.Members, this.onRoomMembers); + this.props.room?.currentState.off(RoomStateEvent.Update, this.calculateRoomMembersCount); SettingsStore.unwatchSetting(this.showTypingNotificationsWatcherRef); } @@ -314,16 +313,11 @@ export default class MessagePanel extends React.Component { return this.props.room?.getInvitedAndJoinedMemberCount() <= 2 && this.props.layout === Layout.Bubble; } - private onRoomMembers = (event: MatrixEvent): void => { - if (this.props.room && event.getRoomId() !== this.props.room.roomId) return; // different room - this.calculateRoomMembersCount(); - }; - - private calculateRoomMembersCount = throttle((): void => { + private calculateRoomMembersCount = (): void => { this.setState({ hideSender: this.shouldHideSender(), }); - }, 200, { leading: true, trailing: true }); + }; private onShowTypingNotificationsChange = (): void => { this.setState({ diff --git a/src/components/structures/RightPanel.tsx b/src/components/structures/RightPanel.tsx index 0779a9a52b..0ed9f4cee5 100644 --- a/src/components/structures/RightPanel.tsx +++ b/src/components/structures/RightPanel.tsx @@ -17,7 +17,7 @@ limitations under the License. import React from 'react'; import { Room } from "matrix-js-sdk/src/models/room"; -import { RoomState } from "matrix-js-sdk/src/models/room-state"; +import { RoomState, RoomStateEvent } from "matrix-js-sdk/src/models/room-state"; import { RoomMember } from "matrix-js-sdk/src/models/room-member"; import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import { throttle } from 'lodash'; @@ -67,6 +67,7 @@ interface IState { @replaceableComponent("structures.RightPanel") export default class RightPanel extends React.Component { static contextType = MatrixClientContext; + public context!: React.ContextType; constructor(props, context) { super(props, context); @@ -81,15 +82,12 @@ export default class RightPanel extends React.Component { }, 500, { leading: true, trailing: true }); public componentDidMount(): void { - const cli = this.context; - cli.on("RoomState.members", this.onRoomStateMember); + this.context.on(RoomStateEvent.Members, this.onRoomStateMember); RightPanelStore.instance.on(UPDATE_EVENT, this.onRightPanelStoreUpdate); } public componentWillUnmount(): void { - if (this.context) { - this.context.removeListener("RoomState.members", this.onRoomStateMember); - } + this.context?.removeListener(RoomStateEvent.Members, this.onRoomStateMember); RightPanelStore.instance.off(UPDATE_EVENT, this.onRightPanelStoreUpdate); } diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 648b505927..543ebeba50 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -22,17 +22,19 @@ limitations under the License. import React, { createRef } from 'react'; import classNames from 'classnames'; -import { IRecommendedVersion, NotificationCountType, Room } from "matrix-js-sdk/src/models/room"; -import { MatrixEvent } from "matrix-js-sdk/src/models/event"; +import { IRecommendedVersion, NotificationCountType, Room, RoomEvent } from "matrix-js-sdk/src/models/room"; +import { MatrixEvent, MatrixEventEvent } from "matrix-js-sdk/src/models/event"; import { EventSubscription } from "fbemitter"; import { ISearchResults } from 'matrix-js-sdk/src/@types/search'; import { logger } from "matrix-js-sdk/src/logger"; import { EventTimeline } from 'matrix-js-sdk/src/models/event-timeline'; import { EventType } from 'matrix-js-sdk/src/@types/event'; -import { RoomState } from 'matrix-js-sdk/src/models/room-state'; +import { RoomState, RoomStateEvent } from 'matrix-js-sdk/src/models/room-state'; import { CallState, CallType, MatrixCall } from "matrix-js-sdk/src/webrtc/call"; import { throttle } from "lodash"; import { MatrixError } from 'matrix-js-sdk/src/http-api'; +import { ClientEvent } from "matrix-js-sdk/src/client"; +import { CryptoEvent } from "matrix-js-sdk/src/crypto"; import shouldHideEvent from '../../shouldHideEvent'; import { _t } from '../../languageHandler'; @@ -230,11 +232,12 @@ export class RoomView extends React.Component { private roomViewBody = createRef(); static contextType = MatrixClientContext; + public context!: React.ContextType; - constructor(props, context) { + constructor(props: IRoomProps, context: React.ContextType) { super(props, context); - const llMembers = this.context.hasLazyLoadMembersEnabled(); + const llMembers = context.hasLazyLoadMembersEnabled(); this.state = { roomId: null, roomLoading: true, @@ -268,7 +271,7 @@ export class RoomView extends React.Component { showJoinLeaves: true, showAvatarChanges: true, showDisplaynameChanges: true, - matrixClientIsReady: this.context && this.context.isInitialSyncComplete(), + matrixClientIsReady: context?.isInitialSyncComplete(), mainSplitContentType: MainSplitContentType.Timeline, timelineRenderingType: TimelineRenderingType.Room, liveTimeline: undefined, @@ -276,19 +279,19 @@ export class RoomView extends React.Component { }; this.dispatcherRef = dis.register(this.onAction); - this.context.on("Room", this.onRoom); - this.context.on("Room.timeline", this.onRoomTimeline); - this.context.on("Room.name", this.onRoomName); - this.context.on("Room.accountData", this.onRoomAccountData); - this.context.on("RoomState.events", this.onRoomStateEvents); - this.context.on("RoomState.members", this.onRoomStateMember); - this.context.on("Room.myMembership", this.onMyMembership); - this.context.on("accountData", this.onAccountData); - this.context.on("crypto.keyBackupStatus", this.onKeyBackupStatus); - this.context.on("deviceVerificationChanged", this.onDeviceVerificationChanged); - this.context.on("userTrustStatusChanged", this.onUserVerificationChanged); - this.context.on("crossSigning.keysChanged", this.onCrossSigningKeysChanged); - this.context.on("Event.decrypted", this.onEventDecrypted); + context.on(ClientEvent.Room, this.onRoom); + context.on(RoomEvent.Timeline, this.onRoomTimeline); + context.on(RoomEvent.Name, this.onRoomName); + context.on(RoomEvent.AccountData, this.onRoomAccountData); + context.on(RoomStateEvent.Events, this.onRoomStateEvents); + context.on(RoomStateEvent.Update, this.onRoomStateUpdate); + context.on(RoomEvent.MyMembership, this.onMyMembership); + context.on(ClientEvent.AccountData, this.onAccountData); + context.on(CryptoEvent.KeyBackupStatus, this.onKeyBackupStatus); + context.on(CryptoEvent.DeviceVerificationChanged, this.onDeviceVerificationChanged); + context.on(CryptoEvent.UserTrustStatusChanged, this.onUserVerificationChanged); + context.on(CryptoEvent.KeysChanged, this.onCrossSigningKeysChanged); + context.on(MatrixEventEvent.Decrypted, this.onEventDecrypted); // Start listening for RoomViewStore updates this.roomStoreToken = RoomViewStore.addListener(this.onRoomViewStoreUpdate); @@ -704,19 +707,19 @@ export class RoomView extends React.Component { dis.unregister(this.dispatcherRef); if (this.context) { - this.context.removeListener("Room", this.onRoom); - this.context.removeListener("Room.timeline", this.onRoomTimeline); - this.context.removeListener("Room.name", this.onRoomName); - this.context.removeListener("Room.accountData", this.onRoomAccountData); - this.context.removeListener("RoomState.events", this.onRoomStateEvents); - this.context.removeListener("Room.myMembership", this.onMyMembership); - this.context.removeListener("RoomState.members", this.onRoomStateMember); - this.context.removeListener("accountData", this.onAccountData); - this.context.removeListener("crypto.keyBackupStatus", this.onKeyBackupStatus); - this.context.removeListener("deviceVerificationChanged", this.onDeviceVerificationChanged); - this.context.removeListener("userTrustStatusChanged", this.onUserVerificationChanged); - this.context.removeListener("crossSigning.keysChanged", this.onCrossSigningKeysChanged); - this.context.removeListener("Event.decrypted", this.onEventDecrypted); + this.context.removeListener(ClientEvent.Room, this.onRoom); + this.context.removeListener(RoomEvent.Timeline, this.onRoomTimeline); + this.context.removeListener(RoomEvent.Name, this.onRoomName); + this.context.removeListener(RoomEvent.AccountData, this.onRoomAccountData); + this.context.removeListener(RoomStateEvent.Events, this.onRoomStateEvents); + this.context.removeListener(RoomEvent.MyMembership, this.onMyMembership); + this.context.removeListener(RoomStateEvent.Update, this.onRoomStateUpdate); + this.context.removeListener(ClientEvent.AccountData, this.onAccountData); + this.context.removeListener(CryptoEvent.KeyBackupStatus, this.onKeyBackupStatus); + this.context.removeListener(CryptoEvent.DeviceVerificationChanged, this.onDeviceVerificationChanged); + this.context.removeListener(CryptoEvent.UserTrustStatusChanged, this.onUserVerificationChanged); + this.context.removeListener(CryptoEvent.KeysChanged, this.onCrossSigningKeysChanged); + this.context.removeListener(MatrixEventEvent.Decrypted, this.onEventDecrypted); } window.removeEventListener('beforeunload', this.onPageUnload); @@ -1097,7 +1100,7 @@ export class RoomView extends React.Component { }); }; - private onDeviceVerificationChanged = (userId: string, device: object) => { + private onDeviceVerificationChanged = (userId: string) => { const room = this.state.room; if (!room.currentState.getMember(userId)) { return; @@ -1105,7 +1108,7 @@ export class RoomView extends React.Component { this.updateE2EStatus(room); }; - private onUserVerificationChanged = (userId: string, trustStatus: object) => { + private onUserVerificationChanged = (userId: string) => { const room = this.state.room; if (!room || !room.currentState.getMember(userId)) { return; @@ -1156,9 +1159,7 @@ export class RoomView extends React.Component { private onRoomStateEvents = (ev: MatrixEvent, state: RoomState) => { // ignore if we don't have a room yet - if (!this.state.room || this.state.room.roomId !== state.roomId) { - return; - } + if (!this.state.room || this.state.room.roomId !== state.roomId) return; if (ev.getType() === EventType.RoomCanonicalAlias) { // re-view the room so MatrixChat can manage the alias in the URL properly @@ -1173,14 +1174,9 @@ export class RoomView extends React.Component { this.updatePermissions(this.state.room); }; - private onRoomStateMember = (ev: MatrixEvent, state, member) => { - // ignore if we don't have a room yet - if (!this.state.room) { - return; - } - + private onRoomStateUpdate = (state: RoomState) => { // ignore members in other rooms - if (member.roomId !== this.state.room.roomId) { + if (state.roomId !== this.state.room?.roomId) { return; } @@ -1255,7 +1251,7 @@ export class RoomView extends React.Component { private onJoinButtonClicked = () => { // If the user is a ROU, allow them to transition to a PWLU - if (this.context && this.context.isGuest()) { + if (this.context?.isGuest()) { // Join this room once the user has registered and logged in // (If we failed to peek, we may not have a valid room object.) dis.dispatch>({ @@ -1712,7 +1708,7 @@ export class RoomView extends React.Component { getHiddenHighlightCount() { const oldRoom = this.getOldRoom(); if (!oldRoom) return 0; - return oldRoom.getUnreadNotificationCount('highlight'); + return oldRoom.getUnreadNotificationCount(NotificationCountType.Highlight); } onHiddenHighlightsClick = () => { diff --git a/src/components/structures/SpaceHierarchy.tsx b/src/components/structures/SpaceHierarchy.tsx index d1331e0d96..8ce911e0d4 100644 --- a/src/components/structures/SpaceHierarchy.tsx +++ b/src/components/structures/SpaceHierarchy.tsx @@ -27,7 +27,7 @@ import React, { useRef, useState, } from "react"; -import { Room } from "matrix-js-sdk/src/models/room"; +import { Room, RoomEvent } from "matrix-js-sdk/src/models/room"; import { RoomHierarchy } from "matrix-js-sdk/src/room-hierarchy"; import { EventType, RoomType } from "matrix-js-sdk/src/@types/event"; import { IHierarchyRelation, IHierarchyRoom } from "matrix-js-sdk/src/@types/spaces"; @@ -58,7 +58,7 @@ import { Key } from "../../Keyboard"; import { IState, RovingTabIndexProvider, useRovingTabIndex } from "../../accessibility/RovingTabIndex"; import { getDisplayAliasForRoom } from "./RoomDirectory"; import MatrixClientContext from "../../contexts/MatrixClientContext"; -import { useEventEmitterState } from "../../hooks/useEventEmitter"; +import { useTypedEventEmitterState } from "../../hooks/useEventEmitter"; import { IOOBData } from "../../stores/ThreepidInviteStore"; import { awaitRoomDownSync } from "../../utils/RoomUpgrade"; import RoomViewStore from "../../stores/RoomViewStore"; @@ -99,7 +99,7 @@ const Tile: React.FC = ({ const cliRoom = cli.getRoom(room.room_id); return cliRoom?.getMyMembership() === "join" ? cliRoom : null; }); - const joinedRoomName = useEventEmitterState(joinedRoom, "Room.name", room => room?.name); + const joinedRoomName = useTypedEventEmitterState(joinedRoom, RoomEvent.Name, room => room?.name); const name = joinedRoomName || room.name || room.canonical_alias || room.aliases?.[0] || (room.room_type === RoomType.Space ? _t("Unnamed Space") : _t("Unnamed Room")); diff --git a/src/components/structures/SpaceRoomView.tsx b/src/components/structures/SpaceRoomView.tsx index 3ff5d7371c..896c296504 100644 --- a/src/components/structures/SpaceRoomView.tsx +++ b/src/components/structures/SpaceRoomView.tsx @@ -17,7 +17,7 @@ limitations under the License. import React, { RefObject, useContext, useRef, useState } from "react"; import { EventType } from "matrix-js-sdk/src/@types/event"; import { JoinRule, Preset } from "matrix-js-sdk/src/@types/partials"; -import { Room } from "matrix-js-sdk/src/models/room"; +import { Room, RoomEvent } from "matrix-js-sdk/src/models/room"; import { logger } from "matrix-js-sdk/src/logger"; import MatrixClientContext from "../../contexts/MatrixClientContext"; @@ -31,7 +31,7 @@ import { inviteMultipleToRoom, showRoomInviteDialog } from "../../RoomInvite"; import { useRoomMembers } from "../../hooks/useRoomMembers"; import createRoom, { IOpts } from "../../createRoom"; import Field from "../views/elements/Field"; -import { useEventEmitter } from "../../hooks/useEventEmitter"; +import { useTypedEventEmitter } from "../../hooks/useEventEmitter"; import withValidation from "../views/elements/Validation"; import * as Email from "../../email"; import defaultDispatcher from "../../dispatcher/dispatcher"; @@ -121,7 +121,7 @@ const RoomMemberCount = ({ room, children }) => { const useMyRoomMembership = (room: Room) => { const [membership, setMembership] = useState(room.getMyMembership()); - useEventEmitter(room, "Room.myMembership", () => { + useTypedEventEmitter(room, RoomEvent.MyMembership, () => { setMembership(room.getMyMembership()); }); return membership; @@ -790,17 +790,18 @@ const SpaceSetupPrivateInvite = ({ space, onFinished }) => { export default class SpaceRoomView extends React.PureComponent { static contextType = MatrixClientContext; + public context!: React.ContextType; private readonly creator: string; private readonly dispatcherRef: string; - constructor(props, context) { + constructor(props: IProps, context: React.ContextType) { super(props, context); let phase = Phase.Landing; this.creator = this.props.space.currentState.getStateEvents(EventType.RoomCreate, "")?.getSender(); - const showSetup = this.props.justCreatedOpts && this.context.getUserId() === this.creator; + const showSetup = this.props.justCreatedOpts && context.getUserId() === this.creator; if (showSetup) { phase = this.props.justCreatedOpts.createOpts.preset === Preset.PublicChat @@ -815,13 +816,16 @@ export default class SpaceRoomView extends React.PureComponent { this.dispatcherRef = defaultDispatcher.register(this.onAction); RightPanelStore.instance.on(UPDATE_EVENT, this.onRightPanelStoreUpdate); - this.context.on("Room.myMembership", this.onMyMembership); + } + + componentDidMount() { + this.context.on(RoomEvent.MyMembership, this.onMyMembership); } componentWillUnmount() { defaultDispatcher.unregister(this.dispatcherRef); RightPanelStore.instance.off(UPDATE_EVENT, this.onRightPanelStoreUpdate); - this.context.off("Room.myMembership", this.onMyMembership); + this.context.off(RoomEvent.MyMembership, this.onMyMembership); } private onMyMembership = (room: Room, myMembership: string) => { diff --git a/src/components/views/avatars/BaseAvatar.tsx b/src/components/views/avatars/BaseAvatar.tsx index 92d46c3132..22d2d1ab00 100644 --- a/src/components/views/avatars/BaseAvatar.tsx +++ b/src/components/views/avatars/BaseAvatar.tsx @@ -20,13 +20,14 @@ limitations under the License. import React, { useCallback, useContext, useEffect, useState } from 'react'; import classNames from 'classnames'; import { ResizeMethod } from 'matrix-js-sdk/src/@types/partials'; +import { ClientEvent } from "matrix-js-sdk/src/client"; import * as AvatarLogic from '../../../Avatar'; import SettingsStore from "../../../settings/SettingsStore"; import AccessibleButton from '../elements/AccessibleButton'; import RoomContext from "../../../contexts/RoomContext"; import MatrixClientContext from "../../../contexts/MatrixClientContext"; -import { useEventEmitter } from "../../../hooks/useEventEmitter"; +import { useTypedEventEmitter } from "../../../hooks/useEventEmitter"; import { toPx } from "../../../utils/units"; import { _t } from '../../../languageHandler'; @@ -92,7 +93,7 @@ const useImageUrl = ({ url, urls }): [string, () => void] => { setIndex(0); } }, []); - useEventEmitter(cli, "sync", onClientSync); + useTypedEventEmitter(cli, ClientEvent.Sync, onClientSync); const imageUrl = imageUrls[urlsIndex]; return [imageUrl, onError]; diff --git a/src/components/views/avatars/RoomAvatar.tsx b/src/components/views/avatars/RoomAvatar.tsx index b84bb4a58c..9625b64138 100644 --- a/src/components/views/avatars/RoomAvatar.tsx +++ b/src/components/views/avatars/RoomAvatar.tsx @@ -20,6 +20,7 @@ import { ResizeMethod } from 'matrix-js-sdk/src/@types/partials'; import { MatrixEvent } from 'matrix-js-sdk/src/models/event'; import { RoomStateEvent } from "matrix-js-sdk/src/models/room-state"; import classNames from "classnames"; +import { EventType } from "matrix-js-sdk/src/@types/event"; import BaseAvatar from './BaseAvatar'; import ImageView from '../elements/ImageView'; @@ -86,10 +87,7 @@ export default class RoomAvatar extends React.Component { } private onRoomStateEvents = (ev: MatrixEvent) => { - if (!this.props.room || - ev.getRoomId() !== this.props.room.roomId || - ev.getType() !== 'm.room.avatar' - ) return; + if (ev.getRoomId() !== this.props.room?.roomId || ev.getType() !== EventType.RoomAvatar) return; this.setState({ urls: RoomAvatar.getImageUrls(this.props), diff --git a/src/components/views/dialogs/DevtoolsDialog.tsx b/src/components/views/dialogs/DevtoolsDialog.tsx index 58db90cb79..f69041336c 100644 --- a/src/components/views/dialogs/DevtoolsDialog.tsx +++ b/src/components/views/dialogs/DevtoolsDialog.tsx @@ -24,6 +24,7 @@ import { PHASE_STARTED, PHASE_CANCELLED, VerificationRequest, + VerificationRequestEvent, } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; import { Room } from "matrix-js-sdk/src/models/room"; import { MatrixEvent } from "matrix-js-sdk/src/models/event"; @@ -34,7 +35,7 @@ import SyntaxHighlight from '../elements/SyntaxHighlight'; import { _t } from '../../../languageHandler'; import Field from "../elements/Field"; import MatrixClientContext from "../../../contexts/MatrixClientContext"; -import { useEventEmitter } from "../../../hooks/useEventEmitter"; +import { useTypedEventEmitter } from "../../../hooks/useEventEmitter"; import WidgetStore, { IApp } from "../../../stores/WidgetStore"; import { UPDATE_EVENT } from "../../../stores/AsyncStore"; import { SETTINGS } from "../../../settings/Settings"; @@ -756,7 +757,7 @@ const VerificationRequestExplorer: React.FC<{ const [timeout, setRequestTimeout] = useState(request.timeout); /* Re-render if something changes state */ - useEventEmitter(request, "change", updateState); + useTypedEventEmitter(request, VerificationRequestEvent.Change, updateState); /* Keep re-rendering if there's a timeout */ useEffect(() => { diff --git a/src/components/views/elements/RoomName.tsx b/src/components/views/elements/RoomName.tsx index cdd83aedc2..0494d7148f 100644 --- a/src/components/views/elements/RoomName.tsx +++ b/src/components/views/elements/RoomName.tsx @@ -15,9 +15,9 @@ limitations under the License. */ import React, { useEffect, useState } from "react"; -import { Room } from "matrix-js-sdk/src/models/room"; +import { Room, RoomEvent } from "matrix-js-sdk/src/models/room"; -import { useEventEmitter } from "../../../hooks/useEventEmitter"; +import { useTypedEventEmitter } from "../../../hooks/useEventEmitter"; interface IProps { room: Room; @@ -26,7 +26,7 @@ interface IProps { const RoomName = ({ room, children }: IProps): JSX.Element => { const [name, setName] = useState(room?.name); - useEventEmitter(room, "Room.name", () => { + useTypedEventEmitter(room, RoomEvent.Name, () => { setName(room?.name); }); useEffect(() => { diff --git a/src/components/views/elements/RoomTopic.tsx b/src/components/views/elements/RoomTopic.tsx index 0526c2676c..4120f6780e 100644 --- a/src/components/views/elements/RoomTopic.tsx +++ b/src/components/views/elements/RoomTopic.tsx @@ -17,8 +17,10 @@ limitations under the License. import React, { useEffect, useState } from "react"; import { EventType } from "matrix-js-sdk/src/@types/event"; import { Room } from "matrix-js-sdk/src/models/room"; +import { RoomStateEvent } from "matrix-js-sdk/src/models/room-state"; +import { MatrixEvent } from "matrix-js-sdk/src/models/event"; -import { useEventEmitter } from "../../../hooks/useEventEmitter"; +import { useTypedEventEmitter } from "../../../hooks/useEventEmitter"; import { linkifyElement } from "../../../HtmlUtils"; interface IProps { @@ -30,7 +32,8 @@ export const getTopic = room => room?.currentState?.getStateEvents(EventType.Roo const RoomTopic = ({ room, children }: IProps): JSX.Element => { const [topic, setTopic] = useState(getTopic(room)); - useEventEmitter(room.currentState, "RoomState.events", () => { + useTypedEventEmitter(room.currentState, RoomStateEvent.Events, (ev: MatrixEvent) => { + if (ev.getType() !== EventType.RoomTopic) return; setTopic(getTopic(room)); }); useEffect(() => { diff --git a/src/components/views/messages/SenderProfile.tsx b/src/components/views/messages/SenderProfile.tsx index 7f30c81793..ae6df373b3 100644 --- a/src/components/views/messages/SenderProfile.tsx +++ b/src/components/views/messages/SenderProfile.tsx @@ -17,6 +17,7 @@ import React from 'react'; import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import { MsgType } from "matrix-js-sdk/src/@types/event"; +import { RoomStateEvent } from "matrix-js-sdk/src/models/room-state"; import Flair from '../elements/Flair'; import FlairStore from '../../../stores/FlairStore'; @@ -35,13 +36,14 @@ interface IProps { } interface IState { - userGroups; - relatedGroups; + userGroups: string[]; + relatedGroups: string[]; } @replaceableComponent("views.messages.SenderProfile") export default class SenderProfile extends React.Component { static contextType = MatrixClientContext; + public context!: React.ContextType; private unmounted = false; constructor(props: IProps) { @@ -61,12 +63,12 @@ export default class SenderProfile extends React.Component { this.getPublicisedGroups(); } - this.context.on('RoomState.events', this.onRoomStateEvents); + this.context.on(RoomStateEvent.Events, this.onRoomStateEvents); } componentWillUnmount() { this.unmounted = true; - this.context.removeListener('RoomState.events', this.onRoomStateEvents); + this.context.removeListener(RoomStateEvent.Events, this.onRoomStateEvents); } private async getPublicisedGroups() { diff --git a/src/components/views/right_panel/EncryptionPanel.tsx b/src/components/views/right_panel/EncryptionPanel.tsx index 38c0ec747c..f3f0ff2b41 100644 --- a/src/components/views/right_panel/EncryptionPanel.tsx +++ b/src/components/views/right_panel/EncryptionPanel.tsx @@ -16,9 +16,10 @@ limitations under the License. import React, { useCallback, useEffect, useState } from "react"; import { - VerificationRequest, PHASE_REQUESTED, PHASE_UNSENT, + VerificationRequest, + VerificationRequestEvent, } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; import { RoomMember } from "matrix-js-sdk/src/models/room-member"; import { User } from "matrix-js-sdk/src/models/user"; @@ -27,7 +28,7 @@ import EncryptionInfo from "./EncryptionInfo"; import VerificationPanel from "./VerificationPanel"; import { MatrixClientPeg } from "../../../MatrixClientPeg"; import { ensureDMExists } from "../../../createRoom"; -import { useEventEmitter } from "../../../hooks/useEventEmitter"; +import { useTypedEventEmitter } from "../../../hooks/useEventEmitter"; import Modal from "../../../Modal"; import * as sdk from "../../../index"; import { _t } from "../../../languageHandler"; @@ -107,7 +108,7 @@ const EncryptionPanel: React.FC = (props: IProps) => { } }, [onClose, request]); - useEventEmitter(request, "change", changeHandler); + useTypedEventEmitter(request, VerificationRequestEvent.Change, changeHandler); const onStartVerification = useCallback(async () => { setRequesting(true); diff --git a/src/components/views/right_panel/PinnedMessagesCard.tsx b/src/components/views/right_panel/PinnedMessagesCard.tsx index c5ba91c275..31fcc27266 100644 --- a/src/components/views/right_panel/PinnedMessagesCard.tsx +++ b/src/components/views/right_panel/PinnedMessagesCard.tsx @@ -15,16 +15,17 @@ limitations under the License. */ import React, { useCallback, useContext, useEffect, useState } from "react"; -import { Room } from "matrix-js-sdk/src/models/room"; +import { Room, RoomEvent } from "matrix-js-sdk/src/models/room"; import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import { EventType } from 'matrix-js-sdk/src/@types/event'; import { logger } from "matrix-js-sdk/src/logger"; +import { RoomStateEvent } from "matrix-js-sdk/src/models/room-state"; import { _t } from "../../../languageHandler"; import BaseCard from "./BaseCard"; import Spinner from "../elements/Spinner"; import MatrixClientContext from "../../../contexts/MatrixClientContext"; -import { useEventEmitter } from "../../../hooks/useEventEmitter"; +import { useTypedEventEmitter } from "../../../hooks/useEventEmitter"; import PinningUtils from "../../../utils/PinningUtils"; import { useAsyncMemo } from "../../../hooks/useAsyncMemo"; import PinnedEventTile from "../rooms/PinnedEventTile"; @@ -45,7 +46,7 @@ export const usePinnedEvents = (room: Room): string[] => { setPinnedEvents(room.currentState.getStateEvents(EventType.RoomPinnedEvents, "")?.getContent()?.pinned || []); }, [room]); - useEventEmitter(room?.currentState, "RoomState.events", update); + useTypedEventEmitter(room?.currentState, RoomStateEvent.Events, update); useEffect(() => { update(); return () => { @@ -67,7 +68,7 @@ export const useReadPinnedEvents = (room: Room): Set => { setReadPinnedEvents(new Set(readPins || [])); }, [room]); - useEventEmitter(room, "Room.accountData", update); + useTypedEventEmitter(room, RoomEvent.AccountData, update); useEffect(() => { update(); return () => { diff --git a/src/components/views/right_panel/UserInfo.tsx b/src/components/views/right_panel/UserInfo.tsx index 890a5c3899..37347c04bd 100644 --- a/src/components/views/right_panel/UserInfo.tsx +++ b/src/components/views/right_panel/UserInfo.tsx @@ -19,7 +19,7 @@ limitations under the License. import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react'; import classNames from 'classnames'; -import { MatrixClient } from 'matrix-js-sdk/src/client'; +import { ClientEvent, MatrixClient } from 'matrix-js-sdk/src/client'; import { RoomMember } from 'matrix-js-sdk/src/models/room-member'; import { User } from 'matrix-js-sdk/src/models/user'; import { Room } from 'matrix-js-sdk/src/models/room'; @@ -29,6 +29,7 @@ import { VerificationRequest } from "matrix-js-sdk/src/crypto/verification/reque import { EventType } from "matrix-js-sdk/src/@types/event"; import { logger } from "matrix-js-sdk/src/logger"; import { CryptoEvent } from "matrix-js-sdk/src/crypto"; +import { RoomStateEvent } from "matrix-js-sdk/src/models/room-state"; import dis from '../../../dispatcher/dispatcher'; import Modal from '../../../Modal'; @@ -42,7 +43,7 @@ import MultiInviter from "../../../utils/MultiInviter"; import GroupStore from "../../../stores/GroupStore"; import { MatrixClientPeg } from "../../../MatrixClientPeg"; import E2EIcon from "../rooms/E2EIcon"; -import { useEventEmitter } from "../../../hooks/useEventEmitter"; +import { useTypedEventEmitter } from "../../../hooks/useEventEmitter"; import { textualPowerLevel } from '../../../Roles'; import MatrixClientContext from "../../../contexts/MatrixClientContext"; import { RightPanelPhases } from '../../../stores/right-panel/RightPanelStorePhases'; @@ -547,7 +548,7 @@ export const useRoomPowerLevels = (cli: MatrixClient, room: Room) => { setPowerLevels(getPowerLevels(room)); }, [room]); - useEventEmitter(cli, "RoomState.events", update); + useTypedEventEmitter(cli, RoomStateEvent.Events, update); useEffect(() => { update(); return () => { @@ -1105,7 +1106,7 @@ function useRoomPermissions(cli: MatrixClient, room: Room, user: RoomMember): IR }); }, [cli, user, room]); - useEventEmitter(cli, "RoomState.members", updateRoomPermissions); + useTypedEventEmitter(cli, RoomStateEvent.Update, updateRoomPermissions); useEffect(() => { updateRoomPermissions(); return () => { @@ -1316,7 +1317,7 @@ const BasicUserInfo: React.FC<{ setIsIgnored(cli.isUserIgnored(member.userId)); } }, [cli, member.userId]); - useEventEmitter(cli, "accountData", accountDataHandler); + useTypedEventEmitter(cli, ClientEvent.AccountData, accountDataHandler); // Count of how many operations are currently in progress, if > 0 then show a Spinner const [pendingUpdateCount, setPendingUpdateCount] = useState(0); diff --git a/src/components/views/rooms/AuxPanel.tsx b/src/components/views/rooms/AuxPanel.tsx index 7a031b5a44..8c723429c5 100644 --- a/src/components/views/rooms/AuxPanel.tsx +++ b/src/components/views/rooms/AuxPanel.tsx @@ -19,6 +19,7 @@ import { lexicographicCompare } from 'matrix-js-sdk/src/utils'; import { Room } from 'matrix-js-sdk/src/models/room'; import { throttle } from 'lodash'; import { RoomStateEvent } from "matrix-js-sdk/src/models/room-state"; +import { MatrixEvent } from 'matrix-js-sdk/src/models/event'; import { MatrixClientPeg } from "../../../MatrixClientPeg"; import AppsDrawer from './AppsDrawer'; @@ -67,14 +68,13 @@ export default class AuxPanel extends React.Component { componentDidMount() { const cli = MatrixClientPeg.get(); if (SettingsStore.getValue("feature_state_counters")) { - cli.on(RoomStateEvent.Events, this.rateLimitedUpdate); + cli.on(RoomStateEvent.Events, this.onRoomStateEvents); } } componentWillUnmount() { - const cli = MatrixClientPeg.get(); - if (cli && SettingsStore.getValue("feature_state_counters")) { - cli.removeListener(RoomStateEvent.Events, this.rateLimitedUpdate); + if (SettingsStore.getValue("feature_state_counters")) { + MatrixClientPeg.get()?.removeListener(RoomStateEvent.Events, this.onRoomStateEvents); } } @@ -82,7 +82,13 @@ export default class AuxPanel extends React.Component { return objectHasDiff(this.props, nextProps) || objectHasDiff(this.state, nextState); } - private rateLimitedUpdate = throttle(() => { + private onRoomStateEvents = (ev: MatrixEvent) => { + if (ev.getType() === "re.jki.counter") { + this.updateCounters(); + } + }; + + private updateCounters = throttle(() => { this.setState({ counters: this.computeCounters() }); }, 500, { leading: true, trailing: true }); diff --git a/src/components/views/rooms/MemberList.tsx b/src/components/views/rooms/MemberList.tsx index 14ec20ccd4..9b5047c55c 100644 --- a/src/components/views/rooms/MemberList.tsx +++ b/src/components/views/rooms/MemberList.tsx @@ -26,6 +26,7 @@ import { User, UserEvent } from "matrix-js-sdk/src/models/user"; import { throttle } from 'lodash'; import { JoinRule } from "matrix-js-sdk/src/@types/partials"; import { ClientEvent } from "matrix-js-sdk/src/client"; +import { EventType } from "matrix-js-sdk/src/@types/event"; import { _t } from '../../../languageHandler'; import SdkConfig from '../../../SdkConfig'; @@ -113,7 +114,7 @@ export default class MemberList extends React.Component { private listenForMembersChanges(): void { const cli = MatrixClientPeg.get(); - cli.on(RoomStateEvent.Members, this.onRoomStateMember); + cli.on(RoomStateEvent.Update, this.onRoomStateUpdate); cli.on(RoomMemberEvent.Name, this.onRoomMemberName); cli.on(RoomStateEvent.Events, this.onRoomStateEvent); // We listen for changes to the lastPresenceTs which is essentially @@ -129,7 +130,7 @@ export default class MemberList extends React.Component { this.mounted = false; const cli = MatrixClientPeg.get(); if (cli) { - cli.removeListener(RoomStateEvent.Members, this.onRoomStateMember); + cli.removeListener(RoomStateEvent.Update, this.onRoomStateUpdate); cli.removeListener(RoomMemberEvent.Name, this.onRoomMemberName); cli.removeListener(RoomEvent.MyMembership, this.onMyMembership); cli.removeListener(RoomStateEvent.Events, this.onRoomStateEvent); @@ -224,10 +225,8 @@ export default class MemberList extends React.Component { } }; - private onRoomStateMember = (ev: MatrixEvent, state: RoomState, member: RoomMember): void => { - if (member.roomId !== this.props.roomId) { - return; - } + private onRoomStateUpdate = (state: RoomState): void => { + if (state.roomId !== this.props.roomId) return; this.updateList(); }; @@ -238,9 +237,8 @@ export default class MemberList extends React.Component { this.updateList(); }; - private onRoomStateEvent = (event: MatrixEvent, state: RoomState): void => { - if (event.getRoomId() === this.props.roomId && - event.getType() === "m.room.third_party_invite") { + private onRoomStateEvent = (event: MatrixEvent): void => { + if (event.getRoomId() === this.props.roomId && event.getType() === EventType.RoomThirdPartyInvite) { this.updateList(); } diff --git a/src/components/views/rooms/MessageComposer.tsx b/src/components/views/rooms/MessageComposer.tsx index e7f319d676..9a825b7291 100644 --- a/src/components/views/rooms/MessageComposer.tsx +++ b/src/components/views/rooms/MessageComposer.tsx @@ -229,19 +229,19 @@ export default class MessageComposer extends React.Component { this.voiceRecording = null; } - private onRoomStateEvents = (ev, state) => { + private onRoomStateEvents = (ev: MatrixEvent) => { if (ev.getRoomId() !== this.props.room.roomId) return; - if (ev.getType() === 'm.room.tombstone') { + if (ev.getType() === EventType.RoomTombstone) { this.setState({ tombstone: this.getRoomTombstone() }); } - if (ev.getType() === 'm.room.power_levels') { + if (ev.getType() === EventType.RoomPowerLevels) { this.setState({ canSendMessages: this.props.room.maySendMessage() }); } }; private getRoomTombstone() { - return this.props.room.currentState.getStateEvents('m.room.tombstone', ''); + return this.props.room.currentState.getStateEvents(EventType.RoomTombstone, ''); } private onTombstoneClick = (ev) => { diff --git a/src/components/views/rooms/RoomHeader.tsx b/src/components/views/rooms/RoomHeader.tsx index c81fd5b77c..20eac5b1d7 100644 --- a/src/components/views/rooms/RoomHeader.tsx +++ b/src/components/views/rooms/RoomHeader.tsx @@ -18,7 +18,7 @@ limitations under the License. import React from 'react'; import classNames from 'classnames'; import { throttle } from 'lodash'; -import { MatrixEvent, Room, RoomState, RoomStateEvent } from 'matrix-js-sdk/src'; +import { MatrixEvent, Room, RoomStateEvent } from 'matrix-js-sdk/src'; import { CallType } from "matrix-js-sdk/src/webrtc/call"; import { _t } from '../../../languageHandler'; @@ -94,7 +94,7 @@ export default class RoomHeader extends React.Component { notiStore.removeListener(NotificationStateEvents.Update, this.onNotificationUpdate); } - private onRoomStateEvents = (event: MatrixEvent, state: RoomState) => { + private onRoomStateEvents = (event: MatrixEvent) => { if (!this.props.room || event.getRoomId() !== this.props.room.roomId) { return; } diff --git a/src/components/views/rooms/RoomList.tsx b/src/components/views/rooms/RoomList.tsx index 86829d9149..d77cafb1c4 100644 --- a/src/components/views/rooms/RoomList.tsx +++ b/src/components/views/rooms/RoomList.tsx @@ -128,7 +128,7 @@ const auxButtonContextMenuPosition = (handle: RefObject) => { const DmAuxButton = ({ tabIndex, dispatcher = defaultDispatcher }: IAuxButtonProps) => { const [menuDisplayed, handle, openMenu, closeMenu] = useContextMenu(); - const activeSpace = useEventEmitterState(SpaceStore.instance, UPDATE_SELECTED_SPACE, () => { + const activeSpace: Room = useEventEmitterState(SpaceStore.instance, UPDATE_SELECTED_SPACE, () => { return SpaceStore.instance.activeSpaceRoom; }); diff --git a/src/components/views/rooms/RoomListHeader.tsx b/src/components/views/rooms/RoomListHeader.tsx index 70cafe10ac..bee14b6fa5 100644 --- a/src/components/views/rooms/RoomListHeader.tsx +++ b/src/components/views/rooms/RoomListHeader.tsx @@ -15,11 +15,12 @@ limitations under the License. */ import React, { ComponentProps, useContext, useEffect, useState } from "react"; -import { Room } from "matrix-js-sdk/src/models/room"; +import { Room, RoomEvent } from "matrix-js-sdk/src/models/room"; import { EventType } from "matrix-js-sdk/src/@types/event"; +import { ClientEvent } from "matrix-js-sdk/src/client"; import { _t } from "../../../languageHandler"; -import { useEventEmitter, useEventEmitterState } from "../../../hooks/useEventEmitter"; +import { useEventEmitterState, useTypedEventEmitter, useTypedEventEmitterState } from "../../../hooks/useEventEmitter"; import SpaceStore from "../../../stores/spaces/SpaceStore"; import { ChevronFace, ContextMenuTooltipButton, useContextMenu } from "../../structures/ContextMenu"; import SpaceContextMenu from "../context_menus/SpaceContextMenu"; @@ -150,7 +151,7 @@ const useJoiningRooms = (): Set => { break; } }); - useEventEmitter(cli, "Room", (room: Room) => { + useTypedEventEmitter(cli, ClientEvent.Room, (room: Room) => { if (joiningRooms.delete(room.roomId)) { setJoiningRooms(new Set(joiningRooms)); } @@ -190,7 +191,7 @@ const RoomListHeader = ({ spacePanelDisabled, onVisibilityChange }: IProps) => { // we pass null for the queryLength to inhibit the metrics hook for when there is no filterCondition useWebSearchMetrics(count, filterCondition ? filterCondition.search.length : null, false); - const spaceName = useEventEmitterState(activeSpace, "Room.name", () => activeSpace?.name); + const spaceName = useTypedEventEmitterState(activeSpace, RoomEvent.Name, () => activeSpace?.name); useEffect(() => { if (onVisibilityChange) { diff --git a/src/components/views/rooms/RoomUpgradeWarningBar.tsx b/src/components/views/rooms/RoomUpgradeWarningBar.tsx index d316214d5e..a9b121919b 100644 --- a/src/components/views/rooms/RoomUpgradeWarningBar.tsx +++ b/src/components/views/rooms/RoomUpgradeWarningBar.tsx @@ -17,7 +17,7 @@ limitations under the License. import React from 'react'; import { MatrixEvent } from 'matrix-js-sdk/src/models/event'; import { Room } from 'matrix-js-sdk/src/models/room'; -import { RoomState, RoomStateEvent } from 'matrix-js-sdk/src/models/room-state'; +import { RoomStateEvent } from 'matrix-js-sdk/src/models/room-state'; import Modal from '../../../Modal'; import { _t } from '../../../languageHandler'; @@ -56,7 +56,7 @@ export default class RoomUpgradeWarningBar extends React.PureComponent { + private onStateEvents = (event: MatrixEvent): void => { if (!this.props.room || event.getRoomId() !== this.props.room.roomId) { return; } diff --git a/src/components/views/rooms/ThirdPartyMemberInfo.tsx b/src/components/views/rooms/ThirdPartyMemberInfo.tsx index 133fb60638..0b8536afcf 100644 --- a/src/components/views/rooms/ThirdPartyMemberInfo.tsx +++ b/src/components/views/rooms/ThirdPartyMemberInfo.tsx @@ -19,6 +19,7 @@ import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import { Room } from "matrix-js-sdk/src/models/room"; import { logger } from "matrix-js-sdk/src/logger"; import { RoomStateEvent } from "matrix-js-sdk/src/models/room-state"; +import { EventType } from "matrix-js-sdk/src/@types/event"; import { MatrixClientPeg } from "../../../MatrixClientPeg"; import { _t } from "../../../languageHandler"; @@ -82,8 +83,8 @@ export default class ThirdPartyMemberInfo extends React.Component { - if (ev.getType() === "m.room.third_party_invite" && ev.getStateKey() === this.state.stateKey) { + onRoomStateEvents = (ev: MatrixEvent) => { + if (ev.getType() === EventType.RoomThirdPartyInvite && ev.getStateKey() === this.state.stateKey) { const newDisplayName = ev.getContent().display_name; const isInvited = isValid3pidInvite(ev); diff --git a/src/components/views/settings/ChangeAvatar.tsx b/src/components/views/settings/ChangeAvatar.tsx index abaf6737fd..98cc17f75d 100644 --- a/src/components/views/settings/ChangeAvatar.tsx +++ b/src/components/views/settings/ChangeAvatar.tsx @@ -18,6 +18,7 @@ import React from 'react'; import { MatrixEvent } from 'matrix-js-sdk/src/models/event'; import { Room } from 'matrix-js-sdk/src/models/room'; import { RoomStateEvent } from "matrix-js-sdk/src/models/room-state"; +import { EventType } from "matrix-js-sdk/src/@types/event"; import { MatrixClientPeg } from "../../../MatrixClientPeg"; import { _t } from '../../../languageHandler'; @@ -96,8 +97,10 @@ export default class ChangeAvatar extends React.Component { return; } - if (ev.getRoomId() !== this.props.room.roomId || ev.getType() !== 'm.room.avatar' - || ev.getSender() !== MatrixClientPeg.get().getUserId()) { + if (ev.getRoomId() !== this.props.room.roomId || + ev.getType() !== EventType.RoomAvatar || + ev.getSender() !== MatrixClientPeg.get().getUserId() + ) { return; } diff --git a/src/components/views/settings/tabs/room/RolesRoomSettingsTab.tsx b/src/components/views/settings/tabs/room/RolesRoomSettingsTab.tsx index 2c86ebe96e..7eb9237399 100644 --- a/src/components/views/settings/tabs/room/RolesRoomSettingsTab.tsx +++ b/src/components/views/settings/tabs/room/RolesRoomSettingsTab.tsx @@ -17,7 +17,6 @@ limitations under the License. import React from 'react'; import { EventType } from "matrix-js-sdk/src/@types/event"; import { RoomMember } from "matrix-js-sdk/src/models/room-member"; -import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import { RoomState, RoomStateEvent } from "matrix-js-sdk/src/models/room-state"; import { logger } from "matrix-js-sdk/src/logger"; import { throttle } from "lodash"; @@ -123,17 +122,17 @@ interface IProps { @replaceableComponent("views.settings.tabs.room.RolesRoomSettingsTab") export default class RolesRoomSettingsTab extends React.Component { componentDidMount() { - MatrixClientPeg.get().on(RoomStateEvent.Members, this.onRoomMembership); + MatrixClientPeg.get().on(RoomStateEvent.Update, this.onRoomStateUpdate); } componentWillUnmount() { const client = MatrixClientPeg.get(); if (client) { - client.removeListener(RoomStateEvent.Members, this.onRoomMembership); + client.removeListener(RoomStateEvent.Update, this.onRoomStateUpdate); } } - private onRoomMembership = (event: MatrixEvent, state: RoomState, member: RoomMember) => { + private onRoomStateUpdate = (state: RoomState) => { if (state.roomId !== this.props.roomId) return; this.onThisRoomMembership(); }; diff --git a/src/dispatcher/payloads/AfterLeaveRoomPayload.ts b/src/dispatcher/payloads/AfterLeaveRoomPayload.ts index 942b5d9800..314b266b62 100644 --- a/src/dispatcher/payloads/AfterLeaveRoomPayload.ts +++ b/src/dispatcher/payloads/AfterLeaveRoomPayload.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { Room } from "matrix-js-sdk"; +import { Room } from "matrix-js-sdk/src/models/room"; import { Action } from "../actions"; import { ActionPayload } from "../payloads"; diff --git a/src/dispatcher/payloads/JoinRoomErrorPayload.ts b/src/dispatcher/payloads/JoinRoomErrorPayload.ts index c130401f4b..cbd5332dbc 100644 --- a/src/dispatcher/payloads/JoinRoomErrorPayload.ts +++ b/src/dispatcher/payloads/JoinRoomErrorPayload.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { MatrixError } from "matrix-js-sdk"; +import { MatrixError } from "matrix-js-sdk/src/http-api"; import { ActionPayload } from "../payloads"; import { Action } from "../actions"; diff --git a/src/dispatcher/payloads/ViewRoomErrorPayload.ts b/src/dispatcher/payloads/ViewRoomErrorPayload.ts index c044e150ca..d30aa8893a 100644 --- a/src/dispatcher/payloads/ViewRoomErrorPayload.ts +++ b/src/dispatcher/payloads/ViewRoomErrorPayload.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { MatrixError, Room } from "matrix-js-sdk"; +import { MatrixError, Room } from "matrix-js-sdk/src/matrix"; import { ActionPayload } from "../payloads"; import { Action } from "../actions"; diff --git a/src/dispatcher/payloads/ViewStartChatOrReusePayload.ts b/src/dispatcher/payloads/ViewStartChatOrReusePayload.ts index 4a54aaf4ad..ff2fbbe8b4 100644 --- a/src/dispatcher/payloads/ViewStartChatOrReusePayload.ts +++ b/src/dispatcher/payloads/ViewStartChatOrReusePayload.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { User } from "matrix-js-sdk"; +import { User } from "matrix-js-sdk/src/models/user"; import { ActionPayload } from "../payloads"; import { Action } from "../actions"; diff --git a/src/hooks/useAccountData.ts b/src/hooks/useAccountData.ts index 6e2cd269ff..56a5530000 100644 --- a/src/hooks/useAccountData.ts +++ b/src/hooks/useAccountData.ts @@ -15,11 +15,11 @@ limitations under the License. */ import { useCallback, useState } from "react"; -import { MatrixClient } from "matrix-js-sdk/src/client"; +import { ClientEvent, MatrixClient } from "matrix-js-sdk/src/client"; import { MatrixEvent } from "matrix-js-sdk/src/models/event"; -import { Room } from "matrix-js-sdk/src/models/room"; +import { Room, RoomEvent } from "matrix-js-sdk/src/models/room"; -import { useEventEmitter } from "./useEventEmitter"; +import { useTypedEventEmitter } from "./useEventEmitter"; const tryGetContent = (ev?: MatrixEvent) => ev ? ev.getContent() : undefined; @@ -31,7 +31,7 @@ export const useAccountData = (cli: MatrixClient, eventType: strin if (event.getType() !== eventType) return; setValue(event.getContent()); }, [eventType]); - useEventEmitter(cli, "accountData", handler); + useTypedEventEmitter(cli, ClientEvent.AccountData, handler); return value || {} as T; }; @@ -44,7 +44,7 @@ export const useRoomAccountData = (room: Room, eventType: string) if (event.getType() !== eventType) return; setValue(event.getContent()); }, [eventType]); - useEventEmitter(room, "Room.accountData", handler); + useTypedEventEmitter(room, RoomEvent.AccountData, handler); return value || {} as T; }; diff --git a/src/hooks/useEventEmitter.ts b/src/hooks/useEventEmitter.ts index c73e4ba53d..fccc313b42 100644 --- a/src/hooks/useEventEmitter.ts +++ b/src/hooks/useEventEmitter.ts @@ -15,17 +15,29 @@ limitations under the License. */ import { useRef, useEffect, useState, useCallback } from "react"; +import { ListenerMap, TypedEventEmitter } from "matrix-js-sdk/src/models/typed-event-emitter"; import type { EventEmitter } from "events"; type Handler = (...args: any[]) => void; +export function useTypedEventEmitter< + Events extends string, + Arguments extends ListenerMap, +>( + emitter: TypedEventEmitter, + eventName: Events, + handler: Handler, +): void { + useEventEmitter(emitter, eventName, handler); +} + // Hook to wrap event emitter on and removeListener in hook lifecycle -export const useEventEmitter = ( +export function useEventEmitter( emitter: EventEmitter | undefined, eventName: string | symbol, handler: Handler, -) => { +): void { // Create a ref that stores handler const savedHandler = useRef(handler); @@ -52,15 +64,27 @@ export const useEventEmitter = ( }, [eventName, emitter], // Re-run if eventName or emitter changes ); -}; +} type Mapper = (...args: any[]) => T; -export const useEventEmitterState = ( +export function useTypedEventEmitterState< + T, + Events extends string, + Arguments extends ListenerMap, +>( + emitter: TypedEventEmitter, + eventName: Events, + fn: Mapper, +): T { + return useEventEmitterState(emitter, eventName, fn); +} + +export function useEventEmitterState( emitter: EventEmitter | undefined, eventName: string | symbol, fn: Mapper, -): T => { +): T { const [value, setValue] = useState(fn()); const handler = useCallback((...args: any[]) => { setValue(fn(...args)); @@ -69,4 +93,4 @@ export const useEventEmitterState = ( useEffect(handler, [emitter]); // eslint-disable-line react-hooks/exhaustive-deps useEventEmitter(emitter, eventName, handler); return value; -}; +} diff --git a/src/hooks/useIsEncrypted.ts b/src/hooks/useIsEncrypted.ts index ed8e7f3f50..5a21cfe8b5 100644 --- a/src/hooks/useIsEncrypted.ts +++ b/src/hooks/useIsEncrypted.ts @@ -18,19 +18,21 @@ import { useCallback, useState } from "react"; import { MatrixClient } from "matrix-js-sdk/src/client"; import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import { Room } from "matrix-js-sdk/src/models/room"; +import { RoomStateEvent } from "matrix-js-sdk/src/models/room-state"; +import { EventType } from "matrix-js-sdk/src/@types/event"; -import { useEventEmitter } from "./useEventEmitter"; +import { useTypedEventEmitter } from "./useEventEmitter"; // Hook to simplify watching whether a Matrix room is encrypted, returns undefined if room is undefined export function useIsEncrypted(cli: MatrixClient, room?: Room): boolean | undefined { const [isEncrypted, setIsEncrypted] = useState(room ? cli.isRoomEncrypted(room.roomId) : undefined); const update = useCallback((event: MatrixEvent) => { - if (room && event.getType() === "m.room.encryption") { + if (room && event.getType() === EventType.RoomEncryption) { setIsEncrypted(cli.isRoomEncrypted(room.roomId)); } }, [cli, room]); - useEventEmitter(room ? room.currentState : undefined, "RoomState.events", update); + useTypedEventEmitter(room?.currentState, RoomStateEvent.Events, update); return isEncrypted; } diff --git a/src/hooks/useRoomMembers.ts b/src/hooks/useRoomMembers.ts index be770c09af..a2d4e0a2c8 100644 --- a/src/hooks/useRoomMembers.ts +++ b/src/hooks/useRoomMembers.ts @@ -17,14 +17,15 @@ limitations under the License. import { useState } from "react"; import { Room } from "matrix-js-sdk/src/models/room"; import { RoomMember } from "matrix-js-sdk/src/models/room-member"; +import { RoomStateEvent } from "matrix-js-sdk/src/models/room-state"; import { throttle } from "lodash"; -import { useEventEmitter } from "./useEventEmitter"; +import { useTypedEventEmitter } from "./useEventEmitter"; // Hook to simplify watching Matrix Room joined members export const useRoomMembers = (room: Room, throttleWait = 250) => { const [members, setMembers] = useState(room.getJoinedMembers()); - useEventEmitter(room.currentState, "RoomState.members", throttle(() => { + useTypedEventEmitter(room.currentState, RoomStateEvent.Update, throttle(() => { setMembers(room.getJoinedMembers()); }, throttleWait, { leading: true, trailing: true })); return members; @@ -33,7 +34,7 @@ export const useRoomMembers = (room: Room, throttleWait = 250) => { // Hook to simplify watching Matrix Room joined member count export const useRoomMemberCount = (room: Room, throttleWait = 250) => { const [count, setCount] = useState(room.getJoinedMemberCount()); - useEventEmitter(room.currentState, "RoomState.members", throttle(() => { + useTypedEventEmitter(room.currentState, RoomStateEvent.Update, throttle(() => { setCount(room.getJoinedMemberCount()); }, throttleWait, { leading: true, trailing: true })); return count; diff --git a/src/hooks/useRoomState.ts b/src/hooks/useRoomState.ts index 015ea00924..131fd281ae 100644 --- a/src/hooks/useRoomState.ts +++ b/src/hooks/useRoomState.ts @@ -16,9 +16,9 @@ limitations under the License. import { useCallback, useEffect, useState } from "react"; import { Room } from "matrix-js-sdk/src/models/room"; -import { RoomState } from "matrix-js-sdk/src/models/room-state"; +import { RoomState, RoomStateEvent } from "matrix-js-sdk/src/models/room-state"; -import { useEventEmitter } from "./useEventEmitter"; +import { useTypedEventEmitter } from "./useEventEmitter"; type Mapper = (roomState: RoomState) => T; const defaultMapper: Mapper = (roomState: RoomState) => roomState; @@ -36,7 +36,7 @@ export const useRoomState = ( setValue(mapper(room.currentState)); }, [room, mapper]); - useEventEmitter(room?.currentState, "RoomState.events", update); + useTypedEventEmitter(room?.currentState, RoomStateEvent.Update, update); useEffect(() => { update(); return () => { diff --git a/src/hooks/useUserStatusMessage.ts b/src/hooks/useUserStatusMessage.ts index c3c99dc668..74b6c1dbf2 100644 --- a/src/hooks/useUserStatusMessage.ts +++ b/src/hooks/useUserStatusMessage.ts @@ -15,11 +15,11 @@ limitations under the License. */ import { MatrixClient } from "matrix-js-sdk/src/client"; -import { User } from "matrix-js-sdk/src/models/user"; +import { User, UserEvent } from "matrix-js-sdk/src/models/user"; import { useContext } from "react"; import MatrixClientContext from "../contexts/MatrixClientContext"; -import { useEventEmitterState } from "./useEventEmitter"; +import { useTypedEventEmitterState } from "./useEventEmitter"; import { Member } from "../components/views/right_panel/UserInfo"; import { useFeatureEnabled } from "./useSettings"; @@ -29,10 +29,11 @@ const getStatusMessage = (cli: MatrixClient, user: Member): string => { }; // Hook to simplify handling Matrix User status -export const useUserStatusMessage = (user?: Member): string => { +export const useUserStatusMessage = (member?: Member): string => { const cli = useContext(MatrixClientContext); const enabled = useFeatureEnabled("feature_custom_status"); - return useEventEmitterState(enabled && getUser(cli, user), "User.unstable_statusMessage", () => { + const user = enabled ? getUser(cli, member) : undefined; + return useTypedEventEmitterState(user, UserEvent._UnstableStatusMessage, () => { return getStatusMessage(cli, user); }); }; diff --git a/src/stores/OwnProfileStore.ts b/src/stores/OwnProfileStore.ts index 5ddd31783b..cd5ee2999c 100644 --- a/src/stores/OwnProfileStore.ts +++ b/src/stores/OwnProfileStore.ts @@ -18,6 +18,7 @@ import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import { User, UserEvent } from "matrix-js-sdk/src/models/user"; import { RoomStateEvent } from "matrix-js-sdk/src/models/room-state"; import { throttle } from "lodash"; +import { EventType } from "matrix-js-sdk/src/@types/event"; import { ActionPayload } from "../dispatcher/payloads"; import { AsyncStoreWithClient } from "./AsyncStoreWithClient"; @@ -102,9 +103,7 @@ export class OwnProfileStore extends AsyncStoreWithClient { this.monitoredUser.removeListener(UserEvent.DisplayName, this.onProfileUpdate); this.monitoredUser.removeListener(UserEvent.AvatarUrl, this.onProfileUpdate); } - if (this.matrixClient) { - this.matrixClient.removeListener(RoomStateEvent.Events, this.onStateEvents); - } + this.matrixClient?.removeListener(RoomStateEvent.Events, this.onStateEvents); await this.reset({}); } @@ -127,7 +126,7 @@ export class OwnProfileStore extends AsyncStoreWithClient { // we don't actually do anything here } - private onProfileUpdate = async () => { + private onProfileUpdate = throttle(async () => { // We specifically do not use the User object we stored for profile info as it // could easily be wrong (such as per-room instead of global profile). const profileInfo = await this.matrixClient.getProfileInfo(this.matrixClient.getUserId()); @@ -147,12 +146,12 @@ export class OwnProfileStore extends AsyncStoreWithClient { avatarUrl: profileInfo.avatar_url, fetchedAt: Date.now(), }); - }; + }, 200, { trailing: true, leading: true }); - private onStateEvents = throttle(async (ev: MatrixEvent) => { + private onStateEvents = async (ev: MatrixEvent) => { const myUserId = MatrixClientPeg.get().getUserId(); - if (ev.getType() === 'm.room.member' && ev.getSender() === myUserId && ev.getStateKey() === myUserId) { + if (ev.getType() === EventType.RoomMember && ev.getSender() === myUserId && ev.getStateKey() === myUserId) { await this.onProfileUpdate(); } - }, 200, { trailing: true, leading: true }); + }; } diff --git a/src/utils/WidgetUtils.ts b/src/utils/WidgetUtils.ts index 3a9676ea83..c1d50cf0b7 100644 --- a/src/utils/WidgetUtils.ts +++ b/src/utils/WidgetUtils.ts @@ -205,8 +205,8 @@ export default class WidgetUtils { return; } - function onRoomStateEvents(ev) { - if (ev.getRoomId() !== roomId) return; + function onRoomStateEvents(ev: MatrixEvent) { + if (ev.getRoomId() !== roomId || ev.getType() !== "im.vector.modular.widgets") return; // TODO: Enable support for m.widget event type (https://github.com/vector-im/element-web/issues/13111) const currentWidgetEvents = room.currentState.getStateEvents('im.vector.modular.widgets');