Apply strictNullChecks
to src/components/views/context_menus/*
(#10367)
This commit is contained in:
parent
4c2b5df1f2
commit
7c2511a592
6 changed files with 32 additions and 29 deletions
|
@ -54,7 +54,7 @@ const DeviceContextMenuSection: React.FC<IDeviceContextMenuSectionProps> = ({ de
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const getDevices = async (): Promise<void> => {
|
const getDevices = async (): Promise<void> => {
|
||||||
return setDevices((await MediaDeviceHandler.getDevices())[deviceKind]);
|
return setDevices((await MediaDeviceHandler.getDevices())?.[deviceKind] ?? []);
|
||||||
};
|
};
|
||||||
getDevices();
|
getDevices();
|
||||||
}, [deviceKind]);
|
}, [deviceKind]);
|
||||||
|
|
|
@ -48,7 +48,7 @@ export const KebabContextMenu: React.FC<KebabContextMenuProps> = ({ options, tit
|
||||||
compact
|
compact
|
||||||
rightAligned
|
rightAligned
|
||||||
closeOnInteraction
|
closeOnInteraction
|
||||||
{...contextMenuBelow(button.current.getBoundingClientRect())}
|
{...contextMenuBelow(button.current!.getBoundingClientRect())}
|
||||||
>
|
>
|
||||||
<IconizedContextMenuOptionList>{options}</IconizedContextMenuOptionList>
|
<IconizedContextMenuOptionList>{options}</IconizedContextMenuOptionList>
|
||||||
</IconizedContextMenu>
|
</IconizedContextMenu>
|
||||||
|
|
|
@ -73,7 +73,7 @@ const ReplyInThreadButton: React.FC<IReplyInThreadButton> = ({ mxEvent, closeMen
|
||||||
if (mxEvent.getThread() && !mxEvent.isThreadRoot) {
|
if (mxEvent.getThread() && !mxEvent.isThreadRoot) {
|
||||||
dis.dispatch<ShowThreadPayload>({
|
dis.dispatch<ShowThreadPayload>({
|
||||||
action: Action.ShowThread,
|
action: Action.ShowThread,
|
||||||
rootEvent: mxEvent.getThread().rootEvent,
|
rootEvent: mxEvent.getThread()!.rootEvent!,
|
||||||
initialEvent: mxEvent,
|
initialEvent: mxEvent,
|
||||||
scroll_into_view: true,
|
scroll_into_view: true,
|
||||||
highlighted: true,
|
highlighted: true,
|
||||||
|
@ -163,12 +163,12 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
||||||
// to obliterate the room - https://github.com/matrix-org/synapse/issues/4042
|
// to obliterate the room - https://github.com/matrix-org/synapse/issues/4042
|
||||||
// Similarly for encryption events, since redacting them "breaks everything"
|
// Similarly for encryption events, since redacting them "breaks everything"
|
||||||
const canRedact =
|
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.RoomServerAcl &&
|
||||||
this.props.mxEvent.getType() !== EventType.RoomEncryption;
|
this.props.mxEvent.getType() !== EventType.RoomEncryption;
|
||||||
|
|
||||||
let canPin =
|
let canPin =
|
||||||
room.currentState.mayClientSendStateEvent(EventType.RoomPinnedEvents, cli) &&
|
!!room?.currentState.mayClientSendStateEvent(EventType.RoomPinnedEvents, cli) &&
|
||||||
canPinEvent(this.props.mxEvent);
|
canPinEvent(this.props.mxEvent);
|
||||||
|
|
||||||
// HACK: Intentionally say we can't pin if the user doesn't want to use the functionality
|
// 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<IProps, IState>
|
||||||
private onPinClick = (): void => {
|
private onPinClick = (): void => {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
const room = cli.getRoom(this.props.mxEvent.getRoomId());
|
const room = cli.getRoom(this.props.mxEvent.getRoomId());
|
||||||
|
if (!room) return;
|
||||||
const eventId = this.props.mxEvent.getId();
|
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)) {
|
if (pinnedIds.includes(eventId)) {
|
||||||
pinnedIds.splice(pinnedIds.indexOf(eventId), 1);
|
pinnedIds.splice(pinnedIds.indexOf(eventId), 1);
|
||||||
|
@ -261,7 +262,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
||||||
event_ids: [...(room.getAccountData(ReadPinsEventId)?.getContent()?.event_ids || []), eventId],
|
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();
|
this.closeMenu();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -294,12 +295,13 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
||||||
|
|
||||||
private onCopyLinkClick = (e: ButtonEvent): void => {
|
private onCopyLinkClick = (e: ButtonEvent): void => {
|
||||||
e.preventDefault(); // So that we don't open the permalink
|
e.preventDefault(); // So that we don't open the permalink
|
||||||
|
if (!this.props.link) return;
|
||||||
copyPlaintext(this.props.link);
|
copyPlaintext(this.props.link);
|
||||||
this.closeMenu();
|
this.closeMenu();
|
||||||
};
|
};
|
||||||
|
|
||||||
private onCollapseReplyChainClick = (): void => {
|
private onCollapseReplyChainClick = (): void => {
|
||||||
this.props.collapseReplyChain();
|
this.props.collapseReplyChain?.();
|
||||||
this.closeMenu();
|
this.closeMenu();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -349,10 +351,12 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
const room = cli.getRoom(this.props.mxEvent.getRoomId());
|
const room = cli.getRoom(this.props.mxEvent.getRoomId());
|
||||||
const eventId = this.props.mxEvent.getId();
|
const eventId = this.props.mxEvent.getId();
|
||||||
return room.getPendingEvents().filter((e) => {
|
return (
|
||||||
const relation = e.getRelation();
|
room?.getPendingEvents().filter((e) => {
|
||||||
return relation?.rel_type === RelationType.Annotation && relation.event_id === eventId && filter(e);
|
const relation = e.getRelation();
|
||||||
});
|
return relation?.rel_type === RelationType.Annotation && relation.event_id === eventId && filter(e);
|
||||||
|
}) ?? []
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private getUnsentReactions(): MatrixEvent[] {
|
private getUnsentReactions(): MatrixEvent[] {
|
||||||
|
@ -380,7 +384,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
|
||||||
const eventStatus = mxEvent.status;
|
const eventStatus = mxEvent.status;
|
||||||
const unsentReactionsCount = this.getUnsentReactions().length;
|
const unsentReactionsCount = this.getUnsentReactions().length;
|
||||||
const contentActionable = isContentActionable(mxEvent);
|
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
|
// status is SENT before remote-echo, null after
|
||||||
const isSent = !eventStatus || eventStatus === EventStatus.SENT;
|
const isSent = !eventStatus || eventStatus === EventStatus.SENT;
|
||||||
const { timelineRenderingType, canReact, canSendMessages } = this.context;
|
const { timelineRenderingType, canReact, canSendMessages } = this.context;
|
||||||
|
|
|
@ -399,7 +399,7 @@ const RoomContextMenu: React.FC<IProps> = ({ room, onFinished, ...props }) => {
|
||||||
Modal.createDialog(
|
Modal.createDialog(
|
||||||
DevtoolsDialog,
|
DevtoolsDialog,
|
||||||
{
|
{
|
||||||
roomId: SdkContextClass.instance.roomViewStore.getRoomId(),
|
roomId: room.roomId,
|
||||||
},
|
},
|
||||||
"mx_DevtoolsDialog_wrapper",
|
"mx_DevtoolsDialog_wrapper",
|
||||||
);
|
);
|
||||||
|
|
|
@ -72,7 +72,7 @@ const ThreadListContextMenu: React.FC<ThreadListContextMenuProps> = ({
|
||||||
if (permalinkCreator) {
|
if (permalinkCreator) {
|
||||||
evt?.preventDefault();
|
evt?.preventDefault();
|
||||||
evt?.stopPropagation();
|
evt?.stopPropagation();
|
||||||
const matrixToUrl = permalinkCreator.forEvent(mxEvent.getId());
|
const matrixToUrl = permalinkCreator.forEvent(mxEvent.getId()!);
|
||||||
await copyPlaintext(matrixToUrl);
|
await copyPlaintext(matrixToUrl);
|
||||||
closeThreadOptions();
|
closeThreadOptions();
|
||||||
}
|
}
|
||||||
|
@ -84,9 +84,8 @@ const ThreadListContextMenu: React.FC<ThreadListContextMenuProps> = ({
|
||||||
onMenuToggle?.(menuDisplayed);
|
onMenuToggle?.(menuDisplayed);
|
||||||
}, [menuDisplayed, onMenuToggle]);
|
}, [menuDisplayed, onMenuToggle]);
|
||||||
|
|
||||||
const isMainSplitTimelineShown = !WidgetLayoutStore.instance.hasMaximisedWidget(
|
const room = MatrixClientPeg.get().getRoom(mxEvent.getRoomId());
|
||||||
MatrixClientPeg.get().getRoom(mxEvent.getRoomId()),
|
const isMainSplitTimelineShown = !!room && !WidgetLayoutStore.instance.hasMaximisedWidget(room);
|
||||||
);
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<ContextMenuTooltipButton
|
<ContextMenuTooltipButton
|
||||||
|
@ -104,7 +103,7 @@ const ThreadListContextMenu: React.FC<ThreadListContextMenuProps> = ({
|
||||||
className="mx_RoomTile_contextMenu"
|
className="mx_RoomTile_contextMenu"
|
||||||
compact
|
compact
|
||||||
rightAligned
|
rightAligned
|
||||||
{...contextMenuBelow(button.current.getBoundingClientRect())}
|
{...contextMenuBelow(button.current!.getBoundingClientRect())}
|
||||||
>
|
>
|
||||||
<IconizedContextMenuOptionList>
|
<IconizedContextMenuOptionList>
|
||||||
{isMainSplitTimelineShown && (
|
{isMainSplitTimelineShown && (
|
||||||
|
|
|
@ -63,8 +63,8 @@ export const WidgetContextMenu: React.FC<IProps> = ({
|
||||||
const widgetMessaging = WidgetMessagingStore.instance.getMessagingForUid(WidgetUtils.getWidgetUid(app));
|
const widgetMessaging = WidgetMessagingStore.instance.getMessagingForUid(WidgetUtils.getWidgetUid(app));
|
||||||
const canModify = userWidget || WidgetUtils.canUserModifyWidgets(roomId);
|
const canModify = userWidget || WidgetUtils.canUserModifyWidgets(roomId);
|
||||||
|
|
||||||
let streamAudioStreamButton;
|
let streamAudioStreamButton: JSX.Element | undefined;
|
||||||
if (getConfigLivestreamUrl() && WidgetType.JITSI.matches(app.type)) {
|
if (roomId && getConfigLivestreamUrl() && WidgetType.JITSI.matches(app.type)) {
|
||||||
const onStreamAudioClick = async (): Promise<void> => {
|
const onStreamAudioClick = async (): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
await startJitsiAudioLivestream(widgetMessaging!, roomId);
|
await startJitsiAudioLivestream(widgetMessaging!, roomId);
|
||||||
|
@ -87,12 +87,12 @@ export const WidgetContextMenu: React.FC<IProps> = ({
|
||||||
const pinnedWidgets = room ? WidgetLayoutStore.instance.getContainerWidgets(room, Container.Top) : [];
|
const pinnedWidgets = room ? WidgetLayoutStore.instance.getContainerWidgets(room, Container.Top) : [];
|
||||||
const widgetIndex = pinnedWidgets.findIndex((widget) => widget.id === app.id);
|
const widgetIndex = pinnedWidgets.findIndex((widget) => widget.id === app.id);
|
||||||
|
|
||||||
let editButton;
|
let editButton: JSX.Element | undefined;
|
||||||
if (canModify && WidgetUtils.isManagedByManager(app)) {
|
if (canModify && WidgetUtils.isManagedByManager(app)) {
|
||||||
const _onEditClick = (): void => {
|
const _onEditClick = (): void => {
|
||||||
if (onEditClick) {
|
if (onEditClick) {
|
||||||
onEditClick();
|
onEditClick();
|
||||||
} else {
|
} else if (room) {
|
||||||
WidgetUtils.editWidget(room, app);
|
WidgetUtils.editWidget(room, app);
|
||||||
}
|
}
|
||||||
onFinished();
|
onFinished();
|
||||||
|
@ -101,7 +101,7 @@ export const WidgetContextMenu: React.FC<IProps> = ({
|
||||||
editButton = <IconizedContextMenuOption onClick={_onEditClick} label={_t("Edit")} />;
|
editButton = <IconizedContextMenuOption onClick={_onEditClick} label={_t("Edit")} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
let snapshotButton;
|
let snapshotButton: JSX.Element | undefined;
|
||||||
const screenshotsEnabled = SettingsStore.getValue("enableWidgetScreenshots");
|
const screenshotsEnabled = SettingsStore.getValue("enableWidgetScreenshots");
|
||||||
if (screenshotsEnabled && widgetMessaging?.hasCapability(MatrixCapabilities.Screenshots)) {
|
if (screenshotsEnabled && widgetMessaging?.hasCapability(MatrixCapabilities.Screenshots)) {
|
||||||
const onSnapshotClick = (): void => {
|
const onSnapshotClick = (): void => {
|
||||||
|
@ -122,12 +122,12 @@ export const WidgetContextMenu: React.FC<IProps> = ({
|
||||||
snapshotButton = <IconizedContextMenuOption onClick={onSnapshotClick} label={_t("Take a picture")} />;
|
snapshotButton = <IconizedContextMenuOption onClick={onSnapshotClick} label={_t("Take a picture")} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
let deleteButton;
|
let deleteButton: JSX.Element | undefined;
|
||||||
if (onDeleteClick || canModify) {
|
if (onDeleteClick || canModify) {
|
||||||
const _onDeleteClick = (): void => {
|
const _onDeleteClick = (): void => {
|
||||||
if (onDeleteClick) {
|
if (onDeleteClick) {
|
||||||
onDeleteClick();
|
onDeleteClick();
|
||||||
} else {
|
} else if (roomId) {
|
||||||
// Show delete confirmation dialog
|
// Show delete confirmation dialog
|
||||||
Modal.createDialog(QuestionDialog, {
|
Modal.createDialog(QuestionDialog, {
|
||||||
title: _t("Delete Widget"),
|
title: _t("Delete Widget"),
|
||||||
|
@ -159,7 +159,7 @@ export const WidgetContextMenu: React.FC<IProps> = ({
|
||||||
app.creatorUserId === cli.getUserId();
|
app.creatorUserId === cli.getUserId();
|
||||||
|
|
||||||
const isLocalWidget = WidgetType.JITSI.matches(app.type);
|
const isLocalWidget = WidgetType.JITSI.matches(app.type);
|
||||||
let revokeButton;
|
let revokeButton: JSX.Element | undefined;
|
||||||
if (!userWidget && !isLocalWidget && isAllowedWidget) {
|
if (!userWidget && !isLocalWidget && isAllowedWidget) {
|
||||||
const opts: ApprovalOpts = { approved: undefined };
|
const opts: ApprovalOpts = { approved: undefined };
|
||||||
ModuleRunner.instance.invoke(WidgetLifecycle.PreLoadRequest, opts, new ElementWidget(app));
|
ModuleRunner.instance.invoke(WidgetLifecycle.PreLoadRequest, opts, new ElementWidget(app));
|
||||||
|
@ -182,7 +182,7 @@ export const WidgetContextMenu: React.FC<IProps> = ({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let moveLeftButton;
|
let moveLeftButton: JSX.Element | undefined;
|
||||||
if (showUnpin && widgetIndex > 0) {
|
if (showUnpin && widgetIndex > 0) {
|
||||||
const onClick = (): void => {
|
const onClick = (): void => {
|
||||||
if (!room) throw new Error("room must be defined");
|
if (!room) throw new Error("room must be defined");
|
||||||
|
@ -193,7 +193,7 @@ export const WidgetContextMenu: React.FC<IProps> = ({
|
||||||
moveLeftButton = <IconizedContextMenuOption onClick={onClick} label={_t("Move left")} />;
|
moveLeftButton = <IconizedContextMenuOption onClick={onClick} label={_t("Move left")} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
let moveRightButton;
|
let moveRightButton: JSX.Element | undefined;
|
||||||
if (showUnpin && widgetIndex < pinnedWidgets.length - 1) {
|
if (showUnpin && widgetIndex < pinnedWidgets.length - 1) {
|
||||||
const onClick = (): void => {
|
const onClick = (): void => {
|
||||||
if (!room) throw new Error("room must be defined");
|
if (!room) throw new Error("room must be defined");
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue