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

Commit 81a4498

Browse files
author
Kerry
authored
Apply strictNullChecks to src/utils/*!exportUtils (#10455
* Apply `strictNullChecks` to `src/utils/exportUtils` * strict fix * fix strictNullChecks issues in some utils * fix error message * test coverage * lint * more strictNullChecks * small optimisation for getUniqueRoomsWithIndividuals * tidy * test coverage
1 parent 4ed6e39 commit 81a4498

18 files changed

+143
-81
lines changed

src/components/structures/InteractiveAuth.tsx

+6-5
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17+
import React, { createRef } from "react";
1718
import {
1819
AuthType,
1920
IAuthData,
@@ -23,8 +24,8 @@ import {
2324
IStageStatus,
2425
} from "matrix-js-sdk/src/interactive-auth";
2526
import { MatrixClient } from "matrix-js-sdk/src/client";
26-
import React, { createRef } from "react";
2727
import { logger } from "matrix-js-sdk/src/logger";
28+
import { UIAResponse } from "matrix-js-sdk/src/@types/uia";
2829

2930
import getEntryComponentForLoginType, { IStageComponent } from "../views/auth/InteractiveAuthEntryComponents";
3031
import Spinner from "../views/elements/Spinner";
@@ -39,7 +40,7 @@ type InteractiveAuthCallbackSuccess = (
3940
type InteractiveAuthCallbackFailure = (success: false, response: IAuthData | Error) => void;
4041
export type InteractiveAuthCallback = InteractiveAuthCallbackSuccess & InteractiveAuthCallbackFailure;
4142

42-
interface IProps {
43+
export interface InteractiveAuthProps<T> {
4344
// matrix client to use for UI auth requests
4445
matrixClient: MatrixClient;
4546
// response from initial request. If not supplied, will do a request on mount.
@@ -61,7 +62,7 @@ interface IProps {
6162
continueText?: string;
6263
continueKind?: string;
6364
// callback
64-
makeRequest(auth: IAuthData | null): Promise<IAuthData>;
65+
makeRequest(auth?: IAuthData): Promise<UIAResponse<T>>;
6566
// callback called when the auth process has finished,
6667
// successfully or unsuccessfully.
6768
// @param {boolean} status True if the operation requiring
@@ -92,14 +93,14 @@ interface IState {
9293
submitButtonEnabled: boolean;
9394
}
9495

95-
export default class InteractiveAuthComponent extends React.Component<IProps, IState> {
96+
export default class InteractiveAuthComponent<T> extends React.Component<InteractiveAuthProps<T>, IState> {
9697
private readonly authLogic: InteractiveAuth;
9798
private readonly intervalId: number | null = null;
9899
private readonly stageComponent = createRef<IStageComponent>();
99100

100101
private unmounted = false;
101102

102-
public constructor(props: IProps) {
103+
public constructor(props: InteractiveAuthProps<T>) {
103104
super(props);
104105

105106
this.state = {

src/components/views/dialogs/InteractiveAuthDialog.tsx

+9-11
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@ import { AuthType, IAuthData } from "matrix-js-sdk/src/interactive-auth";
2222

2323
import { _t } from "../../../languageHandler";
2424
import AccessibleButton from "../elements/AccessibleButton";
25-
import InteractiveAuth, { ERROR_USER_CANCELLED, InteractiveAuthCallback } from "../../structures/InteractiveAuth";
25+
import InteractiveAuth, {
26+
ERROR_USER_CANCELLED,
27+
InteractiveAuthCallback,
28+
InteractiveAuthProps,
29+
} from "../../structures/InteractiveAuth";
2630
import { SSOAuthEntry } from "../auth/InteractiveAuthEntryComponents";
2731
import BaseDialog from "./BaseDialog";
2832

@@ -37,17 +41,11 @@ type DialogAesthetics = Partial<{
3741
};
3842
}>;
3943

40-
export interface InteractiveAuthDialogProps {
44+
export interface InteractiveAuthDialogProps<T = unknown>
45+
extends Pick<InteractiveAuthProps<T>, "makeRequest" | "authData"> {
4146
// matrix client to use for UI auth requests
4247
matrixClient: MatrixClient;
4348

44-
// response from initial request. If not supplied, will do a request on
45-
// mount.
46-
authData?: IAuthData;
47-
48-
// callback
49-
makeRequest: (auth: IAuthData) => Promise<IAuthData>;
50-
5149
// Optional title and body to show when not showing a particular stage
5250
title?: string;
5351
body?: string;
@@ -83,8 +81,8 @@ interface IState {
8381
uiaStagePhase: number | null;
8482
}
8583

86-
export default class InteractiveAuthDialog extends React.Component<InteractiveAuthDialogProps, IState> {
87-
public constructor(props: InteractiveAuthDialogProps) {
84+
export default class InteractiveAuthDialog<T> extends React.Component<InteractiveAuthDialogProps<T>, IState> {
85+
public constructor(props: InteractiveAuthDialogProps<T>) {
8886
super(props);
8987

9088
this.state = {

src/utils/AutoDiscoveryUtils.tsx

+11-6
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import { logger } from "matrix-js-sdk/src/logger";
2020
import { IClientWellKnown } from "matrix-js-sdk/src/matrix";
2121

2222
import { _t, UserFriendlyError } from "../languageHandler";
23-
import { makeType } from "./TypeUtils";
2423
import SdkConfig from "../SdkConfig";
2524
import { ValidatedServerConfig } from "./ValidatedServerConfig";
2625

@@ -43,7 +42,7 @@ export default class AutoDiscoveryUtils {
4342
* @param {string | Error} error The error to check
4443
* @returns {boolean} True if the error is a liveliness error.
4544
*/
46-
public static isLivelinessError(error: string | Error): boolean {
45+
public static isLivelinessError(error?: string | Error | null): boolean {
4746
if (!error) return false;
4847
return !!LIVELINESS_DISCOVERY_ERRORS.find((e) =>
4948
typeof error === "string" ? e === error : e === error.message,
@@ -197,7 +196,7 @@ export default class AutoDiscoveryUtils {
197196
): ValidatedServerConfig {
198197
if (!discoveryResult || !discoveryResult["m.homeserver"]) {
199198
// This shouldn't happen without major misconfiguration, so we'll log a bit of information
200-
// in the log so we can find this bit of codee but otherwise tell teh user "it broke".
199+
// in the log so we can find this bit of code but otherwise tell the user "it broke".
201200
logger.error("Ended up in a state of not knowing which homeserver to connect to.");
202201
throw new UserFriendlyError("Unexpected error resolving homeserver configuration");
203202
}
@@ -216,7 +215,7 @@ export default class AutoDiscoveryUtils {
216215
// of Element.
217216
let preferredIdentityUrl = defaultConfig && defaultConfig["isUrl"];
218217
if (isResult && isResult.state === AutoDiscovery.SUCCESS) {
219-
preferredIdentityUrl = isResult["base_url"];
218+
preferredIdentityUrl = isResult["base_url"] ?? undefined;
220219
} else if (isResult && isResult.state !== AutoDiscovery.PROMPT) {
221220
logger.error("Error determining preferred identity server URL:", isResult);
222221
if (isResult.state === AutoDiscovery.FAIL_ERROR) {
@@ -244,6 +243,12 @@ export default class AutoDiscoveryUtils {
244243
}
245244

246245
const preferredHomeserverUrl = hsResult["base_url"];
246+
247+
if (!preferredHomeserverUrl) {
248+
logger.error("No homeserver URL configured");
249+
throw new UserFriendlyError("Unexpected error resolving homeserver configuration");
250+
}
251+
247252
let preferredHomeserverName = serverName ? serverName : hsResult["server_name"];
248253

249254
const url = new URL(preferredHomeserverUrl);
@@ -255,14 +260,14 @@ export default class AutoDiscoveryUtils {
255260
throw new UserFriendlyError("Unexpected error resolving homeserver configuration");
256261
}
257262

258-
return makeType(ValidatedServerConfig, {
263+
return {
259264
hsUrl: preferredHomeserverUrl,
260265
hsName: preferredHomeserverName,
261266
hsNameIsDifferent: url.hostname !== preferredHomeserverName,
262267
isUrl: preferredIdentityUrl,
263268
isDefault: false,
264269
warning: hsResult.error,
265270
isNameResolvable: !isSynthetic,
266-
});
271+
} as ValidatedServerConfig;
267272
}
268273
}

src/utils/DMRoomMap.ts

+11-12
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,6 @@ export default class DMRoomMap {
103103
}
104104

105105
private onAccountData = (ev: MatrixEvent): void => {
106-
console.log("onAccountData");
107-
108106
if (ev.getType() == EventType.Direct) {
109107
this.setMDirectFromContent(ev.getContent());
110108
this.userToRooms = null;
@@ -207,13 +205,16 @@ export default class DMRoomMap {
207205

208206
public getUniqueRoomsWithIndividuals(): { [userId: string]: Room } {
209207
if (!this.roomToUser) return {}; // No rooms means no map.
210-
return Object.keys(this.roomToUser)
211-
.map((r) => ({ userId: this.getUserIdForRoomId(r), room: this.matrixClient.getRoom(r) }))
212-
.filter((r) => r.userId && r.room?.getInvitedAndJoinedMemberCount() === 2)
213-
.reduce((obj, r) => {
214-
obj[r.userId] = r.room;
215-
return obj;
216-
}, {} as Record<string, Room>);
208+
// map roomToUser to valid rooms with two participants
209+
return Object.keys(this.roomToUser).reduce((acc, roomId: string) => {
210+
const userId = this.getUserIdForRoomId(roomId);
211+
const room = this.matrixClient.getRoom(roomId);
212+
const hasTwoMembers = room?.getInvitedAndJoinedMemberCount() === 2;
213+
if (userId && room && hasTwoMembers) {
214+
acc[userId] = room;
215+
}
216+
return acc;
217+
}, {} as Record<string, Room>);
217218
}
218219

219220
/**
@@ -236,9 +237,7 @@ export default class DMRoomMap {
236237
// to avoid multiple devices fighting to correct
237238
// the account data, only try to send the corrected
238239
// version once.
239-
logger.warn(
240-
`Invalid m.direct account data detected ` + `(self-chats that shouldn't be), patching it up.`,
241-
);
240+
logger.warn(`Invalid m.direct account data detected (self-chats that shouldn't be), patching it up.`);
242241
if (neededPatching && !this.hasSentOutPatchDirectAccountDataPatch) {
243242
this.hasSentOutPatchDirectAccountDataPatch = true;
244243
this.matrixClient.setAccountData(EventType.Direct, userToRooms);

src/utils/IdentityServerUtils.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ import SdkConfig from "../SdkConfig";
2121
import { MatrixClientPeg } from "../MatrixClientPeg";
2222
import { Policies } from "../Terms";
2323

24-
export function getDefaultIdentityServerUrl(): string {
25-
return SdkConfig.get("validated_server_config").isUrl;
24+
export function getDefaultIdentityServerUrl(): string | undefined {
25+
return SdkConfig.get("validated_server_config")?.isUrl;
2626
}
2727

2828
export function setToDefaultIdentityServer(): void {

src/utils/MediaEventHelper.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,10 @@ export class MediaEventHelper implements IDestroyable {
102102
}
103103
}
104104

105-
return fetch(this.media.thumbnailHttp).then((r) => r.blob());
105+
const thumbnailHttp = this.media.thumbnailHttp;
106+
if (!thumbnailHttp) return Promise.resolve(null);
107+
108+
return fetch(thumbnailHttp).then((r) => r.blob());
106109
};
107110

108111
public static isEligible(event: MatrixEvent): boolean {

src/utils/TypeUtils.ts

-30
This file was deleted.

src/utils/UserInteractiveAuth.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ type FunctionWithUIA<R, A> = (auth?: IAuthData, ...args: A[]) => Promise<UIAResp
2424

2525
export function wrapRequestWithDialog<R, A = any>(
2626
requestFunction: FunctionWithUIA<R, A>,
27-
opts: Omit<InteractiveAuthDialogProps, "makeRequest" | "onFinished">,
27+
opts: Omit<InteractiveAuthDialogProps<R>, "makeRequest" | "onFinished">,
2828
): (...args: A[]) => Promise<R> {
2929
return async function (...args): Promise<R> {
3030
return new Promise((resolve, reject) => {

src/utils/WidgetUtils.ts

+1
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ export default class WidgetUtils {
119119
if (
120120
testUrl.protocol === scalarUrl.protocol &&
121121
testUrl.host === scalarUrl.host &&
122+
scalarUrl.pathname &&
122123
testUrl.pathname?.startsWith(scalarUrl.pathname)
123124
) {
124125
return true;

src/utils/leave-behaviour.ts

+6
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@ export async function leaveRoomBehaviour(roomId: string, retry = true, spinner =
6161
}
6262

6363
const room = cli.getRoom(roomId);
64+
65+
// should not encounter this
66+
if (!room) {
67+
throw new Error(`Expected to find room for id ${roomId}`);
68+
}
69+
6470
// await any queued messages being sent so that they do not fail
6571
await Promise.all(
6672
room

src/utils/location/map.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,14 @@ export const createMapSiteLinkFromEvent = (event: MatrixEvent): string | null =>
8585
if (mLocation !== undefined) {
8686
const uri = mLocation["uri"];
8787
if (uri !== undefined) {
88-
return makeMapSiteLink(parseGeoUri(uri));
88+
const geoCoords = parseGeoUri(uri);
89+
return geoCoords ? makeMapSiteLink(geoCoords) : null;
8990
}
9091
} else {
9192
const geoUri = content["geo_uri"];
9293
if (geoUri) {
93-
return makeMapSiteLink(parseGeoUri(geoUri));
94+
const geoCoords = parseGeoUri(geoUri);
95+
return geoCoords ? makeMapSiteLink(geoCoords) : null;
9496
}
9597
}
9698
return null;

src/utils/location/parseGeoUri.ts

+11-4
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,23 @@ export const parseGeoUri = (uri: string): GeolocationCoordinates | undefined =>
2828
if (!m) return;
2929
const parts = m[1].split(";");
3030
const coords = parts[0].split(",");
31-
let uncertainty: number | null;
31+
let uncertainty: number | null | undefined = undefined;
3232
for (const param of parts.slice(1)) {
3333
const m = param.match(/u=(.*)/);
3434
if (m) uncertainty = parse(m[1]);
3535
}
36+
const latitude = parse(coords[0]);
37+
const longitude = parse(coords[1]);
38+
39+
if (latitude === null || longitude === null) {
40+
return;
41+
}
42+
3643
return {
37-
latitude: parse(coords[0]),
38-
longitude: parse(coords[1]),
44+
latitude: latitude!,
45+
longitude: longitude!,
3946
altitude: parse(coords[2]),
40-
accuracy: uncertainty,
47+
accuracy: uncertainty!,
4148
altitudeAccuracy: null,
4249
heading: null,
4350
speed: null,

src/utils/stringOrderField.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ export const reorderLexicographically = (
120120
// verify the right move would be sufficient
121121
if (
122122
rightBoundIdx === newOrder.length - 1 &&
123-
(newOrder[rightBoundIdx] ? stringToBase(newOrder[rightBoundIdx].order) : BigInt(Number.MAX_VALUE)) -
123+
(newOrder[rightBoundIdx]?.order ? stringToBase(newOrder[rightBoundIdx].order!) : BigInt(Number.MAX_VALUE)) -
124124
prevBase <=
125125
rightBoundIdx - toIndex
126126
) {

src/utils/threepids.ts

+3
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ export const lookupThreePids = async (
6565
if (threePids.length === 0) return [];
6666

6767
const token = await client.identityServer.getAccessToken();
68+
69+
if (!token) return [];
70+
6871
const lookedUp = await client.bulkLookupThreePids(
6972
threePids.map((t) => [t.isEmail ? "email" : "msisdn", t.userId]),
7073
token,

test/test-utils/test-utils.ts

+3-4
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ import { MapperOpts } from "matrix-js-sdk/src/event-mapper";
4747

4848
import type { GroupCall } from "matrix-js-sdk/src/webrtc/groupCall";
4949
import { MatrixClientPeg as peg } from "../../src/MatrixClientPeg";
50-
import { makeType } from "../../src/utils/TypeUtils";
5150
import { ValidatedServerConfig } from "../../src/utils/ValidatedServerConfig";
5251
import { EnhancedMap } from "../../src/utils/maps";
5352
import { AsyncStoreWithClient } from "../../src/stores/AsyncStoreWithClient";
@@ -591,13 +590,13 @@ export function mkStubRoom(
591590
} as unknown as Room;
592591
}
593592

594-
export function mkServerConfig(hsUrl: string, isUrl: string) {
595-
return makeType(ValidatedServerConfig, {
593+
export function mkServerConfig(hsUrl: string, isUrl: string): ValidatedServerConfig {
594+
return {
596595
hsUrl,
597596
hsName: "TEST_ENVIRONMENT",
598597
hsNameIsDifferent: false, // yes, we lie
599598
isUrl,
600-
});
599+
} as ValidatedServerConfig;
601600
}
602601

603602
// These methods make some use of some private methods on the AsyncStoreWithClient to simplify getting into a consistent

0 commit comments

Comments
 (0)