diff --git a/src/components/views/context_menus/DeviceContextMenu.tsx b/src/components/views/context_menus/DeviceContextMenu.tsx index f30a7e1043..16ac988f6f 100644 --- a/src/components/views/context_menus/DeviceContextMenu.tsx +++ b/src/components/views/context_menus/DeviceContextMenu.tsx @@ -54,7 +54,7 @@ const DeviceContextMenuSection: React.FC = ({ de useEffect(() => { const getDevices = async (): Promise => { - return setDevices((await MediaDeviceHandler.getDevices())[deviceKind]); + return setDevices((await MediaDeviceHandler.getDevices())?.[deviceKind] ?? []); }; getDevices(); }, [deviceKind]); diff --git a/src/components/views/context_menus/KebabContextMenu.tsx b/src/components/views/context_menus/KebabContextMenu.tsx index 3cde63433e..b81c6aef6f 100644 --- a/src/components/views/context_menus/KebabContextMenu.tsx +++ b/src/components/views/context_menus/KebabContextMenu.tsx @@ -48,7 +48,7 @@ export const KebabContextMenu: React.FC = ({ options, tit compact rightAligned closeOnInteraction - {...contextMenuBelow(button.current.getBoundingClientRect())} + {...contextMenuBelow(button.current!.getBoundingClientRect())} > {options} diff --git a/src/components/views/context_menus/MessageContextMenu.tsx b/src/components/views/context_menus/MessageContextMenu.tsx index 8392e6a14f..0455f68e6b 100644 --- a/src/components/views/context_menus/MessageContextMenu.tsx +++ b/src/components/views/context_menus/MessageContextMenu.tsx @@ -73,7 +73,7 @@ const ReplyInThreadButton: React.FC = ({ mxEvent, closeMen if (mxEvent.getThread() && !mxEvent.isThreadRoot) { dis.dispatch({ action: Action.ShowThread, - rootEvent: mxEvent.getThread().rootEvent, + rootEvent: mxEvent.getThread()!.rootEvent!, initialEvent: mxEvent, scroll_into_view: true, highlighted: true, @@ -163,12 +163,12 @@ export default class MessageContextMenu extends React.Component // to obliterate the room - https://github.com/matrix-org/synapse/issues/4042 // Similarly for encryption events, since redacting them "breaks everything" const canRedact = - room.currentState.maySendRedactionForEvent(this.props.mxEvent, cli.credentials.userId) && + !!room?.currentState.maySendRedactionForEvent(this.props.mxEvent, cli.getSafeUserId()) && this.props.mxEvent.getType() !== EventType.RoomServerAcl && this.props.mxEvent.getType() !== EventType.RoomEncryption; let canPin = - room.currentState.mayClientSendStateEvent(EventType.RoomPinnedEvents, cli) && + !!room?.currentState.mayClientSendStateEvent(EventType.RoomPinnedEvents, cli) && canPinEvent(this.props.mxEvent); // HACK: Intentionally say we can't pin if the user doesn't want to use the functionality @@ -249,9 +249,10 @@ export default class MessageContextMenu extends React.Component private onPinClick = (): void => { const cli = MatrixClientPeg.get(); const room = cli.getRoom(this.props.mxEvent.getRoomId()); + if (!room) return; const eventId = this.props.mxEvent.getId(); - const pinnedIds = room?.currentState?.getStateEvents(EventType.RoomPinnedEvents, "")?.getContent().pinned || []; + const pinnedIds = room.currentState?.getStateEvents(EventType.RoomPinnedEvents, "")?.getContent().pinned || []; if (pinnedIds.includes(eventId)) { pinnedIds.splice(pinnedIds.indexOf(eventId), 1); @@ -261,7 +262,7 @@ export default class MessageContextMenu extends React.Component event_ids: [...(room.getAccountData(ReadPinsEventId)?.getContent()?.event_ids || []), eventId], }); } - cli.sendStateEvent(this.props.mxEvent.getRoomId(), EventType.RoomPinnedEvents, { pinned: pinnedIds }, ""); + cli.sendStateEvent(room.roomId, EventType.RoomPinnedEvents, { pinned: pinnedIds }, ""); this.closeMenu(); }; @@ -294,12 +295,13 @@ export default class MessageContextMenu extends React.Component private onCopyLinkClick = (e: ButtonEvent): void => { e.preventDefault(); // So that we don't open the permalink + if (!this.props.link) return; copyPlaintext(this.props.link); this.closeMenu(); }; private onCollapseReplyChainClick = (): void => { - this.props.collapseReplyChain(); + this.props.collapseReplyChain?.(); this.closeMenu(); }; @@ -349,10 +351,12 @@ export default class MessageContextMenu extends React.Component const cli = MatrixClientPeg.get(); const room = cli.getRoom(this.props.mxEvent.getRoomId()); const eventId = this.props.mxEvent.getId(); - return room.getPendingEvents().filter((e) => { - const relation = e.getRelation(); - return relation?.rel_type === RelationType.Annotation && relation.event_id === eventId && filter(e); - }); + return ( + room?.getPendingEvents().filter((e) => { + const relation = e.getRelation(); + return relation?.rel_type === RelationType.Annotation && relation.event_id === eventId && filter(e); + }) ?? [] + ); } private getUnsentReactions(): MatrixEvent[] { @@ -380,7 +384,7 @@ export default class MessageContextMenu extends React.Component const eventStatus = mxEvent.status; const unsentReactionsCount = this.getUnsentReactions().length; const contentActionable = isContentActionable(mxEvent); - const permalink = this.props.permalinkCreator?.forEvent(this.props.mxEvent.getId()); + const permalink = this.props.permalinkCreator?.forEvent(this.props.mxEvent.getId()!); // status is SENT before remote-echo, null after const isSent = !eventStatus || eventStatus === EventStatus.SENT; const { timelineRenderingType, canReact, canSendMessages } = this.context; diff --git a/src/components/views/context_menus/RoomContextMenu.tsx b/src/components/views/context_menus/RoomContextMenu.tsx index 3b335b4dcd..bad72b2bb3 100644 --- a/src/components/views/context_menus/RoomContextMenu.tsx +++ b/src/components/views/context_menus/RoomContextMenu.tsx @@ -399,7 +399,7 @@ const RoomContextMenu: React.FC = ({ room, onFinished, ...props }) => { Modal.createDialog( DevtoolsDialog, { - roomId: SdkContextClass.instance.roomViewStore.getRoomId(), + roomId: room.roomId, }, "mx_DevtoolsDialog_wrapper", ); diff --git a/src/components/views/context_menus/ThreadListContextMenu.tsx b/src/components/views/context_menus/ThreadListContextMenu.tsx index 674330a175..543f2b9d96 100644 --- a/src/components/views/context_menus/ThreadListContextMenu.tsx +++ b/src/components/views/context_menus/ThreadListContextMenu.tsx @@ -72,7 +72,7 @@ const ThreadListContextMenu: React.FC = ({ if (permalinkCreator) { evt?.preventDefault(); evt?.stopPropagation(); - const matrixToUrl = permalinkCreator.forEvent(mxEvent.getId()); + const matrixToUrl = permalinkCreator.forEvent(mxEvent.getId()!); await copyPlaintext(matrixToUrl); closeThreadOptions(); } @@ -84,9 +84,8 @@ const ThreadListContextMenu: React.FC = ({ onMenuToggle?.(menuDisplayed); }, [menuDisplayed, onMenuToggle]); - const isMainSplitTimelineShown = !WidgetLayoutStore.instance.hasMaximisedWidget( - MatrixClientPeg.get().getRoom(mxEvent.getRoomId()), - ); + const room = MatrixClientPeg.get().getRoom(mxEvent.getRoomId()); + const isMainSplitTimelineShown = !!room && !WidgetLayoutStore.instance.hasMaximisedWidget(room); return ( = ({ className="mx_RoomTile_contextMenu" compact rightAligned - {...contextMenuBelow(button.current.getBoundingClientRect())} + {...contextMenuBelow(button.current!.getBoundingClientRect())} > {isMainSplitTimelineShown && ( diff --git a/src/components/views/context_menus/WidgetContextMenu.tsx b/src/components/views/context_menus/WidgetContextMenu.tsx index a0e8efd6f5..a70f07a0b1 100644 --- a/src/components/views/context_menus/WidgetContextMenu.tsx +++ b/src/components/views/context_menus/WidgetContextMenu.tsx @@ -63,8 +63,8 @@ export const WidgetContextMenu: React.FC = ({ const widgetMessaging = WidgetMessagingStore.instance.getMessagingForUid(WidgetUtils.getWidgetUid(app)); const canModify = userWidget || WidgetUtils.canUserModifyWidgets(roomId); - let streamAudioStreamButton; - if (getConfigLivestreamUrl() && WidgetType.JITSI.matches(app.type)) { + let streamAudioStreamButton: JSX.Element | undefined; + if (roomId && getConfigLivestreamUrl() && WidgetType.JITSI.matches(app.type)) { const onStreamAudioClick = async (): Promise => { try { await startJitsiAudioLivestream(widgetMessaging!, roomId); @@ -87,12 +87,12 @@ export const WidgetContextMenu: React.FC = ({ const pinnedWidgets = room ? WidgetLayoutStore.instance.getContainerWidgets(room, Container.Top) : []; const widgetIndex = pinnedWidgets.findIndex((widget) => widget.id === app.id); - let editButton; + let editButton: JSX.Element | undefined; if (canModify && WidgetUtils.isManagedByManager(app)) { const _onEditClick = (): void => { if (onEditClick) { onEditClick(); - } else { + } else if (room) { WidgetUtils.editWidget(room, app); } onFinished(); @@ -101,7 +101,7 @@ export const WidgetContextMenu: React.FC = ({ editButton = ; } - let snapshotButton; + let snapshotButton: JSX.Element | undefined; const screenshotsEnabled = SettingsStore.getValue("enableWidgetScreenshots"); if (screenshotsEnabled && widgetMessaging?.hasCapability(MatrixCapabilities.Screenshots)) { const onSnapshotClick = (): void => { @@ -122,12 +122,12 @@ export const WidgetContextMenu: React.FC = ({ snapshotButton = ; } - let deleteButton; + let deleteButton: JSX.Element | undefined; if (onDeleteClick || canModify) { const _onDeleteClick = (): void => { if (onDeleteClick) { onDeleteClick(); - } else { + } else if (roomId) { // Show delete confirmation dialog Modal.createDialog(QuestionDialog, { title: _t("Delete Widget"), @@ -159,7 +159,7 @@ export const WidgetContextMenu: React.FC = ({ app.creatorUserId === cli.getUserId(); const isLocalWidget = WidgetType.JITSI.matches(app.type); - let revokeButton; + let revokeButton: JSX.Element | undefined; if (!userWidget && !isLocalWidget && isAllowedWidget) { const opts: ApprovalOpts = { approved: undefined }; ModuleRunner.instance.invoke(WidgetLifecycle.PreLoadRequest, opts, new ElementWidget(app)); @@ -182,7 +182,7 @@ export const WidgetContextMenu: React.FC = ({ } } - let moveLeftButton; + let moveLeftButton: JSX.Element | undefined; if (showUnpin && widgetIndex > 0) { const onClick = (): void => { if (!room) throw new Error("room must be defined"); @@ -193,7 +193,7 @@ export const WidgetContextMenu: React.FC = ({ moveLeftButton = ; } - let moveRightButton; + let moveRightButton: JSX.Element | undefined; if (showUnpin && widgetIndex < pinnedWidgets.length - 1) { const onClick = (): void => { if (!room) throw new Error("room must be defined");