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
This commit is contained in:
parent
4ed6e39067
commit
81a4498a8f
18 changed files with 143 additions and 81 deletions
|
@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import React, { createRef } from "react";
|
||||||
import {
|
import {
|
||||||
AuthType,
|
AuthType,
|
||||||
IAuthData,
|
IAuthData,
|
||||||
|
@ -23,8 +24,8 @@ import {
|
||||||
IStageStatus,
|
IStageStatus,
|
||||||
} from "matrix-js-sdk/src/interactive-auth";
|
} from "matrix-js-sdk/src/interactive-auth";
|
||||||
import { MatrixClient } from "matrix-js-sdk/src/client";
|
import { MatrixClient } from "matrix-js-sdk/src/client";
|
||||||
import React, { createRef } from "react";
|
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
|
import { UIAResponse } from "matrix-js-sdk/src/@types/uia";
|
||||||
|
|
||||||
import getEntryComponentForLoginType, { IStageComponent } from "../views/auth/InteractiveAuthEntryComponents";
|
import getEntryComponentForLoginType, { IStageComponent } from "../views/auth/InteractiveAuthEntryComponents";
|
||||||
import Spinner from "../views/elements/Spinner";
|
import Spinner from "../views/elements/Spinner";
|
||||||
|
@ -39,7 +40,7 @@ type InteractiveAuthCallbackSuccess = (
|
||||||
type InteractiveAuthCallbackFailure = (success: false, response: IAuthData | Error) => void;
|
type InteractiveAuthCallbackFailure = (success: false, response: IAuthData | Error) => void;
|
||||||
export type InteractiveAuthCallback = InteractiveAuthCallbackSuccess & InteractiveAuthCallbackFailure;
|
export type InteractiveAuthCallback = InteractiveAuthCallbackSuccess & InteractiveAuthCallbackFailure;
|
||||||
|
|
||||||
interface IProps {
|
export interface InteractiveAuthProps<T> {
|
||||||
// matrix client to use for UI auth requests
|
// matrix client to use for UI auth requests
|
||||||
matrixClient: MatrixClient;
|
matrixClient: MatrixClient;
|
||||||
// response from initial request. If not supplied, will do a request on mount.
|
// response from initial request. If not supplied, will do a request on mount.
|
||||||
|
@ -61,7 +62,7 @@ interface IProps {
|
||||||
continueText?: string;
|
continueText?: string;
|
||||||
continueKind?: string;
|
continueKind?: string;
|
||||||
// callback
|
// callback
|
||||||
makeRequest(auth: IAuthData | null): Promise<IAuthData>;
|
makeRequest(auth?: IAuthData): Promise<UIAResponse<T>>;
|
||||||
// callback called when the auth process has finished,
|
// callback called when the auth process has finished,
|
||||||
// successfully or unsuccessfully.
|
// successfully or unsuccessfully.
|
||||||
// @param {boolean} status True if the operation requiring
|
// @param {boolean} status True if the operation requiring
|
||||||
|
@ -92,14 +93,14 @@ interface IState {
|
||||||
submitButtonEnabled: boolean;
|
submitButtonEnabled: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class InteractiveAuthComponent extends React.Component<IProps, IState> {
|
export default class InteractiveAuthComponent<T> extends React.Component<InteractiveAuthProps<T>, IState> {
|
||||||
private readonly authLogic: InteractiveAuth;
|
private readonly authLogic: InteractiveAuth;
|
||||||
private readonly intervalId: number | null = null;
|
private readonly intervalId: number | null = null;
|
||||||
private readonly stageComponent = createRef<IStageComponent>();
|
private readonly stageComponent = createRef<IStageComponent>();
|
||||||
|
|
||||||
private unmounted = false;
|
private unmounted = false;
|
||||||
|
|
||||||
public constructor(props: IProps) {
|
public constructor(props: InteractiveAuthProps<T>) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
|
|
|
@ -22,7 +22,11 @@ import { AuthType, IAuthData } from "matrix-js-sdk/src/interactive-auth";
|
||||||
|
|
||||||
import { _t } from "../../../languageHandler";
|
import { _t } from "../../../languageHandler";
|
||||||
import AccessibleButton from "../elements/AccessibleButton";
|
import AccessibleButton from "../elements/AccessibleButton";
|
||||||
import InteractiveAuth, { ERROR_USER_CANCELLED, InteractiveAuthCallback } from "../../structures/InteractiveAuth";
|
import InteractiveAuth, {
|
||||||
|
ERROR_USER_CANCELLED,
|
||||||
|
InteractiveAuthCallback,
|
||||||
|
InteractiveAuthProps,
|
||||||
|
} from "../../structures/InteractiveAuth";
|
||||||
import { SSOAuthEntry } from "../auth/InteractiveAuthEntryComponents";
|
import { SSOAuthEntry } from "../auth/InteractiveAuthEntryComponents";
|
||||||
import BaseDialog from "./BaseDialog";
|
import BaseDialog from "./BaseDialog";
|
||||||
|
|
||||||
|
@ -37,17 +41,11 @@ type DialogAesthetics = Partial<{
|
||||||
};
|
};
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
export interface InteractiveAuthDialogProps {
|
export interface InteractiveAuthDialogProps<T = unknown>
|
||||||
|
extends Pick<InteractiveAuthProps<T>, "makeRequest" | "authData"> {
|
||||||
// matrix client to use for UI auth requests
|
// matrix client to use for UI auth requests
|
||||||
matrixClient: MatrixClient;
|
matrixClient: MatrixClient;
|
||||||
|
|
||||||
// response from initial request. If not supplied, will do a request on
|
|
||||||
// mount.
|
|
||||||
authData?: IAuthData;
|
|
||||||
|
|
||||||
// callback
|
|
||||||
makeRequest: (auth: IAuthData) => Promise<IAuthData>;
|
|
||||||
|
|
||||||
// Optional title and body to show when not showing a particular stage
|
// Optional title and body to show when not showing a particular stage
|
||||||
title?: string;
|
title?: string;
|
||||||
body?: string;
|
body?: string;
|
||||||
|
@ -83,8 +81,8 @@ interface IState {
|
||||||
uiaStagePhase: number | null;
|
uiaStagePhase: number | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class InteractiveAuthDialog extends React.Component<InteractiveAuthDialogProps, IState> {
|
export default class InteractiveAuthDialog<T> extends React.Component<InteractiveAuthDialogProps<T>, IState> {
|
||||||
public constructor(props: InteractiveAuthDialogProps) {
|
public constructor(props: InteractiveAuthDialogProps<T>) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
|
|
|
@ -20,7 +20,6 @@ import { logger } from "matrix-js-sdk/src/logger";
|
||||||
import { IClientWellKnown } from "matrix-js-sdk/src/matrix";
|
import { IClientWellKnown } from "matrix-js-sdk/src/matrix";
|
||||||
|
|
||||||
import { _t, UserFriendlyError } from "../languageHandler";
|
import { _t, UserFriendlyError } from "../languageHandler";
|
||||||
import { makeType } from "./TypeUtils";
|
|
||||||
import SdkConfig from "../SdkConfig";
|
import SdkConfig from "../SdkConfig";
|
||||||
import { ValidatedServerConfig } from "./ValidatedServerConfig";
|
import { ValidatedServerConfig } from "./ValidatedServerConfig";
|
||||||
|
|
||||||
|
@ -43,7 +42,7 @@ export default class AutoDiscoveryUtils {
|
||||||
* @param {string | Error} error The error to check
|
* @param {string | Error} error The error to check
|
||||||
* @returns {boolean} True if the error is a liveliness error.
|
* @returns {boolean} True if the error is a liveliness error.
|
||||||
*/
|
*/
|
||||||
public static isLivelinessError(error: string | Error): boolean {
|
public static isLivelinessError(error?: string | Error | null): boolean {
|
||||||
if (!error) return false;
|
if (!error) return false;
|
||||||
return !!LIVELINESS_DISCOVERY_ERRORS.find((e) =>
|
return !!LIVELINESS_DISCOVERY_ERRORS.find((e) =>
|
||||||
typeof error === "string" ? e === error : e === error.message,
|
typeof error === "string" ? e === error : e === error.message,
|
||||||
|
@ -197,7 +196,7 @@ export default class AutoDiscoveryUtils {
|
||||||
): ValidatedServerConfig {
|
): ValidatedServerConfig {
|
||||||
if (!discoveryResult || !discoveryResult["m.homeserver"]) {
|
if (!discoveryResult || !discoveryResult["m.homeserver"]) {
|
||||||
// This shouldn't happen without major misconfiguration, so we'll log a bit of information
|
// This shouldn't happen without major misconfiguration, so we'll log a bit of information
|
||||||
// in the log so we can find this bit of codee but otherwise tell teh user "it broke".
|
// in the log so we can find this bit of code but otherwise tell the user "it broke".
|
||||||
logger.error("Ended up in a state of not knowing which homeserver to connect to.");
|
logger.error("Ended up in a state of not knowing which homeserver to connect to.");
|
||||||
throw new UserFriendlyError("Unexpected error resolving homeserver configuration");
|
throw new UserFriendlyError("Unexpected error resolving homeserver configuration");
|
||||||
}
|
}
|
||||||
|
@ -216,7 +215,7 @@ export default class AutoDiscoveryUtils {
|
||||||
// of Element.
|
// of Element.
|
||||||
let preferredIdentityUrl = defaultConfig && defaultConfig["isUrl"];
|
let preferredIdentityUrl = defaultConfig && defaultConfig["isUrl"];
|
||||||
if (isResult && isResult.state === AutoDiscovery.SUCCESS) {
|
if (isResult && isResult.state === AutoDiscovery.SUCCESS) {
|
||||||
preferredIdentityUrl = isResult["base_url"];
|
preferredIdentityUrl = isResult["base_url"] ?? undefined;
|
||||||
} else if (isResult && isResult.state !== AutoDiscovery.PROMPT) {
|
} else if (isResult && isResult.state !== AutoDiscovery.PROMPT) {
|
||||||
logger.error("Error determining preferred identity server URL:", isResult);
|
logger.error("Error determining preferred identity server URL:", isResult);
|
||||||
if (isResult.state === AutoDiscovery.FAIL_ERROR) {
|
if (isResult.state === AutoDiscovery.FAIL_ERROR) {
|
||||||
|
@ -244,6 +243,12 @@ export default class AutoDiscoveryUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
const preferredHomeserverUrl = hsResult["base_url"];
|
const preferredHomeserverUrl = hsResult["base_url"];
|
||||||
|
|
||||||
|
if (!preferredHomeserverUrl) {
|
||||||
|
logger.error("No homeserver URL configured");
|
||||||
|
throw new UserFriendlyError("Unexpected error resolving homeserver configuration");
|
||||||
|
}
|
||||||
|
|
||||||
let preferredHomeserverName = serverName ? serverName : hsResult["server_name"];
|
let preferredHomeserverName = serverName ? serverName : hsResult["server_name"];
|
||||||
|
|
||||||
const url = new URL(preferredHomeserverUrl);
|
const url = new URL(preferredHomeserverUrl);
|
||||||
|
@ -255,7 +260,7 @@ export default class AutoDiscoveryUtils {
|
||||||
throw new UserFriendlyError("Unexpected error resolving homeserver configuration");
|
throw new UserFriendlyError("Unexpected error resolving homeserver configuration");
|
||||||
}
|
}
|
||||||
|
|
||||||
return makeType(ValidatedServerConfig, {
|
return {
|
||||||
hsUrl: preferredHomeserverUrl,
|
hsUrl: preferredHomeserverUrl,
|
||||||
hsName: preferredHomeserverName,
|
hsName: preferredHomeserverName,
|
||||||
hsNameIsDifferent: url.hostname !== preferredHomeserverName,
|
hsNameIsDifferent: url.hostname !== preferredHomeserverName,
|
||||||
|
@ -263,6 +268,6 @@ export default class AutoDiscoveryUtils {
|
||||||
isDefault: false,
|
isDefault: false,
|
||||||
warning: hsResult.error,
|
warning: hsResult.error,
|
||||||
isNameResolvable: !isSynthetic,
|
isNameResolvable: !isSynthetic,
|
||||||
});
|
} as ValidatedServerConfig;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,8 +103,6 @@ export default class DMRoomMap {
|
||||||
}
|
}
|
||||||
|
|
||||||
private onAccountData = (ev: MatrixEvent): void => {
|
private onAccountData = (ev: MatrixEvent): void => {
|
||||||
console.log("onAccountData");
|
|
||||||
|
|
||||||
if (ev.getType() == EventType.Direct) {
|
if (ev.getType() == EventType.Direct) {
|
||||||
this.setMDirectFromContent(ev.getContent());
|
this.setMDirectFromContent(ev.getContent());
|
||||||
this.userToRooms = null;
|
this.userToRooms = null;
|
||||||
|
@ -207,12 +205,15 @@ export default class DMRoomMap {
|
||||||
|
|
||||||
public getUniqueRoomsWithIndividuals(): { [userId: string]: Room } {
|
public getUniqueRoomsWithIndividuals(): { [userId: string]: Room } {
|
||||||
if (!this.roomToUser) return {}; // No rooms means no map.
|
if (!this.roomToUser) return {}; // No rooms means no map.
|
||||||
return Object.keys(this.roomToUser)
|
// map roomToUser to valid rooms with two participants
|
||||||
.map((r) => ({ userId: this.getUserIdForRoomId(r), room: this.matrixClient.getRoom(r) }))
|
return Object.keys(this.roomToUser).reduce((acc, roomId: string) => {
|
||||||
.filter((r) => r.userId && r.room?.getInvitedAndJoinedMemberCount() === 2)
|
const userId = this.getUserIdForRoomId(roomId);
|
||||||
.reduce((obj, r) => {
|
const room = this.matrixClient.getRoom(roomId);
|
||||||
obj[r.userId] = r.room;
|
const hasTwoMembers = room?.getInvitedAndJoinedMemberCount() === 2;
|
||||||
return obj;
|
if (userId && room && hasTwoMembers) {
|
||||||
|
acc[userId] = room;
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
}, {} as Record<string, Room>);
|
}, {} as Record<string, Room>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,9 +237,7 @@ export default class DMRoomMap {
|
||||||
// to avoid multiple devices fighting to correct
|
// to avoid multiple devices fighting to correct
|
||||||
// the account data, only try to send the corrected
|
// the account data, only try to send the corrected
|
||||||
// version once.
|
// version once.
|
||||||
logger.warn(
|
logger.warn(`Invalid m.direct account data detected (self-chats that shouldn't be), patching it up.`);
|
||||||
`Invalid m.direct account data detected ` + `(self-chats that shouldn't be), patching it up.`,
|
|
||||||
);
|
|
||||||
if (neededPatching && !this.hasSentOutPatchDirectAccountDataPatch) {
|
if (neededPatching && !this.hasSentOutPatchDirectAccountDataPatch) {
|
||||||
this.hasSentOutPatchDirectAccountDataPatch = true;
|
this.hasSentOutPatchDirectAccountDataPatch = true;
|
||||||
this.matrixClient.setAccountData(EventType.Direct, userToRooms);
|
this.matrixClient.setAccountData(EventType.Direct, userToRooms);
|
||||||
|
|
|
@ -21,8 +21,8 @@ import SdkConfig from "../SdkConfig";
|
||||||
import { MatrixClientPeg } from "../MatrixClientPeg";
|
import { MatrixClientPeg } from "../MatrixClientPeg";
|
||||||
import { Policies } from "../Terms";
|
import { Policies } from "../Terms";
|
||||||
|
|
||||||
export function getDefaultIdentityServerUrl(): string {
|
export function getDefaultIdentityServerUrl(): string | undefined {
|
||||||
return SdkConfig.get("validated_server_config").isUrl;
|
return SdkConfig.get("validated_server_config")?.isUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setToDefaultIdentityServer(): void {
|
export function setToDefaultIdentityServer(): void {
|
||||||
|
|
|
@ -102,7 +102,10 @@ export class MediaEventHelper implements IDestroyable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return fetch(this.media.thumbnailHttp).then((r) => r.blob());
|
const thumbnailHttp = this.media.thumbnailHttp;
|
||||||
|
if (!thumbnailHttp) return Promise.resolve(null);
|
||||||
|
|
||||||
|
return fetch(thumbnailHttp).then((r) => r.blob());
|
||||||
};
|
};
|
||||||
|
|
||||||
public static isEligible(event: MatrixEvent): boolean {
|
public static isEligible(event: MatrixEvent): boolean {
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2019 New Vector Ltd
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a class of a given type using the objects defined. This
|
|
||||||
* is a stopgap function while we don't have TypeScript interfaces.
|
|
||||||
* In future, we'd define the `type` as an interface and just cast
|
|
||||||
* it instead of cheating like we are here.
|
|
||||||
* @param {Type} Type The type of class to construct.
|
|
||||||
* @param {*} opts The options (properties) to set on the object.
|
|
||||||
* @returns {*} The created object.
|
|
||||||
*/
|
|
||||||
export function makeType<T>(Type: { new (): T }, opts: Partial<T>): T {
|
|
||||||
const c = new Type();
|
|
||||||
Object.assign(c, opts);
|
|
||||||
return c;
|
|
||||||
}
|
|
|
@ -24,7 +24,7 @@ type FunctionWithUIA<R, A> = (auth?: IAuthData, ...args: A[]) => Promise<UIAResp
|
||||||
|
|
||||||
export function wrapRequestWithDialog<R, A = any>(
|
export function wrapRequestWithDialog<R, A = any>(
|
||||||
requestFunction: FunctionWithUIA<R, A>,
|
requestFunction: FunctionWithUIA<R, A>,
|
||||||
opts: Omit<InteractiveAuthDialogProps, "makeRequest" | "onFinished">,
|
opts: Omit<InteractiveAuthDialogProps<R>, "makeRequest" | "onFinished">,
|
||||||
): (...args: A[]) => Promise<R> {
|
): (...args: A[]) => Promise<R> {
|
||||||
return async function (...args): Promise<R> {
|
return async function (...args): Promise<R> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|
|
@ -119,6 +119,7 @@ export default class WidgetUtils {
|
||||||
if (
|
if (
|
||||||
testUrl.protocol === scalarUrl.protocol &&
|
testUrl.protocol === scalarUrl.protocol &&
|
||||||
testUrl.host === scalarUrl.host &&
|
testUrl.host === scalarUrl.host &&
|
||||||
|
scalarUrl.pathname &&
|
||||||
testUrl.pathname?.startsWith(scalarUrl.pathname)
|
testUrl.pathname?.startsWith(scalarUrl.pathname)
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -61,6 +61,12 @@ export async function leaveRoomBehaviour(roomId: string, retry = true, spinner =
|
||||||
}
|
}
|
||||||
|
|
||||||
const room = cli.getRoom(roomId);
|
const room = cli.getRoom(roomId);
|
||||||
|
|
||||||
|
// should not encounter this
|
||||||
|
if (!room) {
|
||||||
|
throw new Error(`Expected to find room for id ${roomId}`);
|
||||||
|
}
|
||||||
|
|
||||||
// await any queued messages being sent so that they do not fail
|
// await any queued messages being sent so that they do not fail
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
room
|
room
|
||||||
|
|
|
@ -85,12 +85,14 @@ export const createMapSiteLinkFromEvent = (event: MatrixEvent): string | null =>
|
||||||
if (mLocation !== undefined) {
|
if (mLocation !== undefined) {
|
||||||
const uri = mLocation["uri"];
|
const uri = mLocation["uri"];
|
||||||
if (uri !== undefined) {
|
if (uri !== undefined) {
|
||||||
return makeMapSiteLink(parseGeoUri(uri));
|
const geoCoords = parseGeoUri(uri);
|
||||||
|
return geoCoords ? makeMapSiteLink(geoCoords) : null;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const geoUri = content["geo_uri"];
|
const geoUri = content["geo_uri"];
|
||||||
if (geoUri) {
|
if (geoUri) {
|
||||||
return makeMapSiteLink(parseGeoUri(geoUri));
|
const geoCoords = parseGeoUri(geoUri);
|
||||||
|
return geoCoords ? makeMapSiteLink(geoCoords) : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -28,16 +28,23 @@ export const parseGeoUri = (uri: string): GeolocationCoordinates | undefined =>
|
||||||
if (!m) return;
|
if (!m) return;
|
||||||
const parts = m[1].split(";");
|
const parts = m[1].split(";");
|
||||||
const coords = parts[0].split(",");
|
const coords = parts[0].split(",");
|
||||||
let uncertainty: number | null;
|
let uncertainty: number | null | undefined = undefined;
|
||||||
for (const param of parts.slice(1)) {
|
for (const param of parts.slice(1)) {
|
||||||
const m = param.match(/u=(.*)/);
|
const m = param.match(/u=(.*)/);
|
||||||
if (m) uncertainty = parse(m[1]);
|
if (m) uncertainty = parse(m[1]);
|
||||||
}
|
}
|
||||||
|
const latitude = parse(coords[0]);
|
||||||
|
const longitude = parse(coords[1]);
|
||||||
|
|
||||||
|
if (latitude === null || longitude === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
latitude: parse(coords[0]),
|
latitude: latitude!,
|
||||||
longitude: parse(coords[1]),
|
longitude: longitude!,
|
||||||
altitude: parse(coords[2]),
|
altitude: parse(coords[2]),
|
||||||
accuracy: uncertainty,
|
accuracy: uncertainty!,
|
||||||
altitudeAccuracy: null,
|
altitudeAccuracy: null,
|
||||||
heading: null,
|
heading: null,
|
||||||
speed: null,
|
speed: null,
|
||||||
|
|
|
@ -120,7 +120,7 @@ export const reorderLexicographically = (
|
||||||
// verify the right move would be sufficient
|
// verify the right move would be sufficient
|
||||||
if (
|
if (
|
||||||
rightBoundIdx === newOrder.length - 1 &&
|
rightBoundIdx === newOrder.length - 1 &&
|
||||||
(newOrder[rightBoundIdx] ? stringToBase(newOrder[rightBoundIdx].order) : BigInt(Number.MAX_VALUE)) -
|
(newOrder[rightBoundIdx]?.order ? stringToBase(newOrder[rightBoundIdx].order!) : BigInt(Number.MAX_VALUE)) -
|
||||||
prevBase <=
|
prevBase <=
|
||||||
rightBoundIdx - toIndex
|
rightBoundIdx - toIndex
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -65,6 +65,9 @@ export const lookupThreePids = async (
|
||||||
if (threePids.length === 0) return [];
|
if (threePids.length === 0) return [];
|
||||||
|
|
||||||
const token = await client.identityServer.getAccessToken();
|
const token = await client.identityServer.getAccessToken();
|
||||||
|
|
||||||
|
if (!token) return [];
|
||||||
|
|
||||||
const lookedUp = await client.bulkLookupThreePids(
|
const lookedUp = await client.bulkLookupThreePids(
|
||||||
threePids.map((t) => [t.isEmail ? "email" : "msisdn", t.userId]),
|
threePids.map((t) => [t.isEmail ? "email" : "msisdn", t.userId]),
|
||||||
token,
|
token,
|
||||||
|
|
|
@ -47,7 +47,6 @@ import { MapperOpts } from "matrix-js-sdk/src/event-mapper";
|
||||||
|
|
||||||
import type { GroupCall } from "matrix-js-sdk/src/webrtc/groupCall";
|
import type { GroupCall } from "matrix-js-sdk/src/webrtc/groupCall";
|
||||||
import { MatrixClientPeg as peg } from "../../src/MatrixClientPeg";
|
import { MatrixClientPeg as peg } from "../../src/MatrixClientPeg";
|
||||||
import { makeType } from "../../src/utils/TypeUtils";
|
|
||||||
import { ValidatedServerConfig } from "../../src/utils/ValidatedServerConfig";
|
import { ValidatedServerConfig } from "../../src/utils/ValidatedServerConfig";
|
||||||
import { EnhancedMap } from "../../src/utils/maps";
|
import { EnhancedMap } from "../../src/utils/maps";
|
||||||
import { AsyncStoreWithClient } from "../../src/stores/AsyncStoreWithClient";
|
import { AsyncStoreWithClient } from "../../src/stores/AsyncStoreWithClient";
|
||||||
|
@ -591,13 +590,13 @@ export function mkStubRoom(
|
||||||
} as unknown as Room;
|
} as unknown as Room;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mkServerConfig(hsUrl: string, isUrl: string) {
|
export function mkServerConfig(hsUrl: string, isUrl: string): ValidatedServerConfig {
|
||||||
return makeType(ValidatedServerConfig, {
|
return {
|
||||||
hsUrl,
|
hsUrl,
|
||||||
hsName: "TEST_ENVIRONMENT",
|
hsName: "TEST_ENVIRONMENT",
|
||||||
hsNameIsDifferent: false, // yes, we lie
|
hsNameIsDifferent: false, // yes, we lie
|
||||||
isUrl,
|
isUrl,
|
||||||
});
|
} as ValidatedServerConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
// These methods make some use of some private methods on the AsyncStoreWithClient to simplify getting into a consistent
|
// These methods make some use of some private methods on the AsyncStoreWithClient to simplify getting into a consistent
|
||||||
|
|
|
@ -16,7 +16,7 @@ limitations under the License.
|
||||||
|
|
||||||
import { mocked, Mocked } from "jest-mock";
|
import { mocked, Mocked } from "jest-mock";
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
import { ClientEvent, EventType, IContent, MatrixClient, MatrixEvent } from "matrix-js-sdk/src/matrix";
|
import { ClientEvent, EventType, IContent, MatrixClient, MatrixEvent, Room } from "matrix-js-sdk/src/matrix";
|
||||||
|
|
||||||
import DMRoomMap from "../../src/utils/DMRoomMap";
|
import DMRoomMap from "../../src/utils/DMRoomMap";
|
||||||
import { mkEvent, stubClient } from "../test-utils";
|
import { mkEvent, stubClient } from "../test-utils";
|
||||||
|
@ -137,4 +137,56 @@ describe("DMRoomMap", () => {
|
||||||
expect(dmRoomMap.getRoomIds()).toEqual(new Set([roomId1, roomId2, roomId4]));
|
expect(dmRoomMap.getRoomIds()).toEqual(new Set([roomId1, roomId2, roomId4]));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("getUniqueRoomsWithIndividuals()", () => {
|
||||||
|
const bigRoom = {
|
||||||
|
roomId: "!bigRoom:server.org",
|
||||||
|
getInvitedAndJoinedMemberCount: jest.fn().mockReturnValue(5000),
|
||||||
|
} as unknown as Room;
|
||||||
|
const dmWithBob = {
|
||||||
|
roomId: "!dmWithBob:server.org",
|
||||||
|
getInvitedAndJoinedMemberCount: jest.fn().mockReturnValue(2),
|
||||||
|
} as unknown as Room;
|
||||||
|
const dmWithCharlie = {
|
||||||
|
roomId: "!dmWithCharlie:server.org",
|
||||||
|
getInvitedAndJoinedMemberCount: jest.fn().mockReturnValue(2),
|
||||||
|
} as unknown as Room;
|
||||||
|
const smallRoom = {
|
||||||
|
roomId: "!smallRoom:server.org",
|
||||||
|
getInvitedAndJoinedMemberCount: jest.fn().mockReturnValue(3),
|
||||||
|
} as unknown as Room;
|
||||||
|
|
||||||
|
const mDirectContent = {
|
||||||
|
"@bob:server.org": [bigRoom.roomId, dmWithBob.roomId, smallRoom.roomId],
|
||||||
|
"@charlie:server.org": [dmWithCharlie.roomId, smallRoom.roomId],
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
client.getAccountData.mockReturnValue(mkMDirectEvent(mDirectContent));
|
||||||
|
client.getRoom.mockImplementation((roomId: string) =>
|
||||||
|
[bigRoom, smallRoom, dmWithCharlie, dmWithBob].find((room) => room.roomId === roomId),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns an empty object when room map has not been populated", () => {
|
||||||
|
const instance = new DMRoomMap(client);
|
||||||
|
expect(instance.getUniqueRoomsWithIndividuals()).toEqual({});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns map of users to rooms with 2 members", () => {
|
||||||
|
const dmRoomMap = new DMRoomMap(client);
|
||||||
|
dmRoomMap.start();
|
||||||
|
expect(dmRoomMap.getUniqueRoomsWithIndividuals()).toEqual({
|
||||||
|
"@bob:server.org": dmWithBob,
|
||||||
|
"@charlie:server.org": dmWithCharlie,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("excludes rooms that are not found by matrixClient", () => {
|
||||||
|
client.getRoom.mockReset().mockReturnValue(undefined);
|
||||||
|
const dmRoomMap = new DMRoomMap(client);
|
||||||
|
dmRoomMap.start();
|
||||||
|
expect(dmRoomMap.getUniqueRoomsWithIndividuals()).toEqual({});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -31,15 +31,23 @@ describe("createMapSiteLinkFromEvent", () => {
|
||||||
).toBeNull();
|
).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("returns OpenStreetMap link if event contains m.location", () => {
|
it("returns OpenStreetMap link if event contains m.location with valid uri", () => {
|
||||||
expect(createMapSiteLinkFromEvent(makeLocationEvent("geo:51.5076,-0.1276"))).toEqual(
|
expect(createMapSiteLinkFromEvent(makeLocationEvent("geo:51.5076,-0.1276"))).toEqual(
|
||||||
"https://www.openstreetmap.org/" + "?mlat=51.5076&mlon=-0.1276" + "#map=16/51.5076/-0.1276",
|
"https://www.openstreetmap.org/" + "?mlat=51.5076&mlon=-0.1276" + "#map=16/51.5076/-0.1276",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("returns null if event contains m.location with invalid uri", () => {
|
||||||
|
expect(createMapSiteLinkFromEvent(makeLocationEvent("123 Sesame St"))).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
it("returns OpenStreetMap link if event contains geo_uri", () => {
|
it("returns OpenStreetMap link if event contains geo_uri", () => {
|
||||||
expect(createMapSiteLinkFromEvent(makeLegacyLocationEvent("geo:51.5076,-0.1276"))).toEqual(
|
expect(createMapSiteLinkFromEvent(makeLegacyLocationEvent("geo:51.5076,-0.1276"))).toEqual(
|
||||||
"https://www.openstreetmap.org/" + "?mlat=51.5076&mlon=-0.1276" + "#map=16/51.5076/-0.1276",
|
"https://www.openstreetmap.org/" + "?mlat=51.5076&mlon=-0.1276" + "#map=16/51.5076/-0.1276",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("returns null if event contains an invalid geo_uri", () => {
|
||||||
|
expect(createMapSiteLinkFromEvent(makeLegacyLocationEvent("123 Sesame St"))).toBeNull();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -21,6 +21,14 @@ describe("parseGeoUri", () => {
|
||||||
expect(parseGeoUri("")).toBeFalsy();
|
expect(parseGeoUri("")).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("returns undefined if latitude is not a number", () => {
|
||||||
|
expect(parseGeoUri("geo:ABCD,16.3695,183")).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns undefined if longitude is not a number", () => {
|
||||||
|
expect(parseGeoUri("geo:48.2010,EFGH,183")).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
// We use some examples from the spec, but don't check semantics
|
// We use some examples from the spec, but don't check semantics
|
||||||
// like two textually-different URIs being equal, since we are
|
// like two textually-different URIs being equal, since we are
|
||||||
// just a humble parser.
|
// just a humble parser.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue