Apply strictNullChecks to src/stores/widgets/* (#10324)

* Apply `strictNullChecks` to src/stores/widgets/*

* Iterate

* Iterate
This commit is contained in:
Michael Telatynski 2023-03-08 11:48:58 +00:00 committed by GitHub
parent 0c1c3f1cde
commit c0e40217f3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 59 additions and 55 deletions

View file

@ -458,7 +458,7 @@ export default class LegacyCallHandler extends EventEmitter {
public getAllActiveCallsForPip(roomId: string): MatrixCall[] {
const room = MatrixClientPeg.get().getRoom(roomId);
if (WidgetLayoutStore.instance.hasMaximisedWidget(room)) {
if (room && WidgetLayoutStore.instance.hasMaximisedWidget(room)) {
// This checks if there is space for the call view in the aux panel
// If there is no space any call should be displayed in PiP
return this.getAllActiveCalls();

View file

@ -67,7 +67,7 @@ export const WidgetContextMenu: React.FC<IProps> = ({
if (getConfigLivestreamUrl() && WidgetType.JITSI.matches(app.type)) {
const onStreamAudioClick = async (): Promise<void> => {
try {
await startJitsiAudioLivestream(widgetMessaging, roomId);
await startJitsiAudioLivestream(widgetMessaging!, roomId);
} catch (err) {
logger.error("Failed to start livestream", err);
// XXX: won't i18n well, but looks like widget api only support 'message'?
@ -84,7 +84,7 @@ export const WidgetContextMenu: React.FC<IProps> = ({
);
}
const pinnedWidgets = WidgetLayoutStore.instance.getContainerWidgets(room, Container.Top);
const pinnedWidgets = room ? WidgetLayoutStore.instance.getContainerWidgets(room, Container.Top) : [];
const widgetIndex = pinnedWidgets.findIndex((widget) => widget.id === app.id);
let editButton;
@ -196,6 +196,7 @@ export const WidgetContextMenu: React.FC<IProps> = ({
let moveRightButton;
if (showUnpin && widgetIndex < pinnedWidgets.length - 1) {
const onClick = (): void => {
if (!room) throw new Error("room must be defined");
WidgetLayoutStore.instance.moveWithinContainer(room, Container.Top, app, 1);
onFinished();
};

View file

@ -99,7 +99,7 @@ interface IState {
hasPermissionToLoad: boolean;
// Wait for user profile load to display correct name
isUserProfileReady: boolean;
error: Error;
error: Error | null;
menuDisplayed: boolean;
requiresClient: boolean;
}
@ -124,7 +124,7 @@ export default class AppTile extends React.Component<IProps, IState> {
private iframe: HTMLIFrameElement; // ref to the iframe (callback style)
private allowedWidgetsWatchRef: string;
private persistKey: string;
private sgWidget: StopGapWidget;
private sgWidget: StopGapWidget | null;
private dispatcherRef: string;
private unmounted: boolean;
@ -202,7 +202,7 @@ export default class AppTile extends React.Component<IProps, IState> {
private determineInitialRequiresClientState(): boolean {
try {
const mockWidget = new ElementWidget(this.props.app);
const widgetApi = WidgetMessagingStore.instance.getMessaging(mockWidget, this.props.room.roomId);
const widgetApi = WidgetMessagingStore.instance.getMessaging(mockWidget, this.props.room?.roomId);
if (widgetApi) {
// Load value from existing API to prevent resetting the requiresClient value on layout changes.
return widgetApi.hasCapability(ElementWidgetCapabilities.RequiresClient);
@ -310,9 +310,9 @@ export default class AppTile extends React.Component<IProps, IState> {
}
private setupSgListeners(): void {
this.sgWidget.on("preparing", this.onWidgetPreparing);
this.sgWidget?.on("preparing", this.onWidgetPreparing);
// emits when the capabilities have been set up or changed
this.sgWidget.on("capabilitiesNotified", this.onWidgetCapabilitiesNotified);
this.sgWidget?.on("capabilitiesNotified", this.onWidgetCapabilitiesNotified);
}
private stopSgListeners(): void {
@ -336,7 +336,7 @@ export default class AppTile extends React.Component<IProps, IState> {
}
private startWidget(): void {
this.sgWidget.prepare().then(() => {
this.sgWidget?.prepare().then(() => {
if (this.unmounted) return;
this.setState({ initialising: false });
});
@ -406,7 +406,7 @@ export default class AppTile extends React.Component<IProps, IState> {
private onWidgetCapabilitiesNotified = (): void => {
this.setState({
requiresClient: this.sgWidget.widgetApi.hasCapability(ElementWidgetCapabilities.RequiresClient),
requiresClient: !!this.sgWidget?.widgetApi?.hasCapability(ElementWidgetCapabilities.RequiresClient),
});
};
@ -415,7 +415,7 @@ export default class AppTile extends React.Component<IProps, IState> {
case "m.sticker":
if (
payload.widgetId === this.props.app.id &&
this.sgWidget.widgetApi.hasCapability(MatrixCapabilities.StickerSending)
this.sgWidget?.widgetApi?.hasCapability(MatrixCapabilities.StickerSending)
) {
dis.dispatch({
action: "post_sticker_message",
@ -444,8 +444,8 @@ export default class AppTile extends React.Component<IProps, IState> {
logger.info("Granting permission for widget to load: " + this.props.app.eventId);
const current = SettingsStore.getValue("allowedWidgets", roomId);
if (this.props.app.eventId !== undefined) current[this.props.app.eventId] = true;
const level = SettingsStore.firstSupportedLevel("allowedWidgets");
SettingsStore.setValue("allowedWidgets", roomId, level, current)
const level = SettingsStore.firstSupportedLevel("allowedWidgets")!;
SettingsStore.setValue("allowedWidgets", roomId ?? null, level, current)
.then(() => {
this.setState({ hasPermissionToLoad: true });
@ -501,7 +501,7 @@ export default class AppTile extends React.Component<IProps, IState> {
this.resetWidget(this.props);
this.startMessaging();
if (this.iframe) {
if (this.iframe && this.sgWidget) {
// Reload iframe
this.iframe.src = this.sgWidget.embedUrl;
}
@ -519,7 +519,7 @@ export default class AppTile extends React.Component<IProps, IState> {
// window.open(this._getPopoutUrl(), '_blank', 'noopener=yes');
Object.assign(document.createElement("a"), {
target: "_blank",
href: this.sgWidget.popoutUrl,
href: this.sgWidget?.popoutUrl,
rel: "noreferrer noopener",
}).click();
};
@ -676,7 +676,7 @@ export default class AppTile extends React.Component<IProps, IState> {
if (this.state.menuDisplayed) {
contextMenu = (
<WidgetContextMenu
{...aboveLeftOf(this.contextMenuButton.current.getBoundingClientRect(), null)}
{...aboveLeftOf(this.contextMenuButton.current.getBoundingClientRect())}
app={this.props.app}
onFinished={this.closeContextMenu}
showUnpin={!this.props.userWidget}

View file

@ -43,7 +43,7 @@ export class IntegrationManagerInstance {
public get name(): string {
const parsed = url.parse(this.uiUrl);
return parsed.host;
return parsed.host ?? "";
}
public get trimmedApiUrl(): string {

View file

@ -75,7 +75,7 @@ export default class ActiveWidgetStore extends EventEmitter {
this.setWidgetPersistence(widgetId, roomId, false);
}
public setWidgetPersistence(widgetId: string, roomId: string, val: boolean): void {
public setWidgetPersistence(widgetId: string, roomId: string | null, val: boolean): void {
const isPersisted = this.getWidgetPersistence(widgetId, roomId);
if (isPersisted && !val) {
@ -88,7 +88,7 @@ export default class ActiveWidgetStore extends EventEmitter {
this.emit(ActiveWidgetStoreEvent.Persistence);
}
public getWidgetPersistence(widgetId: string, roomId: string): boolean {
public getWidgetPersistence(widgetId: string, roomId: string | null): boolean {
return this.persistentWidgetId === widgetId && this.persistentRoomId === roomId;
}

View file

@ -285,7 +285,7 @@ export class StopGapWidget extends EventEmitter {
this.messaging = new ClientWidgetApi(this.mockWidget, iframe, driver);
this.messaging.on("preparing", () => this.emit("preparing"));
this.messaging.on("ready", () => {
WidgetMessagingStore.instance.storeMessaging(this.mockWidget, this.roomId, this.messaging);
WidgetMessagingStore.instance.storeMessaging(this.mockWidget, this.roomId, this.messaging!);
this.emit("ready");
});
this.messaging.on("capabilitiesNotified", () => this.emit("capabilitiesNotified"));
@ -347,7 +347,7 @@ export class StopGapWidget extends EventEmitter {
if (this.messaging?.hasCapability(MatrixCapabilities.AlwaysOnScreen)) {
ActiveWidgetStore.instance.setWidgetPersistence(
this.mockWidget.id,
this.roomId,
this.roomId ?? null,
ev.detail.data.value,
);
ev.preventDefault();
@ -393,14 +393,12 @@ export class StopGapWidget extends EventEmitter {
const integType = data?.integType as string;
const integId = <string>data?.integId;
const roomId = SdkContextClass.instance.roomViewStore.getRoomId();
const room = roomId ? this.client.getRoom(roomId) : undefined;
if (!room) return;
// noinspection JSIgnoredPromiseFromCall
IntegrationManagers.sharedInstance()
.getPrimaryManager()
.open(
this.client.getRoom(SdkContextClass.instance.roomViewStore.getRoomId()),
`type_${integType}`,
integId,
);
IntegrationManagers.sharedInstance()?.getPrimaryManager()?.open(room, `type_${integType}`, integId);
},
);
}
@ -434,7 +432,7 @@ export class StopGapWidget extends EventEmitter {
if (managers.hasManager()) {
// TODO: Pick the right manager for the widget
const defaultManager = managers.getPrimaryManager();
if (WidgetUtils.isScalarUrl(defaultManager.apiUrl)) {
if (defaultManager && WidgetUtils.isScalarUrl(defaultManager.apiUrl)) {
const scalar = defaultManager.getScalarClient();
this.scalarToken = await scalar.getScalarToken();
}
@ -452,7 +450,10 @@ export class StopGapWidget extends EventEmitter {
* @param opts
*/
public stopMessaging(opts = { forceDestroy: false }): void {
if (!opts?.forceDestroy && ActiveWidgetStore.instance.getWidgetPersistence(this.mockWidget.id, this.roomId)) {
if (
!opts?.forceDestroy &&
ActiveWidgetStore.instance.getWidgetPersistence(this.mockWidget.id, this.roomId ?? null)
) {
logger.log("Skipping destroy - persistent widget");
return;
}
@ -500,9 +501,11 @@ export class StopGapWidget extends EventEmitter {
let isBeforeMark = true;
const room = this.client.getRoom(ev.getRoomId()!);
if (!room) return;
// Timelines are most recent last, so reverse the order and limit ourselves to 100 events
// to avoid overusing the CPU.
const timeline = this.client.getRoom(ev.getRoomId()!).getLiveTimeline();
const timeline = room.getLiveTimeline();
const events = arrayFastClone(timeline.getEvents()).reverse().slice(0, 100);
for (const timelineEvent of events) {
@ -533,7 +536,7 @@ export class StopGapWidget extends EventEmitter {
}
const raw = ev.getEffectiveEvent();
this.messaging.feedEvent(raw as IRoomEvent, this.eventListenerRoomId).catch((e) => {
this.messaging.feedEvent(raw as IRoomEvent, this.eventListenerRoomId!).catch((e) => {
logger.error("Error sending event to widget: ", e);
});
}

View file

@ -113,7 +113,7 @@ export class StopGapWidgetDriver extends WidgetDriver {
this.allowedCapabilities.add("visibility");
} else if (
virtual &&
new URL(SdkConfig.get("element_call").url ?? DEFAULTS.element_call.url).origin === this.forWidget.origin
new URL(SdkConfig.get("element_call").url ?? DEFAULTS.element_call.url!).origin === this.forWidget.origin
) {
// This is a trusted Element Call widget that we control
this.allowedCapabilities.add(MatrixCapabilities.AlwaysOnScreen);
@ -202,8 +202,8 @@ export class StopGapWidgetDriver extends WidgetDriver {
widget: this.forWidget,
widgetKind: this.forWidgetKind,
}).finished;
(result.approved || []).forEach((cap) => allowedSoFar.add(cap));
rememberApproved = result.remember;
result?.approved?.forEach((cap) => allowedSoFar.add(cap));
rememberApproved = !!result?.remember;
} catch (e) {
logger.error("Non-fatal error getting capabilities: ", e);
}
@ -267,7 +267,7 @@ export class StopGapWidgetDriver extends WidgetDriver {
const client = MatrixClientPeg.get();
if (encrypted) {
const deviceInfoMap = await client.crypto.deviceList.downloadKeys(Object.keys(contentMap), false);
const deviceInfoMap = await client.crypto!.deviceList.downloadKeys(Object.keys(contentMap), false);
await Promise.all(
Object.entries(contentMap).flatMap(([userId, userContentMap]) =>
@ -313,7 +313,7 @@ export class StopGapWidgetDriver extends WidgetDriver {
? roomIds.includes(Symbols.AnyRoom)
? client.getVisibleRooms()
: roomIds.map((r) => client.getRoom(r))
: [client.getRoom(SdkContextClass.instance.roomViewStore.getRoomId())];
: [client.getRoom(SdkContextClass.instance.roomViewStore.getRoomId()!)];
return targetRooms.filter((r) => !!r) as Room[];
}
@ -356,7 +356,7 @@ export class StopGapWidgetDriver extends WidgetDriver {
const allResults: IRoomEvent[] = [];
for (const room of rooms) {
const results: MatrixEvent[] = [];
const state: Map<string, MatrixEvent> = room.currentState.events.get(eventType);
const state = room.currentState.events.get(eventType);
if (state) {
if (stateKey === "" || !!stateKey) {
const forKey = state.get(stateKey);

View file

@ -17,7 +17,6 @@
import { Room } from "matrix-js-sdk/src/models/room";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { RoomStateEvent } from "matrix-js-sdk/src/models/room-state";
import { Optional } from "matrix-events-sdk";
import { compare } from "matrix-js-sdk/src/utils";
import SettingsStore from "../../settings/SettingsStore";
@ -63,7 +62,7 @@ export interface IStoredLayout {
// this only applies to the top container currently, and that container
// will take the highest value among widgets in the container. Clamped
// to 0-100 and may have minimums imposed on it.
height?: number;
height?: number | null;
// TODO: [Deferred] Maximizing (fullscreen) widgets by default.
}
@ -147,6 +146,7 @@ export class WidgetLayoutStore extends ReadyWatchingStore {
}
private updateAllRooms = (): void => {
if (!this.matrixClient) return;
this.byRoom = {};
for (const room of this.matrixClient.getVisibleRooms()) {
this.recalculateRoom(room);
@ -267,7 +267,7 @@ export class WidgetLayoutStore extends ReadyWatchingStore {
const userWidgetLayout = userLayout?.widgets?.[widget.id];
if (Number.isFinite(userWidgetLayout?.width) || Number.isFinite(widgetLayout?.width)) {
const val = userWidgetLayout?.width || widgetLayout?.width;
const val = (userWidgetLayout?.width || widgetLayout?.width)!;
const normalized = clamp(val, MIN_WIDGET_WIDTH_PCT, 100);
widths.push(normalized);
doAutobalance = false; // a manual width was specified
@ -278,7 +278,7 @@ export class WidgetLayoutStore extends ReadyWatchingStore {
if (widgetLayout?.height || userWidgetLayout?.height) {
const defRoomHeight = defaultNumber(widgetLayout?.height, MIN_WIDGET_HEIGHT_PCT);
const h = defaultNumber(userWidgetLayout?.height, defRoomHeight);
maxHeight = Math.max(maxHeight, clamp(h, MIN_WIDGET_HEIGHT_PCT, 100));
maxHeight = Math.max(maxHeight ?? 0, clamp(h, MIN_WIDGET_HEIGHT_PCT, 100));
}
}
if (doAutobalance) {
@ -346,11 +346,11 @@ export class WidgetLayoutStore extends ReadyWatchingStore {
}
}
public getContainerWidgets(room: Optional<Room>, container: Container): IApp[] {
return this.byRoom[room?.roomId]?.[container]?.ordered || [];
public getContainerWidgets(room: Room, container: Container): IApp[] {
return this.byRoom[room.roomId]?.[container]?.ordered || [];
}
public isInContainer(room: Optional<Room>, widget: IApp, container: Container): boolean {
public isInContainer(room: Room, widget: IApp, container: Container): boolean {
return this.getContainerWidgets(room, container).some((w) => w.id === widget.id);
}
@ -406,7 +406,7 @@ export class WidgetLayoutStore extends ReadyWatchingStore {
return this.byRoom[room.roomId]?.[container]?.height ?? null; // let the default get returned if needed
}
public setContainerHeight(room: Room, container: Container, height?: number): void {
public setContainerHeight(room: Room, container: Container, height?: number | null): void {
const widgets = this.getContainerWidgets(room, container);
const widths = this.byRoom[room.roomId]?.[container]?.distributions;
const localLayout: Record<string, IStoredLayout> = {};
@ -438,7 +438,7 @@ export class WidgetLayoutStore extends ReadyWatchingStore {
container: container,
width: widths?.[i],
index: i,
height: height,
height,
};
});
this.updateUserLayout(room, localLayout);
@ -477,7 +477,7 @@ export class WidgetLayoutStore extends ReadyWatchingStore {
this.updateUserLayout(room, newLayout);
}
public hasMaximisedWidget(room?: Room): boolean {
public hasMaximisedWidget(room: Room): boolean {
return this.getContainerWidgets(room, Container.Center).length > 0;
}
@ -517,7 +517,7 @@ export class WidgetLayoutStore extends ReadyWatchingStore {
const ret: [IApp, Container][] = [];
for (const container in containers) {
const widgets = containers[container as Container].ordered;
const widgets = containers[container as Container]!.ordered;
for (const widget of widgets) {
ret.push([widget, container as Container]);
}

View file

@ -58,7 +58,7 @@ export class WidgetMessagingStore extends AsyncStoreWithClient<{}> {
this.widgetMap.clear();
}
public storeMessaging(widget: Widget, roomId: string, widgetApi: ClientWidgetApi): void {
public storeMessaging(widget: Widget, roomId: string | undefined, widgetApi: ClientWidgetApi): void {
this.stopMessaging(widget, roomId);
const uid = WidgetUtils.calcWidgetUid(widget.id, roomId);
this.widgetMap.set(uid, widgetApi);
@ -66,11 +66,11 @@ export class WidgetMessagingStore extends AsyncStoreWithClient<{}> {
this.emit(WidgetMessagingStoreEvent.StoreMessaging, uid, widgetApi);
}
public stopMessaging(widget: Widget, roomId: string): void {
public stopMessaging(widget: Widget, roomId: string | undefined): void {
this.stopMessagingByUid(WidgetUtils.calcWidgetUid(widget.id, roomId));
}
public getMessaging(widget: Widget, roomId: string): ClientWidgetApi {
public getMessaging(widget: Widget, roomId: string | undefined): ClientWidgetApi | undefined {
return this.widgetMap.get(WidgetUtils.calcWidgetUid(widget.id, roomId));
}
@ -88,7 +88,7 @@ export class WidgetMessagingStore extends AsyncStoreWithClient<{}> {
* @param {string} widgetUid The widget UID.
* @returns {ClientWidgetApi} The widget API, or a falsy value if not found.
*/
public getMessagingForUid(widgetUid: string): ClientWidgetApi {
public getMessagingForUid(widgetUid: string): ClientWidgetApi | undefined {
return this.widgetMap.get(widgetUid);
}
}

View file

@ -57,7 +57,7 @@ export default class WidgetUtils {
* @return Boolean -- true if the user can modify widgets in this room
* @throws Error -- specifies the error reason
*/
public static canUserModifyWidgets(roomId: string): boolean {
public static canUserModifyWidgets(roomId?: string): boolean {
if (!roomId) {
logger.warn("No room ID specified");
return false;

View file

@ -541,7 +541,7 @@ describe("RoomHeader", () => {
await withCall(async (call) => {
await call.connect();
const messaging = WidgetMessagingStore.instance.getMessagingForUid(WidgetUtils.getWidgetUid(call.widget));
const messaging = WidgetMessagingStore.instance.getMessagingForUid(WidgetUtils.getWidgetUid(call.widget))!;
renderHeader({ viewingCall: true, activeCall: call });
// Should start with Freedom selected