Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Conform more of the codebase to strictNullChecks #10842

Merged
merged 10 commits into from
May 11, 2023
1 change: 1 addition & 0 deletions src/components/structures/RoomSearchView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ export const RoomSearchView = forwardRef<ScrollPanel, Props>(

setHighlights(highlights);
setResults({ ...results }); // copy to force a refresh
return false;
},
(error) => {
if (aborted.current) {
Expand Down
7 changes: 4 additions & 3 deletions src/components/views/avatars/WidgetAvatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,15 @@ limitations under the License.
*/

import React, { ComponentProps } from "react";
import { IWidget } from "matrix-widget-api";
import classNames from "classnames";

import { IApp } from "../../../stores/WidgetStore";
import { IApp, isAppWidget } from "../../../stores/WidgetStore";
import BaseAvatar, { BaseAvatarType } from "./BaseAvatar";
import { mediaFromMxc } from "../../../customisations/Media";

interface IProps extends Omit<ComponentProps<BaseAvatarType>, "name" | "url" | "urls" | "height" | "width"> {
app: IApp;
app: IApp | IWidget;
height?: number;
width?: number;
}
Expand All @@ -46,7 +47,7 @@ const WidgetAvatar: React.FC<IProps> = ({ app, className, width = 20, height = 2
name={app.id}
className={classNames("mx_WidgetAvatar", className)}
// MSC2765
url={app.avatar_url ? mediaFromMxc(app.avatar_url).getSquareThumbnailHttp(20) : null}
url={isAppWidget(app) && app.avatar_url ? mediaFromMxc(app.avatar_url).getSquareThumbnailHttp(20) : null}
urls={iconUrls}
width={width}
height={height}
Expand Down
15 changes: 9 additions & 6 deletions src/components/views/context_menus/WidgetContextMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ limitations under the License.
*/

import React, { ComponentProps, useContext } from "react";
import { MatrixCapabilities } from "matrix-widget-api";
import { IWidget, MatrixCapabilities } from "matrix-widget-api";
import { logger } from "matrix-js-sdk/src/logger";
import { ApprovalOpts, WidgetLifecycle } from "@matrix-org/react-sdk-module-api/lib/lifecycles/WidgetLifecycle";

import IconizedContextMenu, { IconizedContextMenuOption, IconizedContextMenuOptionList } from "./IconizedContextMenu";
import { ChevronFace } from "../../structures/ContextMenu";
import { _t } from "../../../languageHandler";
import { IApp } from "../../../stores/WidgetStore";
import { isAppWidget } from "../../../stores/WidgetStore";
import WidgetUtils from "../../../utils/WidgetUtils";
import { WidgetMessagingStore } from "../../../stores/widgets/WidgetMessagingStore";
import RoomContext from "../../../contexts/RoomContext";
Expand All @@ -39,7 +39,7 @@ import { ModuleRunner } from "../../../modules/ModuleRunner";
import { ElementWidget } from "../../../stores/widgets/StopGapWidget";

interface IProps extends Omit<ComponentProps<typeof IconizedContextMenu>, "children"> {
app: IApp;
app: IWidget;
userWidget?: boolean;
showUnpin?: boolean;
// override delete handler
Expand Down Expand Up @@ -155,7 +155,9 @@ export const WidgetContextMenu: React.FC<IProps> = ({
}

const isAllowedWidget =
(app.eventId !== undefined && (SettingsStore.getValue("allowedWidgets", roomId)[app.eventId] ?? false)) ||
(isAppWidget(app) &&
app.eventId !== undefined &&
(SettingsStore.getValue("allowedWidgets", roomId)[app.eventId] ?? false)) ||
app.creatorUserId === cli.getUserId();

const isLocalWidget = WidgetType.JITSI.matches(app.type);
Expand All @@ -166,9 +168,10 @@ export const WidgetContextMenu: React.FC<IProps> = ({

if (!opts.approved) {
const onRevokeClick = (): void => {
logger.info("Revoking permission for widget to load: " + app.eventId);
const eventId = isAppWidget(app) ? app.eventId : undefined;
logger.info("Revoking permission for widget to load: " + eventId);
const current = SettingsStore.getValue("allowedWidgets", roomId);
if (app.eventId !== undefined) current[app.eventId] = false;
if (eventId !== undefined) current[eventId] = false;
const level = SettingsStore.firstSupportedLevel("allowedWidgets");
if (!level) throw new Error("level must be defined");
SettingsStore.setValue("allowedWidgets", roomId ?? null, level, current).catch((err) => {
Expand Down
52 changes: 38 additions & 14 deletions src/components/views/elements/AppTile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ limitations under the License.
import url from "url";
import React, { ContextType, createRef, CSSProperties, MutableRefObject, ReactNode } from "react";
import classNames from "classnames";
import { MatrixCapabilities } from "matrix-widget-api";
import { IWidget, MatrixCapabilities } from "matrix-widget-api";
import { Room, RoomEvent } from "matrix-js-sdk/src/models/room";
import { logger } from "matrix-js-sdk/src/logger";
import { ApprovalOpts, WidgetLifecycle } from "@matrix-org/react-sdk-module-api/lib/lifecycles/WidgetLifecycle";
Expand All @@ -40,7 +40,7 @@ import { ElementWidget, StopGapWidget } from "../../../stores/widgets/StopGapWid
import { WidgetContextMenu } from "../context_menus/WidgetContextMenu";
import WidgetAvatar from "../avatars/WidgetAvatar";
import LegacyCallHandler from "../../../LegacyCallHandler";
import { IApp } from "../../../stores/WidgetStore";
import { IApp, isAppWidget } from "../../../stores/WidgetStore";
import { Container, WidgetLayoutStore } from "../../../stores/widgets/WidgetLayoutStore";
import { OwnProfileStore } from "../../../stores/OwnProfileStore";
import { UPDATE_EVENT } from "../../../stores/AsyncStore";
Expand All @@ -54,7 +54,7 @@ import { SdkContextClass } from "../../../contexts/SDKContext";
import { ModuleRunner } from "../../../modules/ModuleRunner";

interface IProps {
app: IApp;
app: IWidget | IApp;
// If room is not specified then it is an account level widget
// which bypasses permission prompts as it was added explicitly by that user
room?: Room;
Expand Down Expand Up @@ -133,7 +133,10 @@ export default class AppTile extends React.Component<IProps, IState> {

// Tiles in miniMode are floating, and therefore not docked
if (!this.props.miniMode) {
ActiveWidgetStore.instance.dockWidget(this.props.app.id, this.props.app.roomId);
ActiveWidgetStore.instance.dockWidget(
this.props.app.id,
isAppWidget(this.props.app) ? this.props.app.roomId : null,
);
}

// The key used for PersistedElement
Expand Down Expand Up @@ -169,14 +172,17 @@ export default class AppTile extends React.Component<IProps, IState> {
if (opts.approved) return true;

const currentlyAllowedWidgets = SettingsStore.getValue("allowedWidgets", props.room.roomId);
const allowed = props.app.eventId !== undefined && (currentlyAllowedWidgets[props.app.eventId] ?? false);
const allowed =
isAppWidget(props.app) &&
props.app.eventId !== undefined &&
(currentlyAllowedWidgets[props.app.eventId] ?? false);
return allowed || props.userId === props.creatorUserId;
};

private onUserLeftRoom(): void {
const isActiveWidget = ActiveWidgetStore.instance.getWidgetPersistence(
this.props.app.id,
this.props.app.roomId,
isAppWidget(this.props.app) ? this.props.app.roomId : null,
);
if (isActiveWidget) {
// We just left the room that the active widget was from.
Expand All @@ -188,7 +194,10 @@ export default class AppTile extends React.Component<IProps, IState> {
this.reload();
} else {
// Otherwise just cancel its persistence.
ActiveWidgetStore.instance.destroyPersistentWidget(this.props.app.id, this.props.app.roomId);
ActiveWidgetStore.instance.destroyPersistentWidget(
this.props.app.id,
isAppWidget(this.props.app) ? this.props.app.roomId : null,
);
}
}
}
Expand Down Expand Up @@ -243,7 +252,10 @@ export default class AppTile extends React.Component<IProps, IState> {

if (this.state.hasPermissionToLoad && !hasPermissionToLoad) {
// Force the widget to be non-persistent (able to be deleted/forgotten)
ActiveWidgetStore.instance.destroyPersistentWidget(this.props.app.id, this.props.app.roomId);
ActiveWidgetStore.instance.destroyPersistentWidget(
this.props.app.id,
isAppWidget(this.props.app) ? this.props.app.roomId : null,
);
PersistedElement.destroyElement(this.persistKey);
this.sgWidget?.stopMessaging();
}
Expand Down Expand Up @@ -288,13 +300,21 @@ export default class AppTile extends React.Component<IProps, IState> {
this.unmounted = true;

if (!this.props.miniMode) {
ActiveWidgetStore.instance.undockWidget(this.props.app.id, this.props.app.roomId);
ActiveWidgetStore.instance.undockWidget(
this.props.app.id,
isAppWidget(this.props.app) ? this.props.app.roomId : null,
);
}

// Only tear down the widget if no other component is keeping it alive,
// because we support moving widgets between containers, in which case
// another component will keep it loaded throughout the transition
if (!ActiveWidgetStore.instance.isLive(this.props.app.id, this.props.app.roomId)) {
if (
!ActiveWidgetStore.instance.isLive(
this.props.app.id,
isAppWidget(this.props.app) ? this.props.app.roomId : null,
)
) {
this.endWidgetActions();
}

Expand Down Expand Up @@ -395,7 +415,10 @@ export default class AppTile extends React.Component<IProps, IState> {

// Delete the widget from the persisted store for good measure.
PersistedElement.destroyElement(this.persistKey);
ActiveWidgetStore.instance.destroyPersistentWidget(this.props.app.id, this.props.app.roomId);
ActiveWidgetStore.instance.destroyPersistentWidget(
this.props.app.id,
isAppWidget(this.props.app) ? this.props.app.roomId : null,
);

this.sgWidget?.stopMessaging({ forceDestroy: true });
}
Expand Down Expand Up @@ -441,9 +464,10 @@ export default class AppTile extends React.Component<IProps, IState> {

private grantWidgetPermission = (): void => {
const roomId = this.props.room?.roomId;
logger.info("Granting permission for widget to load: " + this.props.app.eventId);
const eventId = isAppWidget(this.props.app) ? this.props.app.eventId : undefined;
logger.info("Granting permission for widget to load: " + eventId);
const current = SettingsStore.getValue("allowedWidgets", roomId);
if (this.props.app.eventId !== undefined) current[this.props.app.eventId] = true;
if (eventId !== undefined) current[eventId] = true;
const level = SettingsStore.firstSupportedLevel("allowedWidgets")!;
SettingsStore.setValue("allowedWidgets", roomId ?? null, level, current)
.then(() => {
Expand Down Expand Up @@ -550,7 +574,7 @@ export default class AppTile extends React.Component<IProps, IState> {
};

public render(): React.ReactNode {
let appTileBody;
let appTileBody: JSX.Element;

// Note that there is advice saying allow-scripts shouldn't be used with allow-same-origin
// because that would allow the iframe to programmatically remove the sandbox attribute, but
Expand Down
4 changes: 2 additions & 2 deletions src/components/views/elements/ReplyChain.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ interface IProps {
// the latest event in this chain of replies
parentEv: MatrixEvent;
// called when the ReplyChain contents has changed, including EventTiles thereof
onHeightChanged: () => void;
onHeightChanged?: () => void;
permalinkCreator?: RoomPermalinkCreator;
// Specifies which layout to use.
layout?: Layout;
Expand Down Expand Up @@ -104,7 +104,7 @@ export default class ReplyChain extends React.Component<IProps, IState> {
}

public componentDidUpdate(): void {
this.props.onHeightChanged();
this.props.onHeightChanged?.();
this.trySetExpandableQuotes();
}

Expand Down
4 changes: 2 additions & 2 deletions src/components/views/messages/IBodyProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,15 @@ export interface IBodyProps {
highlightLink?: string;

/* callback called when dynamic content in events are loaded */
onHeightChanged: () => void;
onHeightChanged?: () => void;

showUrlPreview?: boolean;
forExport?: boolean;
maxImageHeight?: number;
replacingEventId?: string;
editState?: EditorStateTransfer;
onMessageAllowed: () => void; // TODO: Docs
permalinkCreator: RoomPermalinkCreator;
permalinkCreator?: RoomPermalinkCreator;
mediaEventHelper: MediaEventHelper;

/*
Expand Down
2 changes: 1 addition & 1 deletion src/components/views/messages/MImageBody.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {

private onImageLoad = (): void => {
this.clearBlurhashTimeout();
this.props.onHeightChanged();
this.props.onHeightChanged?.();

let loadedImageDimensions: IState["loadedImageDimensions"];

Expand Down
4 changes: 2 additions & 2 deletions src/components/views/messages/MVideoBody.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ export default class MVideoBody extends React.PureComponent<IBodyProps, IState>
decryptedThumbnailUrl: thumbnailUrl,
decryptedBlob: await this.props.mediaEventHelper.sourceBlob.value,
});
this.props.onHeightChanged();
this.props.onHeightChanged?.();
} else {
logger.log("NOT preloading video");
const content = this.props.mxEvent.getContent<IMediaEventContent>();
Expand Down Expand Up @@ -216,7 +216,7 @@ export default class MVideoBody extends React.PureComponent<IBodyProps, IState>
this.videoRef.current.play();
},
);
this.props.onHeightChanged();
this.props.onHeightChanged?.();
};

protected get showFileBody(): boolean {
Expand Down
2 changes: 1 addition & 1 deletion src/components/views/messages/TextualBody.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ export default class TextualBody extends React.Component<IBodyProps, IState> {

// By expanding/collapsing we changed
// the height, therefore we call this
this.props.onHeightChanged();
this.props.onHeightChanged?.();
};

div.appendChild(button);
Expand Down
14 changes: 7 additions & 7 deletions src/components/views/rooms/AppsDrawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import React from "react";
import classNames from "classnames";
import { Resizable } from "re-resizable";
import { Room } from "matrix-js-sdk/src/models/room";
import { IWidget } from "matrix-widget-api";

import AppTile from "../elements/AppTile";
import dis from "../../../dispatcher/dispatcher";
Expand All @@ -32,7 +33,6 @@ import PercentageDistributor from "../../../resizer/distributors/percentage";
import { Container, WidgetLayoutStore } from "../../../stores/widgets/WidgetLayoutStore";
import { clamp, percentageOf, percentageWithin } from "../../../utils/numbers";
import UIStore from "../../../stores/UIStore";
import { IApp } from "../../../stores/WidgetStore";
import { ActionPayload } from "../../../dispatcher/payloads";
import Spinner from "../elements/Spinner";

Expand All @@ -46,9 +46,9 @@ interface IProps {

interface IState {
apps: {
[Container.Top]: IApp[];
[Container.Center]: IApp[];
[Container.Right]?: IApp[];
[Container.Top]: IWidget[];
[Container.Center]: IWidget[];
[Container.Right]?: IWidget[];
};
resizingVertical: boolean; // true when changing the height of the apps drawer
resizingHorizontal: boolean; // true when changing the distribution of the width between widgets
Expand Down Expand Up @@ -147,7 +147,7 @@ export default class AppsDrawer extends React.Component<IProps, IState> {
this.loadResizerPreferences();
};

private getAppsHash = (apps: IApp[]): string => apps.map((app) => app.id).join("~");
private getAppsHash = (apps: IWidget[]): string => apps.map((app) => app.id).join("~");

public componentDidUpdate(prevProps: IProps, prevState: IState): void {
if (prevProps.userId !== this.props.userId || prevProps.room !== this.props.room) {
Expand Down Expand Up @@ -210,8 +210,8 @@ export default class AppsDrawer extends React.Component<IProps, IState> {
[Container.Top]: WidgetLayoutStore.instance.getContainerWidgets(this.props.room, Container.Top),
[Container.Center]: WidgetLayoutStore.instance.getContainerWidgets(this.props.room, Container.Center),
});
private topApps = (): IApp[] => this.state.apps[Container.Top];
private centerApps = (): IApp[] => this.state.apps[Container.Center];
private topApps = (): IWidget[] => this.state.apps[Container.Top];
private centerApps = (): IWidget[] => this.state.apps[Container.Center];

private updateApps = (): void => {
if (this.unmounted) return;
Expand Down
3 changes: 2 additions & 1 deletion src/components/views/rooms/EventTile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
evt.preventDefault();
evt.stopPropagation();
const { permalinkCreator, mxEvent } = this.props;
if (!permalinkCreator) return;
const matrixToUrl = permalinkCreator.forEvent(mxEvent.getId()!);
await copyPlaintext(matrixToUrl);
};
Expand Down Expand Up @@ -1439,7 +1440,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
const SafeEventTile = forwardRef((props: EventTileProps, ref: RefObject<UnwrappedEventTile>) => {
return (
<>
<TileErrorBoundary mxEvent={props.mxEvent} layout={props.layout}>
<TileErrorBoundary mxEvent={props.mxEvent} layout={props.layout ?? Layout.Group}>
<UnwrappedEventTile ref={ref} {...props} />
</TileErrorBoundary>
</>
Expand Down
4 changes: 2 additions & 2 deletions src/components/views/rooms/LinkPreviewGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ interface IProps {
links: string[]; // the URLs to be previewed
mxEvent: MatrixEvent; // the Event associated with the preview
onCancelClick(): void; // called when the preview's cancel ('hide') button is clicked
onHeightChanged(): void; // called when the preview's contents has loaded
onHeightChanged?(): void; // called when the preview's contents has loaded
}

const LinkPreviewGroup: React.FC<IProps> = ({ links, mxEvent, onCancelClick, onHeightChanged }) => {
Expand All @@ -49,7 +49,7 @@ const LinkPreviewGroup: React.FC<IProps> = ({ links, mxEvent, onCancelClick, onH
);

useEffect(() => {
onHeightChanged();
onHeightChanged?.();
}, [onHeightChanged, expanded, previews]);

const showPreviews = expanded ? previews : previews.slice(0, INITIAL_NUM_PREVIEWS);
Expand Down
Loading