mirror of
https://github.com/vector-im/element-web.git
synced 2024-11-15 20:54:59 +08:00
Conform more code to strict null checking (#10153)
* Conform more code to strict null checking * Conform more code to strict null checking * Iterate * Iterate
This commit is contained in:
parent
a4ff959aa1
commit
145a5a8a8d
@ -193,7 +193,7 @@ export default abstract class BasePlatform {
|
||||
public displayNotification(
|
||||
title: string,
|
||||
msg: string,
|
||||
avatarUrl: string,
|
||||
avatarUrl: string | null,
|
||||
room: Room,
|
||||
ev?: MatrixEvent,
|
||||
): Notification {
|
||||
|
@ -33,7 +33,7 @@ export interface IModal<T extends any[]> {
|
||||
beforeClosePromise?: Promise<boolean>;
|
||||
closeReason?: string;
|
||||
onBeforeClose?(reason?: string): Promise<boolean>;
|
||||
onFinished(...args: T): void;
|
||||
onFinished?(...args: T): void;
|
||||
close(...args: T): void;
|
||||
hidden?: boolean;
|
||||
}
|
||||
@ -68,11 +68,11 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
|
||||
// The modal to prioritise over all others. If this is set, only show
|
||||
// this modal. Remove all other modals from the stack when this modal
|
||||
// is closed.
|
||||
private priorityModal: IModal<any> = null;
|
||||
private priorityModal: IModal<any> | null = null;
|
||||
// The modal to keep open underneath other modals if possible. Useful
|
||||
// for cases like Settings where the modal should remain open while the
|
||||
// user is prompted for more information/errors.
|
||||
private staticModal: IModal<any> = null;
|
||||
private staticModal: IModal<any> | null = null;
|
||||
// A list of the modals we have stacked up, with the most recent at [0]
|
||||
// Neither the static nor priority modal will be in this list.
|
||||
private modals: IModal<any>[] = [];
|
||||
@ -144,17 +144,14 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
|
||||
closeDialog: IHandle<T>["close"];
|
||||
onFinishedProm: IHandle<T>["finished"];
|
||||
} {
|
||||
const modal: IModal<T> = {
|
||||
onFinished: props ? props.onFinished : null,
|
||||
onBeforeClose: options.onBeforeClose,
|
||||
beforeClosePromise: null,
|
||||
closeReason: null,
|
||||
const modal = {
|
||||
onFinished: props?.onFinished,
|
||||
onBeforeClose: options?.onBeforeClose,
|
||||
className,
|
||||
|
||||
// these will be set below but we need an object reference to pass to getCloseFn before we can do that
|
||||
elem: null,
|
||||
close: null,
|
||||
};
|
||||
} as IModal<T>;
|
||||
|
||||
// never call this from onFinished() otherwise it will loop
|
||||
const [closeDialog, onFinishedProm] = this.getCloseFn<T>(modal, props);
|
||||
@ -173,7 +170,7 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
|
||||
|
||||
private getCloseFn<T extends any[]>(
|
||||
modal: IModal<T>,
|
||||
props: IProps<T>,
|
||||
props?: IProps<T>,
|
||||
): [IHandle<T>["close"], IHandle<T>["finished"]] {
|
||||
const deferred = defer<T>();
|
||||
return [
|
||||
@ -183,13 +180,13 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
|
||||
} else if (modal.onBeforeClose) {
|
||||
modal.beforeClosePromise = modal.onBeforeClose(modal.closeReason);
|
||||
const shouldClose = await modal.beforeClosePromise;
|
||||
modal.beforeClosePromise = null;
|
||||
modal.beforeClosePromise = undefined;
|
||||
if (!shouldClose) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
deferred.resolve(args);
|
||||
if (props && props.onFinished) props.onFinished.apply(null, args);
|
||||
if (props?.onFinished) props.onFinished.apply(null, args);
|
||||
const i = this.modals.indexOf(modal);
|
||||
if (i >= 0) {
|
||||
this.modals.splice(i, 1);
|
||||
@ -317,7 +314,7 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
|
||||
// so, pass the reason to close through a member variable
|
||||
modal.closeReason = "backgroundClick";
|
||||
modal.close();
|
||||
modal.closeReason = null;
|
||||
modal.closeReason = undefined;
|
||||
};
|
||||
|
||||
private getCurrentModal(): IModal<any> {
|
||||
|
@ -68,7 +68,7 @@ Override both the content body and the TextForEvent handler for specific msgtype
|
||||
This is useful when the content body contains fallback text that would explain that the client can't handle a particular
|
||||
type of tile.
|
||||
*/
|
||||
const msgTypeHandlers: Record<string, (event: MatrixEvent) => string> = {
|
||||
const msgTypeHandlers: Record<string, (event: MatrixEvent) => string | null> = {
|
||||
[MsgType.KeyVerificationRequest]: (event: MatrixEvent) => {
|
||||
const name = (event.sender || {}).name;
|
||||
return _t("%(name)s is requesting verification", { name });
|
||||
@ -156,7 +156,7 @@ class NotifierClass {
|
||||
msg = "";
|
||||
}
|
||||
|
||||
let avatarUrl = null;
|
||||
let avatarUrl: string | null = null;
|
||||
if (ev.sender && !SettingsStore.getValue("lowBandwidth")) {
|
||||
avatarUrl = Avatar.avatarUrlForMember(ev.sender, 40, 40, "crop");
|
||||
}
|
||||
@ -166,8 +166,8 @@ class NotifierClass {
|
||||
// if displayNotification returns non-null, the platform supports
|
||||
// clearing notifications later, so keep track of this.
|
||||
if (notif) {
|
||||
if (this.notifsByRoom[ev.getRoomId()] === undefined) this.notifsByRoom[ev.getRoomId()] = [];
|
||||
this.notifsByRoom[ev.getRoomId()].push(notif);
|
||||
if (this.notifsByRoom[ev.getRoomId()!] === undefined) this.notifsByRoom[ev.getRoomId()!] = [];
|
||||
this.notifsByRoom[ev.getRoomId()!].push(notif);
|
||||
}
|
||||
}
|
||||
|
||||
@ -219,7 +219,7 @@ class NotifierClass {
|
||||
sound ? `audio[src='${sound.url}']` : "#messageAudio",
|
||||
);
|
||||
let audioElement = selector;
|
||||
if (!selector) {
|
||||
if (!audioElement) {
|
||||
if (!sound) {
|
||||
logger.error("No audio element or sound to play for notification");
|
||||
return;
|
||||
@ -378,11 +378,11 @@ class NotifierClass {
|
||||
return global.localStorage.getItem("notifications_hidden") === "true";
|
||||
}
|
||||
|
||||
return this.toolbarHidden;
|
||||
return !!this.toolbarHidden;
|
||||
}
|
||||
|
||||
// XXX: Exported for tests
|
||||
public onSyncStateChange = (state: SyncState, prevState?: SyncState, data?: ISyncStateData): void => {
|
||||
public onSyncStateChange = (state: SyncState, prevState: SyncState | null, data?: ISyncStateData): void => {
|
||||
if (state === SyncState.Syncing) {
|
||||
this.isSyncing = true;
|
||||
} else if (state === SyncState.Stopped || state === SyncState.Error) {
|
||||
@ -411,7 +411,7 @@ class NotifierClass {
|
||||
// If it's an encrypted event and the type is still 'm.room.encrypted',
|
||||
// it hasn't yet been decrypted, so wait until it is.
|
||||
if (ev.isBeingDecrypted() || ev.isDecryptionFailure()) {
|
||||
this.pendingEncryptedEventIds.push(ev.getId());
|
||||
this.pendingEncryptedEventIds.push(ev.getId()!);
|
||||
// don't let the list fill up indefinitely
|
||||
while (this.pendingEncryptedEventIds.length > MAX_PENDING_ENCRYPTED) {
|
||||
this.pendingEncryptedEventIds.shift();
|
||||
@ -427,7 +427,7 @@ class NotifierClass {
|
||||
// in which case it might decrypt soon if the keys arrive
|
||||
if (ev.isDecryptionFailure()) return;
|
||||
|
||||
const idx = this.pendingEncryptedEventIds.indexOf(ev.getId());
|
||||
const idx = this.pendingEncryptedEventIds.indexOf(ev.getId()!);
|
||||
if (idx === -1) return;
|
||||
|
||||
this.pendingEncryptedEventIds.splice(idx, 1);
|
||||
@ -456,7 +456,7 @@ class NotifierClass {
|
||||
public evaluateEvent(ev: MatrixEvent): void {
|
||||
// Mute notifications for broadcast info events
|
||||
if (ev.getType() === VoiceBroadcastInfoEventType) return;
|
||||
let roomId = ev.getRoomId();
|
||||
let roomId = ev.getRoomId()!;
|
||||
if (LegacyCallHandler.instance.getSupportsVirtualRooms()) {
|
||||
// Attempt to translate a virtual room to a native one
|
||||
const nativeRoomId = VoipUserMapper.sharedInstance().nativeRoomForVirtualRoom(roomId);
|
||||
@ -492,7 +492,7 @@ class NotifierClass {
|
||||
this.displayPopupNotification(ev, room);
|
||||
}
|
||||
if (actions.tweaks.sound && this.isAudioEnabled()) {
|
||||
PlatformPeg.get().loudNotification(ev, room);
|
||||
PlatformPeg.get()?.loudNotification(ev, room);
|
||||
this.playAudioNotification(ev, room);
|
||||
}
|
||||
}
|
||||
@ -504,7 +504,7 @@ class NotifierClass {
|
||||
private performCustomEventHandling(ev: MatrixEvent): void {
|
||||
if (ElementCall.CALL_EVENT_TYPE.names.includes(ev.getType()) && SettingsStore.getValue("feature_group_calls")) {
|
||||
ToastStore.sharedInstance().addOrReplaceToast({
|
||||
key: getIncomingCallToastKey(ev.getStateKey()),
|
||||
key: getIncomingCallToastKey(ev.getStateKey()!),
|
||||
priority: 100,
|
||||
component: IncomingCallToast,
|
||||
bodyClassName: "mx_IncomingCallToast",
|
||||
|
@ -238,11 +238,11 @@ export class PosthogAnalytics {
|
||||
}
|
||||
}
|
||||
|
||||
private static async getPlatformProperties(): Promise<PlatformProperties> {
|
||||
private static async getPlatformProperties(): Promise<Partial<PlatformProperties>> {
|
||||
const platform = PlatformPeg.get();
|
||||
let appVersion: string;
|
||||
let appVersion: string | undefined;
|
||||
try {
|
||||
appVersion = await platform.getAppVersion();
|
||||
appVersion = await platform?.getAppVersion();
|
||||
} catch (e) {
|
||||
// this happens if no version is set i.e. in dev
|
||||
appVersion = "unknown";
|
||||
@ -250,7 +250,7 @@ export class PosthogAnalytics {
|
||||
|
||||
return {
|
||||
appVersion,
|
||||
appPlatform: platform.getHumanReadableName(),
|
||||
appPlatform: platform?.getHumanReadableName(),
|
||||
};
|
||||
}
|
||||
|
||||
@ -411,7 +411,7 @@ export class PosthogAnalytics {
|
||||
// All other scenarios should not track a user before they have given
|
||||
// explicit consent that they are ok with their analytics data being collected
|
||||
const options: IPostHogEventOptions = {};
|
||||
const registrationTime = parseInt(window.localStorage.getItem("mx_registration_time"), 10);
|
||||
const registrationTime = parseInt(window.localStorage.getItem("mx_registration_time")!, 10);
|
||||
if (!isNaN(registrationTime)) {
|
||||
options.timestamp = new Date(registrationTime);
|
||||
}
|
||||
|
@ -142,7 +142,7 @@ export function showAnyInviteErrors(
|
||||
});
|
||||
return false;
|
||||
} else {
|
||||
const errorList = [];
|
||||
const errorList: string[] = [];
|
||||
for (const addr of failedUsers) {
|
||||
if (states[addr] === "error") {
|
||||
const reason = inviter.getErrorText(addr);
|
||||
@ -173,8 +173,11 @@ export function showAnyInviteErrors(
|
||||
<div key={addr} className="mx_InviteDialog_tile mx_InviteDialog_tile--inviterError">
|
||||
<div className="mx_InviteDialog_tile_avatarStack">
|
||||
<BaseAvatar
|
||||
url={avatarUrl ? mediaFromMxc(avatarUrl).getSquareThumbnailHttp(24) : null}
|
||||
name={name}
|
||||
url={
|
||||
(avatarUrl && mediaFromMxc(avatarUrl).getSquareThumbnailHttp(24)) ??
|
||||
undefined
|
||||
}
|
||||
name={name!}
|
||||
idName={user?.userId}
|
||||
width={36}
|
||||
height={36}
|
||||
|
@ -32,8 +32,8 @@ const imApiVersion = "1.1";
|
||||
// TODO: Generify the name of this class and all components within - it's not just for Scalar.
|
||||
|
||||
export default class ScalarAuthClient {
|
||||
private scalarToken: string;
|
||||
private termsInteractionCallback: TermsInteractionCallback;
|
||||
private scalarToken: string | null;
|
||||
private termsInteractionCallback?: TermsInteractionCallback;
|
||||
private isDefaultManager: boolean;
|
||||
|
||||
public constructor(private apiUrl: string, private uiUrl: string) {
|
||||
@ -59,7 +59,7 @@ export default class ScalarAuthClient {
|
||||
}
|
||||
}
|
||||
|
||||
private readTokenFromStore(): string {
|
||||
private readTokenFromStore(): string | null {
|
||||
let token = window.localStorage.getItem("mx_scalar_token_at_" + this.apiUrl);
|
||||
if (!token && this.isDefaultManager) {
|
||||
token = window.localStorage.getItem("mx_scalar_token");
|
||||
@ -67,7 +67,7 @@ export default class ScalarAuthClient {
|
||||
return token;
|
||||
}
|
||||
|
||||
private readToken(): string {
|
||||
private readToken(): string | null {
|
||||
if (this.scalarToken) return this.scalarToken;
|
||||
return this.readTokenFromStore();
|
||||
}
|
||||
|
@ -358,7 +358,7 @@ function inviteUser(event: MessageEvent<any>, roomId: string, userId: string): v
|
||||
if (room) {
|
||||
// if they are already invited or joined we can resolve immediately.
|
||||
const member = room.getMember(userId);
|
||||
if (member && ["join", "invite"].includes(member.membership)) {
|
||||
if (member && ["join", "invite"].includes(member.membership!)) {
|
||||
sendResponse(event, {
|
||||
success: true,
|
||||
});
|
||||
@ -389,7 +389,7 @@ function kickUser(event: MessageEvent<any>, roomId: string, userId: string): voi
|
||||
if (room) {
|
||||
// if they are already not in the room we can resolve immediately.
|
||||
const member = room.getMember(userId);
|
||||
if (!member || getEffectiveMembership(member.membership) === EffectiveMembership.Leave) {
|
||||
if (!member || getEffectiveMembership(member.membership!) === EffectiveMembership.Leave) {
|
||||
sendResponse(event, {
|
||||
success: true,
|
||||
});
|
||||
@ -472,7 +472,7 @@ function setWidget(event: MessageEvent<any>, roomId: string | null): void {
|
||||
} else {
|
||||
// Room widget
|
||||
if (!roomId) {
|
||||
sendError(event, _t("Missing roomId."), null);
|
||||
sendError(event, _t("Missing roomId."));
|
||||
return;
|
||||
}
|
||||
WidgetUtils.setRoomWidget(
|
||||
@ -675,7 +675,7 @@ function canSendEvent(event: MessageEvent<any>, roomId: string): void {
|
||||
sendError(event, _t("You are not in this room."));
|
||||
return;
|
||||
}
|
||||
const me = client.credentials.userId;
|
||||
const me = client.credentials.userId!;
|
||||
|
||||
let canSend = false;
|
||||
if (isState) {
|
||||
|
@ -34,7 +34,7 @@ const SEARCH_LIMIT = 10;
|
||||
|
||||
async function serverSideSearch(
|
||||
term: string,
|
||||
roomId: string = undefined,
|
||||
roomId?: string,
|
||||
abortSignal?: AbortSignal,
|
||||
): Promise<{ response: ISearchResponse; query: ISearchRequestBody }> {
|
||||
const client = MatrixClientPeg.get();
|
||||
@ -67,7 +67,7 @@ async function serverSideSearch(
|
||||
|
||||
async function serverSideSearchProcess(
|
||||
term: string,
|
||||
roomId: string = undefined,
|
||||
roomId?: string,
|
||||
abortSignal?: AbortSignal,
|
||||
): Promise<ISearchResults> {
|
||||
const client = MatrixClientPeg.get();
|
||||
@ -158,7 +158,7 @@ async function combinedSearch(searchTerm: string, abortSignal?: AbortSignal): Pr
|
||||
|
||||
async function localSearch(
|
||||
searchTerm: string,
|
||||
roomId: string = undefined,
|
||||
roomId?: string,
|
||||
processResult = true,
|
||||
): Promise<{ response: IResultRoomEvents; query: ISearchArgs }> {
|
||||
const eventIndex = EventIndexPeg.get();
|
||||
@ -195,7 +195,7 @@ export interface ISeshatSearchResults extends ISearchResults {
|
||||
serverSideNextBatch?: string;
|
||||
}
|
||||
|
||||
async function localSearchProcess(searchTerm: string, roomId: string = undefined): Promise<ISeshatSearchResults> {
|
||||
async function localSearchProcess(searchTerm: string, roomId?: string): Promise<ISeshatSearchResults> {
|
||||
const emptyResult = {
|
||||
results: [],
|
||||
highlights: [],
|
||||
@ -244,7 +244,7 @@ async function localPagination(searchResult: ISeshatSearchResults): Promise<ISes
|
||||
const newSlice = result.results.slice(Math.max(result.results.length - newResultCount, 0));
|
||||
restoreEncryptionInfo(newSlice);
|
||||
|
||||
searchResult.pendingRequest = null;
|
||||
searchResult.pendingRequest = undefined;
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -388,8 +388,8 @@ function combineEventSources(
|
||||
*/
|
||||
function combineEvents(
|
||||
previousSearchResult: ISeshatSearchResults,
|
||||
localEvents: IResultRoomEvents = undefined,
|
||||
serverEvents: IResultRoomEvents = undefined,
|
||||
localEvents?: IResultRoomEvents,
|
||||
serverEvents?: IResultRoomEvents,
|
||||
): IResultRoomEvents {
|
||||
const response = {} as IResultRoomEvents;
|
||||
|
||||
@ -451,8 +451,8 @@ function combineEvents(
|
||||
*/
|
||||
function combineResponses(
|
||||
previousSearchResult: ISeshatSearchResults,
|
||||
localEvents: IResultRoomEvents = undefined,
|
||||
serverEvents: IResultRoomEvents = undefined,
|
||||
localEvents: IResultRoomEvents,
|
||||
serverEvents: IResultRoomEvents,
|
||||
): IResultRoomEvents {
|
||||
// Combine our events first.
|
||||
const response = combineEvents(previousSearchResult, localEvents, serverEvents);
|
||||
@ -496,10 +496,10 @@ function combineResponses(
|
||||
}
|
||||
|
||||
interface IEncryptedSeshatEvent {
|
||||
curve25519Key: string;
|
||||
ed25519Key: string;
|
||||
algorithm: string;
|
||||
forwardingCurve25519KeyChain: string[];
|
||||
curve25519Key?: string;
|
||||
ed25519Key?: string;
|
||||
algorithm?: string;
|
||||
forwardingCurve25519KeyChain?: string[];
|
||||
}
|
||||
|
||||
function restoreEncryptionInfo(searchResultSlice: SearchResult[] = []): void {
|
||||
@ -514,7 +514,7 @@ function restoreEncryptionInfo(searchResultSlice: SearchResult[] = []): void {
|
||||
EventType.RoomMessageEncrypted,
|
||||
{ algorithm: ev.algorithm },
|
||||
ev.curve25519Key,
|
||||
ev.ed25519Key,
|
||||
ev.ed25519Key!,
|
||||
);
|
||||
// @ts-ignore
|
||||
mxEv.forwardingCurve25519KeyChain = ev.forwardingCurve25519KeyChain;
|
||||
@ -581,11 +581,7 @@ async function combinedPagination(searchResult: ISeshatSearchResults): Promise<I
|
||||
return result;
|
||||
}
|
||||
|
||||
function eventIndexSearch(
|
||||
term: string,
|
||||
roomId: string = undefined,
|
||||
abortSignal?: AbortSignal,
|
||||
): Promise<ISearchResults> {
|
||||
function eventIndexSearch(term: string, roomId?: string, abortSignal?: AbortSignal): Promise<ISearchResults> {
|
||||
let searchPromise: Promise<ISearchResults>;
|
||||
|
||||
if (roomId !== undefined) {
|
||||
@ -643,11 +639,7 @@ export function searchPagination(searchResult: ISearchResults): Promise<ISearchR
|
||||
else return eventIndexSearchPagination(searchResult);
|
||||
}
|
||||
|
||||
export default function eventSearch(
|
||||
term: string,
|
||||
roomId: string = undefined,
|
||||
abortSignal?: AbortSignal,
|
||||
): Promise<ISearchResults> {
|
||||
export default function eventSearch(term: string, roomId?: string, abortSignal?: AbortSignal): Promise<ISearchResults> {
|
||||
const eventIndex = EventIndexPeg.get();
|
||||
|
||||
if (eventIndex === null) {
|
||||
|
@ -109,7 +109,7 @@ async function getSecretStorageKey({
|
||||
if (!keyInfo) {
|
||||
// if the default key is not available, pretend the default key
|
||||
// isn't set
|
||||
keyId = undefined;
|
||||
keyId = null;
|
||||
}
|
||||
}
|
||||
if (!keyId) {
|
||||
@ -156,7 +156,7 @@ async function getSecretStorageKey({
|
||||
return MatrixClientPeg.get().checkSecretStorageKey(key, keyInfo);
|
||||
},
|
||||
},
|
||||
/* className= */ null,
|
||||
/* className= */ undefined,
|
||||
/* isPriorityModal= */ false,
|
||||
/* isStaticModal= */ false,
|
||||
/* options= */ {
|
||||
@ -206,7 +206,7 @@ export async function getDehydrationKey(
|
||||
}
|
||||
},
|
||||
},
|
||||
/* className= */ null,
|
||||
/* className= */ undefined,
|
||||
/* isPriorityModal= */ false,
|
||||
/* isStaticModal= */ false,
|
||||
/* options= */ {
|
||||
@ -243,7 +243,7 @@ async function onSecretRequested(
|
||||
requestId: string,
|
||||
name: string,
|
||||
deviceTrust: DeviceTrustLevel,
|
||||
): Promise<string> {
|
||||
): Promise<string | undefined> {
|
||||
logger.log("onSecretRequested", userId, deviceId, requestId, name, deviceTrust);
|
||||
const client = MatrixClientPeg.get();
|
||||
if (userId !== client.getUserId()) {
|
||||
@ -259,19 +259,19 @@ async function onSecretRequested(
|
||||
name === "m.cross_signing.user_signing"
|
||||
) {
|
||||
const callbacks = client.getCrossSigningCacheCallbacks();
|
||||
if (!callbacks.getCrossSigningKeyCache) return;
|
||||
if (!callbacks?.getCrossSigningKeyCache) return;
|
||||
const keyId = name.replace("m.cross_signing.", "");
|
||||
const key = await callbacks.getCrossSigningKeyCache(keyId);
|
||||
if (!key) {
|
||||
logger.log(`${keyId} requested by ${deviceId}, but not found in cache`);
|
||||
}
|
||||
return key && encodeBase64(key);
|
||||
return key ? encodeBase64(key) : undefined;
|
||||
} else if (name === "m.megolm_backup.v1") {
|
||||
const key = await client.crypto.getSessionBackupPrivateKey();
|
||||
const key = await client.crypto?.getSessionBackupPrivateKey();
|
||||
if (!key) {
|
||||
logger.log(`session backup key requested by ${deviceId}, but not found in cache`);
|
||||
}
|
||||
return key && encodeBase64(key);
|
||||
return key ? encodeBase64(key) : undefined;
|
||||
}
|
||||
logger.warn("onSecretRequested didn't recognise the secret named ", name);
|
||||
}
|
||||
@ -284,7 +284,7 @@ export const crossSigningCallbacks: ICryptoCallbacks = {
|
||||
};
|
||||
|
||||
export async function promptForBackupPassphrase(): Promise<Uint8Array> {
|
||||
let key: Uint8Array;
|
||||
let key!: Uint8Array;
|
||||
|
||||
const { finished } = Modal.createDialog(
|
||||
RestoreKeyBackupDialog,
|
||||
@ -292,7 +292,7 @@ export async function promptForBackupPassphrase(): Promise<Uint8Array> {
|
||||
showSummary: false,
|
||||
keyCallback: (k: Uint8Array) => (key = k),
|
||||
},
|
||||
null,
|
||||
undefined,
|
||||
/* priority = */ false,
|
||||
/* static = */ true,
|
||||
);
|
||||
@ -338,7 +338,7 @@ export async function accessSecretStorage(func = async (): Promise<void> => {},
|
||||
{
|
||||
forceReset,
|
||||
},
|
||||
null,
|
||||
undefined,
|
||||
/* priority = */ false,
|
||||
/* static = */ true,
|
||||
/* options = */ {
|
||||
|
@ -81,7 +81,7 @@ const singleMxcUpload = async (): Promise<string | null> => {
|
||||
const fileSelector = document.createElement("input");
|
||||
fileSelector.setAttribute("type", "file");
|
||||
fileSelector.onchange = (ev: HTMLInputEvent) => {
|
||||
const file = ev.target.files[0];
|
||||
const file = ev.target.files?.[0];
|
||||
|
||||
Modal.createDialog(UploadConfirmDialog, {
|
||||
file,
|
||||
@ -111,7 +111,7 @@ export const CommandCategories = {
|
||||
|
||||
export type RunResult = XOR<{ error: Error | ITranslatableError }, { promise: Promise<IContent | undefined> }>;
|
||||
|
||||
type RunFn = (this: Command, roomId: string, args: string) => RunResult;
|
||||
type RunFn = (this: Command, roomId: string, args?: string) => RunResult;
|
||||
|
||||
interface ICommandOpts {
|
||||
command: string;
|
||||
@ -159,7 +159,7 @@ export class Command {
|
||||
return this.getCommand() + " " + this.args;
|
||||
}
|
||||
|
||||
public run(roomId: string, threadId: string, args: string): RunResult {
|
||||
public run(roomId: string, threadId: string | null, args?: string): RunResult {
|
||||
// if it has no runFn then its an ignored/nop command (autocomplete only) e.g `/me`
|
||||
if (!this.runFn) {
|
||||
return reject(newTranslatableError("Command error: Unable to handle slash command."));
|
||||
@ -395,12 +395,12 @@ export const Commands = [
|
||||
runFn: function (roomId, args) {
|
||||
if (args) {
|
||||
const cli = MatrixClientPeg.get();
|
||||
const ev = cli.getRoom(roomId).currentState.getStateEvents("m.room.member", cli.getUserId());
|
||||
const ev = cli.getRoom(roomId)?.currentState.getStateEvents("m.room.member", cli.getUserId()!);
|
||||
const content = {
|
||||
...(ev ? ev.getContent() : { membership: "join" }),
|
||||
displayname: args,
|
||||
};
|
||||
return success(cli.sendStateEvent(roomId, "m.room.member", content, cli.getUserId()));
|
||||
return success(cli.sendStateEvent(roomId, "m.room.member", content, cli.getUserId()!));
|
||||
}
|
||||
return reject(this.getUsage());
|
||||
},
|
||||
@ -413,7 +413,7 @@ export const Commands = [
|
||||
description: _td("Changes the avatar of the current room"),
|
||||
isEnabled: () => !isCurrentLocalRoom(),
|
||||
runFn: function (roomId, args) {
|
||||
let promise = Promise.resolve(args);
|
||||
let promise = Promise.resolve(args ?? null);
|
||||
if (!args) {
|
||||
promise = singleMxcUpload();
|
||||
}
|
||||
@ -436,9 +436,9 @@ export const Commands = [
|
||||
runFn: function (roomId, args) {
|
||||
const cli = MatrixClientPeg.get();
|
||||
const room = cli.getRoom(roomId);
|
||||
const userId = cli.getUserId();
|
||||
const userId = cli.getUserId()!;
|
||||
|
||||
let promise = Promise.resolve(args);
|
||||
let promise = Promise.resolve(args ?? null);
|
||||
if (!args) {
|
||||
promise = singleMxcUpload();
|
||||
}
|
||||
@ -446,7 +446,7 @@ export const Commands = [
|
||||
return success(
|
||||
promise.then((url) => {
|
||||
if (!url) return;
|
||||
const ev = room.currentState.getStateEvents("m.room.member", userId);
|
||||
const ev = room?.currentState.getStateEvents("m.room.member", userId);
|
||||
const content = {
|
||||
...(ev ? ev.getContent() : { membership: "join" }),
|
||||
avatar_url: url,
|
||||
@ -463,7 +463,7 @@ export const Commands = [
|
||||
args: "[<mxc_url>]",
|
||||
description: _td("Changes your avatar in all rooms"),
|
||||
runFn: function (roomId, args) {
|
||||
let promise = Promise.resolve(args);
|
||||
let promise = Promise.resolve(args ?? null);
|
||||
if (!args) {
|
||||
promise = singleMxcUpload();
|
||||
}
|
||||
@ -496,7 +496,7 @@ export const Commands = [
|
||||
);
|
||||
}
|
||||
|
||||
const content: MRoomTopicEventContent = room.currentState.getStateEvents("m.room.topic", "")?.getContent();
|
||||
const content = room.currentState.getStateEvents("m.room.topic", "")?.getContent<MRoomTopicEventContent>();
|
||||
const topic = !!content
|
||||
? ContentHelpers.parseTopicContent(content)
|
||||
: { text: _t("This room has no topic.") };
|
||||
@ -874,7 +874,8 @@ export const Commands = [
|
||||
const cli = MatrixClientPeg.get();
|
||||
const room = cli.getRoom(SdkContextClass.instance.roomViewStore.getRoomId());
|
||||
return (
|
||||
room?.currentState.maySendStateEvent(EventType.RoomPowerLevels, cli.getUserId()) && !isLocalRoom(room)
|
||||
!!room?.currentState.maySendStateEvent(EventType.RoomPowerLevels, cli.getUserId()!) &&
|
||||
!isLocalRoom(room)
|
||||
);
|
||||
},
|
||||
runFn: function (roomId, args) {
|
||||
@ -916,7 +917,8 @@ export const Commands = [
|
||||
const cli = MatrixClientPeg.get();
|
||||
const room = cli.getRoom(SdkContextClass.instance.roomViewStore.getRoomId());
|
||||
return (
|
||||
room?.currentState.maySendStateEvent(EventType.RoomPowerLevels, cli.getUserId()) && !isLocalRoom(room)
|
||||
!!room?.currentState.maySendStateEvent(EventType.RoomPowerLevels, cli.getUserId()!) &&
|
||||
!isLocalRoom(room)
|
||||
);
|
||||
},
|
||||
runFn: function (roomId, args) {
|
||||
@ -932,7 +934,7 @@ export const Commands = [
|
||||
}
|
||||
|
||||
const powerLevelEvent = room.currentState.getStateEvents("m.room.power_levels", "");
|
||||
if (!powerLevelEvent.getContent().users[args]) {
|
||||
if (!powerLevelEvent?.getContent().users[args]) {
|
||||
return reject(newTranslatableError("Could not find user in room"));
|
||||
}
|
||||
return success(cli.setPowerLevel(roomId, args, undefined, powerLevelEvent));
|
||||
@ -1113,9 +1115,9 @@ export const Commands = [
|
||||
MatrixClientPeg.get().forceDiscardSession(roomId);
|
||||
|
||||
return success(
|
||||
room.getEncryptionTargetMembers().then((members) => {
|
||||
room?.getEncryptionTargetMembers().then((members) => {
|
||||
// noinspection JSIgnoredPromiseFromCall
|
||||
MatrixClientPeg.get().crypto.ensureOlmSessionsForUsers(
|
||||
MatrixClientPeg.get().crypto?.ensureOlmSessionsForUsers(
|
||||
members.map((m) => m.userId),
|
||||
true,
|
||||
);
|
||||
@ -1167,7 +1169,7 @@ export const Commands = [
|
||||
return reject(this.getUsage());
|
||||
}
|
||||
|
||||
const member = MatrixClientPeg.get().getRoom(roomId).getMember(userId);
|
||||
const member = MatrixClientPeg.get().getRoom(roomId)?.getMember(userId);
|
||||
dis.dispatch<ViewUserPayload>({
|
||||
action: Action.ViewUser,
|
||||
// XXX: We should be using a real member object and not assuming what the receiver wants.
|
||||
@ -1412,7 +1414,7 @@ interface ICmd {
|
||||
export function getCommand(input: string): ICmd {
|
||||
const { cmd, args } = parseCommandString(input);
|
||||
|
||||
if (CommandMap.has(cmd) && CommandMap.get(cmd).isEnabled()) {
|
||||
if (CommandMap.has(cmd) && CommandMap.get(cmd)!.isEnabled()) {
|
||||
return {
|
||||
cmd: CommandMap.get(cmd),
|
||||
args,
|
||||
|
@ -43,12 +43,12 @@ import { getSenderName } from "./utils/event/getSenderName";
|
||||
function getRoomMemberDisplayname(event: MatrixEvent, userId = event.getSender()): string {
|
||||
const client = MatrixClientPeg.get();
|
||||
const roomId = event.getRoomId();
|
||||
const member = client.getRoom(roomId)?.getMember(userId);
|
||||
const member = client.getRoom(roomId)?.getMember(userId!);
|
||||
return member?.name || member?.rawDisplayName || userId || _t("Someone");
|
||||
}
|
||||
|
||||
function textForCallEvent(event: MatrixEvent): () => string {
|
||||
const roomName = MatrixClientPeg.get().getRoom(event.getRoomId()!).name;
|
||||
const roomName = MatrixClientPeg.get().getRoom(event.getRoomId()!)?.name;
|
||||
const isSupported = MatrixClientPeg.get().supportsVoip();
|
||||
|
||||
return isSupported
|
||||
@ -60,7 +60,7 @@ function textForCallEvent(event: MatrixEvent): () => string {
|
||||
// any text to display at all. For this reason they return deferred values
|
||||
// to avoid the expense of looking up translations when they're not needed.
|
||||
|
||||
function textForCallInviteEvent(event: MatrixEvent): () => string | null {
|
||||
function textForCallInviteEvent(event: MatrixEvent): (() => string) | null {
|
||||
const senderName = getSenderName(event);
|
||||
// FIXME: Find a better way to determine this from the event?
|
||||
const isVoice = !event.getContent().offer?.sdp?.includes("m=video");
|
||||
@ -78,9 +78,11 @@ function textForCallInviteEvent(event: MatrixEvent): () => string | null {
|
||||
} else if (!isVoice && !isSupported) {
|
||||
return () => _t("%(senderName)s placed a video call. (not supported by this browser)", { senderName });
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function textForMemberEvent(ev: MatrixEvent, allowJSX: boolean, showHiddenEvents?: boolean): () => string | null {
|
||||
function textForMemberEvent(ev: MatrixEvent, allowJSX: boolean, showHiddenEvents?: boolean): (() => string) | null {
|
||||
// XXX: SYJS-16 "sender is sometimes null for join messages"
|
||||
const senderName = ev.sender?.name || getRoomMemberDisplayname(ev);
|
||||
const targetName = ev.target?.name || getRoomMemberDisplayname(ev, ev.getStateKey());
|
||||
@ -187,9 +189,11 @@ function textForMemberEvent(ev: MatrixEvent, allowJSX: boolean, showHiddenEvents
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function textForTopicEvent(ev: MatrixEvent): () => string | null {
|
||||
function textForTopicEvent(ev: MatrixEvent): (() => string) | null {
|
||||
const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
|
||||
return () =>
|
||||
_t('%(senderDisplayName)s changed the topic to "%(topic)s".', {
|
||||
@ -198,12 +202,12 @@ function textForTopicEvent(ev: MatrixEvent): () => string | null {
|
||||
});
|
||||
}
|
||||
|
||||
function textForRoomAvatarEvent(ev: MatrixEvent): () => string | null {
|
||||
function textForRoomAvatarEvent(ev: MatrixEvent): (() => string) | null {
|
||||
const senderDisplayName = ev?.sender?.name || ev.getSender();
|
||||
return () => _t("%(senderDisplayName)s changed the room avatar.", { senderDisplayName });
|
||||
}
|
||||
|
||||
function textForRoomNameEvent(ev: MatrixEvent): () => string | null {
|
||||
function textForRoomNameEvent(ev: MatrixEvent): (() => string) | null {
|
||||
const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
|
||||
|
||||
if (!ev.getContent().name || ev.getContent().name.trim().length === 0) {
|
||||
@ -224,7 +228,7 @@ function textForRoomNameEvent(ev: MatrixEvent): () => string | null {
|
||||
});
|
||||
}
|
||||
|
||||
function textForTombstoneEvent(ev: MatrixEvent): () => string | null {
|
||||
function textForTombstoneEvent(ev: MatrixEvent): (() => string) | null {
|
||||
const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
|
||||
return () => _t("%(senderDisplayName)s upgraded this room.", { senderDisplayName });
|
||||
}
|
||||
@ -281,7 +285,7 @@ function textForJoinRulesEvent(ev: MatrixEvent, allowJSX: boolean): () => Render
|
||||
}
|
||||
}
|
||||
|
||||
function textForGuestAccessEvent(ev: MatrixEvent): () => string | null {
|
||||
function textForGuestAccessEvent(ev: MatrixEvent): (() => string) | null {
|
||||
const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
|
||||
switch (ev.getContent().guest_access) {
|
||||
case GuestAccess.CanJoin:
|
||||
@ -298,7 +302,7 @@ function textForGuestAccessEvent(ev: MatrixEvent): () => string | null {
|
||||
}
|
||||
}
|
||||
|
||||
function textForServerACLEvent(ev: MatrixEvent): () => string | null {
|
||||
function textForServerACLEvent(ev: MatrixEvent): (() => string) | null {
|
||||
const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
|
||||
const prevContent = ev.getPrevContent();
|
||||
const current = ev.getContent();
|
||||
@ -308,7 +312,7 @@ function textForServerACLEvent(ev: MatrixEvent): () => string | null {
|
||||
allow_ip_literals: prevContent.allow_ip_literals !== false,
|
||||
};
|
||||
|
||||
let getText: () => string = null;
|
||||
let getText: () => string;
|
||||
if (prev.deny.length === 0 && prev.allow.length === 0) {
|
||||
getText = () => _t("%(senderDisplayName)s set the server ACLs for this room.", { senderDisplayName });
|
||||
} else {
|
||||
@ -328,7 +332,7 @@ function textForServerACLEvent(ev: MatrixEvent): () => string | null {
|
||||
return getText;
|
||||
}
|
||||
|
||||
function textForMessageEvent(ev: MatrixEvent): () => string | null {
|
||||
function textForMessageEvent(ev: MatrixEvent): (() => string) | null {
|
||||
if (isLocationEvent(ev)) {
|
||||
return textForLocationEvent(ev);
|
||||
}
|
||||
@ -354,7 +358,7 @@ function textForMessageEvent(ev: MatrixEvent): () => string | null {
|
||||
};
|
||||
}
|
||||
|
||||
function textForCanonicalAliasEvent(ev: MatrixEvent): () => string | null {
|
||||
function textForCanonicalAliasEvent(ev: MatrixEvent): (() => string) | null {
|
||||
const senderName = getSenderName(ev);
|
||||
const oldAlias = ev.getPrevContent().alias;
|
||||
const oldAltAliases = ev.getPrevContent().alt_aliases || [];
|
||||
@ -414,7 +418,7 @@ function textForCanonicalAliasEvent(ev: MatrixEvent): () => string | null {
|
||||
});
|
||||
}
|
||||
|
||||
function textForThreePidInviteEvent(event: MatrixEvent): () => string | null {
|
||||
function textForThreePidInviteEvent(event: MatrixEvent): (() => string) | null {
|
||||
const senderName = getSenderName(event);
|
||||
|
||||
if (!isValid3pidInvite(event)) {
|
||||
@ -432,7 +436,7 @@ function textForThreePidInviteEvent(event: MatrixEvent): () => string | null {
|
||||
});
|
||||
}
|
||||
|
||||
function textForHistoryVisibilityEvent(event: MatrixEvent): () => string | null {
|
||||
function textForHistoryVisibilityEvent(event: MatrixEvent): (() => string) | null {
|
||||
const senderName = getSenderName(event);
|
||||
switch (event.getContent().history_visibility) {
|
||||
case HistoryVisibility.Invited:
|
||||
@ -463,7 +467,7 @@ function textForHistoryVisibilityEvent(event: MatrixEvent): () => string | null
|
||||
}
|
||||
|
||||
// Currently will only display a change if a user's power level is changed
|
||||
function textForPowerEvent(event: MatrixEvent): () => string | null {
|
||||
function textForPowerEvent(event: MatrixEvent): (() => string) | null {
|
||||
const senderName = getSenderName(event);
|
||||
if (!event.getPrevContent()?.users || !event.getContent()?.users) {
|
||||
return null;
|
||||
@ -528,10 +532,10 @@ const onPinnedMessagesClick = (): void => {
|
||||
RightPanelStore.instance.setCard({ phase: RightPanelPhases.PinnedMessages }, false);
|
||||
};
|
||||
|
||||
function textForPinnedEvent(event: MatrixEvent, allowJSX: boolean): () => Renderable {
|
||||
function textForPinnedEvent(event: MatrixEvent, allowJSX: boolean): (() => Renderable) | null {
|
||||
if (!SettingsStore.getValue("feature_pinning")) return null;
|
||||
const senderName = getSenderName(event);
|
||||
const roomId = event.getRoomId();
|
||||
const roomId = event.getRoomId()!;
|
||||
|
||||
const pinned = event.getContent<{ pinned: string[] }>().pinned ?? [];
|
||||
const previouslyPinned: string[] = event.getPrevContent().pinned ?? [];
|
||||
@ -625,7 +629,7 @@ function textForPinnedEvent(event: MatrixEvent, allowJSX: boolean): () => Render
|
||||
return () => _t("%(senderName)s changed the pinned messages for the room.", { senderName });
|
||||
}
|
||||
|
||||
function textForWidgetEvent(event: MatrixEvent): () => string | null {
|
||||
function textForWidgetEvent(event: MatrixEvent): (() => string) | null {
|
||||
const senderName = getSenderName(event);
|
||||
const { name: prevName, type: prevType, url: prevUrl } = event.getPrevContent();
|
||||
const { name, type, url } = event.getContent() || {};
|
||||
@ -661,12 +665,12 @@ function textForWidgetEvent(event: MatrixEvent): () => string | null {
|
||||
}
|
||||
}
|
||||
|
||||
function textForWidgetLayoutEvent(event: MatrixEvent): () => string | null {
|
||||
function textForWidgetLayoutEvent(event: MatrixEvent): (() => string) | null {
|
||||
const senderName = getSenderName(event);
|
||||
return () => _t("%(senderName)s has updated the room layout", { senderName });
|
||||
}
|
||||
|
||||
function textForMjolnirEvent(event: MatrixEvent): () => string | null {
|
||||
function textForMjolnirEvent(event: MatrixEvent): (() => string) | null {
|
||||
const senderName = getSenderName(event);
|
||||
const { entity: prevEntity } = event.getPrevContent();
|
||||
const { entity, recommendation, reason } = event.getContent();
|
||||
@ -795,7 +799,7 @@ function textForMjolnirEvent(event: MatrixEvent): () => string | null {
|
||||
);
|
||||
}
|
||||
|
||||
export function textForLocationEvent(event: MatrixEvent): () => string | null {
|
||||
export function textForLocationEvent(event: MatrixEvent): () => string {
|
||||
return () =>
|
||||
_t("%(senderName)s has shared their location", {
|
||||
senderName: getSenderName(event),
|
||||
@ -817,7 +821,7 @@ function textForRedactedPollAndMessageEvent(ev: MatrixEvent): string {
|
||||
return message;
|
||||
}
|
||||
|
||||
function textForPollStartEvent(event: MatrixEvent): () => string | null {
|
||||
function textForPollStartEvent(event: MatrixEvent): (() => string) | null {
|
||||
return () => {
|
||||
let message = "";
|
||||
|
||||
@ -836,7 +840,7 @@ function textForPollStartEvent(event: MatrixEvent): () => string | null {
|
||||
};
|
||||
}
|
||||
|
||||
function textForPollEndEvent(event: MatrixEvent): () => string | null {
|
||||
function textForPollEndEvent(event: MatrixEvent): (() => string) | null {
|
||||
return () =>
|
||||
_t("%(senderName)s has ended a poll", {
|
||||
senderName: getSenderName(event),
|
||||
@ -846,7 +850,7 @@ function textForPollEndEvent(event: MatrixEvent): () => string | null {
|
||||
type Renderable = string | React.ReactNode | null;
|
||||
|
||||
interface IHandlers {
|
||||
[type: string]: (ev: MatrixEvent, allowJSX: boolean, showHiddenEvents?: boolean) => () => Renderable;
|
||||
[type: string]: (ev: MatrixEvent, allowJSX: boolean, showHiddenEvents?: boolean) => (() => Renderable) | null;
|
||||
}
|
||||
|
||||
const handlers: IHandlers = {
|
||||
|
@ -73,7 +73,7 @@ export function attachRelation(content: IContent, relation?: IEventRelation): vo
|
||||
// exported for tests
|
||||
export function createMessageContent(
|
||||
model: EditorModel,
|
||||
replyToEvent: MatrixEvent,
|
||||
replyToEvent: MatrixEvent | undefined,
|
||||
relation: IEventRelation | undefined,
|
||||
permalinkCreator: RoomPermalinkCreator,
|
||||
includeReplyLegacyFallback = true,
|
||||
@ -148,8 +148,8 @@ export class SendMessageComposer extends React.Component<ISendMessageComposerPro
|
||||
|
||||
private readonly prepareToEncrypt?: DebouncedFunc<() => void>;
|
||||
private readonly editorRef = createRef<BasicMessageComposer>();
|
||||
private model: EditorModel = null;
|
||||
private currentlyComposedEditorState: SerializedPart[] = null;
|
||||
private model: EditorModel;
|
||||
private currentlyComposedEditorState: SerializedPart[] | null = null;
|
||||
private dispatcherRef: string;
|
||||
private sendHistoryManager: SendHistoryManager;
|
||||
|
||||
@ -299,7 +299,7 @@ export class SendMessageComposer extends React.Component<ISendMessageComposerPro
|
||||
const lastMessage = events[i];
|
||||
const userId = MatrixClientPeg.get().getUserId();
|
||||
const messageReactions = this.props.room.relations.getChildEventsForEvent(
|
||||
lastMessage.getId(),
|
||||
lastMessage.getId()!,
|
||||
RelationType.Annotation,
|
||||
EventType.Reaction,
|
||||
);
|
||||
@ -314,7 +314,7 @@ export class SendMessageComposer extends React.Component<ISendMessageComposerPro
|
||||
shouldReact = !myReactionKeys.includes(reaction);
|
||||
}
|
||||
if (shouldReact) {
|
||||
MatrixClientPeg.get().sendEvent(lastMessage.getRoomId(), EventType.Reaction, {
|
||||
MatrixClientPeg.get().sendEvent(lastMessage.getRoomId()!, EventType.Reaction, {
|
||||
"m.relates_to": {
|
||||
rel_type: RelationType.Annotation,
|
||||
event_id: lastMessage.getId(),
|
||||
@ -359,7 +359,7 @@ export class SendMessageComposer extends React.Component<ISendMessageComposerPro
|
||||
|
||||
const replyToEvent = this.props.replyToEvent;
|
||||
let shouldSend = true;
|
||||
let content: IContent;
|
||||
let content: IContent | null = null;
|
||||
|
||||
if (!containsEmote(model) && isSlashCommand(this.model)) {
|
||||
const [cmd, args, commandText] = getSlashCommand(this.model);
|
||||
@ -481,7 +481,7 @@ export class SendMessageComposer extends React.Component<ISendMessageComposerPro
|
||||
localStorage.removeItem(this.editorStateKey);
|
||||
}
|
||||
|
||||
private restoreStoredEditorState(partCreator: PartCreator): Part[] {
|
||||
private restoreStoredEditorState(partCreator: PartCreator): Part[] | null {
|
||||
const replyingToThread = this.props.relation?.key === THREAD_RELATION_TYPE.name;
|
||||
if (replyingToThread) {
|
||||
return null;
|
||||
@ -504,6 +504,8 @@ export class SendMessageComposer extends React.Component<ISendMessageComposerPro
|
||||
logger.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// should save state when editor has contents or reply is open
|
||||
@ -563,6 +565,8 @@ export class SendMessageComposer extends React.Component<ISendMessageComposerPro
|
||||
);
|
||||
return true; // to skip internal onPaste handler
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
private onChange = (): void => {
|
||||
@ -575,7 +579,7 @@ export class SendMessageComposer extends React.Component<ISendMessageComposerPro
|
||||
|
||||
public render(): React.ReactNode {
|
||||
const threadId =
|
||||
this.props.relation?.rel_type === THREAD_RELATION_TYPE.name ? this.props.relation.event_id : null;
|
||||
this.props.relation?.rel_type === THREAD_RELATION_TYPE.name ? this.props.relation.event_id : undefined;
|
||||
return (
|
||||
<div className="mx_SendMessageComposer" onClick={this.focusComposer} onKeyDown={this.onKeyDown}>
|
||||
<BasicMessageComposer
|
||||
|
@ -90,7 +90,7 @@ export default class DevicesPanelEntry extends React.Component<IProps, IState> {
|
||||
Modal.createDialog(
|
||||
LogoutDialog,
|
||||
/* props= */ {},
|
||||
/* className= */ null,
|
||||
/* className= */ undefined,
|
||||
/* isPriority= */ false,
|
||||
/* isStatic= */ true,
|
||||
);
|
||||
|
@ -23,7 +23,7 @@ export interface SettingUpdatedPayload extends ActionPayload {
|
||||
action: Action.SettingUpdated;
|
||||
|
||||
settingName: string;
|
||||
roomId: string;
|
||||
roomId: string | null;
|
||||
level: SettingLevel;
|
||||
newValueAtLevel: SettingLevel;
|
||||
newValue: SettingValueType;
|
||||
|
@ -288,7 +288,7 @@ export function replaceByRegexes(text: string, mapping: Tags): React.ReactNode;
|
||||
export function replaceByRegexes(text: string, mapping: IVariables | Tags): string | React.ReactNode {
|
||||
// We initially store our output as an array of strings and objects (e.g. React components).
|
||||
// This will then be converted to a string or a <span> at the end
|
||||
const output = [text];
|
||||
const output: SubstitutionValue[] = [text];
|
||||
|
||||
// If we insert any components we need to wrap the output in a span. React doesn't like just an array of components.
|
||||
let shouldWrapInSpan = false;
|
||||
@ -319,7 +319,7 @@ export function replaceByRegexes(text: string, mapping: IVariables | Tags): stri
|
||||
// The textual part before the first match
|
||||
const head = inputText.slice(0, match.index);
|
||||
|
||||
const parts = [];
|
||||
const parts: SubstitutionValue[] = [];
|
||||
// keep track of prevMatch
|
||||
let prevMatch;
|
||||
while (match) {
|
||||
@ -327,7 +327,7 @@ export function replaceByRegexes(text: string, mapping: IVariables | Tags): stri
|
||||
prevMatch = match;
|
||||
const capturedGroups = match.slice(2);
|
||||
|
||||
let replaced;
|
||||
let replaced: SubstitutionValue;
|
||||
// If substitution is a function, call it
|
||||
if (mapping[regexpString] instanceof Function) {
|
||||
replaced = ((mapping as Tags)[regexpString] as Function)(...capturedGroups);
|
||||
@ -434,7 +434,7 @@ export function setLanguage(preferredLangs: string | string[]): Promise<void> {
|
||||
|
||||
return getLanguageRetry(i18nFolder + availLangs[langToUse].fileName);
|
||||
})
|
||||
.then(async (langData): Promise<ICounterpartTranslation> => {
|
||||
.then(async (langData): Promise<ICounterpartTranslation | undefined> => {
|
||||
counterpart.registerTranslations(langToUse, langData);
|
||||
await registerCustomTranslations();
|
||||
counterpart.setLocale(langToUse);
|
||||
@ -664,7 +664,7 @@ export async function registerCustomTranslations(): Promise<void> {
|
||||
if (!lookupUrl) return; // easy - nothing to do
|
||||
|
||||
try {
|
||||
let json: ICustomTranslations;
|
||||
let json: Optional<ICustomTranslations>;
|
||||
if (Date.now() >= cachedCustomTranslationsExpire) {
|
||||
json = CustomTranslationOptions.lookupFn
|
||||
? CustomTranslationOptions.lookupFn(lookupUrl)
|
||||
|
@ -871,7 +871,7 @@ export class ElementCall extends Call {
|
||||
private onScreenshareRequest = async (ev: CustomEvent<IWidgetApiRequest>): Promise<void> => {
|
||||
ev.preventDefault();
|
||||
|
||||
if (PlatformPeg.get().supportsDesktopCapturer()) {
|
||||
if (PlatformPeg.get()?.supportsDesktopCapturer()) {
|
||||
await this.messaging!.transport.reply(ev.detail, { pending: true });
|
||||
|
||||
const { finished } = Modal.createDialog(DesktopCapturerSourcePicker);
|
||||
|
@ -55,10 +55,10 @@ export class NotificationUtils {
|
||||
// "highlight: true/false,
|
||||
// }
|
||||
// If the actions couldn't be decoded then returns null.
|
||||
public static decodeActions(actions: PushRuleAction[]): IEncodedActions {
|
||||
public static decodeActions(actions: PushRuleAction[]): IEncodedActions | null {
|
||||
let notify = false;
|
||||
let sound = null;
|
||||
let highlight = false;
|
||||
let sound: string | undefined;
|
||||
let highlight: boolean | undefined = false;
|
||||
|
||||
for (let i = 0; i < actions.length; ++i) {
|
||||
const action = actions[i];
|
||||
@ -87,7 +87,7 @@ export class NotificationUtils {
|
||||
}
|
||||
|
||||
const result: IEncodedActions = { notify, highlight };
|
||||
if (sound !== null) {
|
||||
if (sound !== undefined) {
|
||||
result.sound = sound;
|
||||
}
|
||||
return result;
|
||||
|
@ -62,7 +62,7 @@ export class PushRuleVectorState {
|
||||
* category or in PushRuleVectorState.LOUD, regardless of its enabled
|
||||
* state. Returns null if it does not match these categories.
|
||||
*/
|
||||
public static contentRuleVectorStateKind(rule: IPushRule): VectorState {
|
||||
public static contentRuleVectorStateKind(rule: IPushRule): VectorState | null {
|
||||
const decoded = NotificationUtils.decodeActions(rule.actions);
|
||||
|
||||
if (!decoded) {
|
||||
@ -77,7 +77,7 @@ export class PushRuleVectorState {
|
||||
if (decoded.highlight) {
|
||||
tweaks++;
|
||||
}
|
||||
let stateKind = null;
|
||||
let stateKind: VectorState | null = null;
|
||||
switch (tweaks) {
|
||||
case 0:
|
||||
stateKind = VectorState.On;
|
||||
|
@ -28,5 +28,5 @@ export class StandardActions {
|
||||
public static ACTION_HIGHLIGHT = encodeActions({ notify: true, highlight: true });
|
||||
public static ACTION_HIGHLIGHT_DEFAULT_SOUND = encodeActions({ notify: true, sound: "default", highlight: true });
|
||||
public static ACTION_DONT_NOTIFY = encodeActions({ notify: false });
|
||||
public static ACTION_DISABLED: PushRuleAction[] | null = null;
|
||||
public static ACTION_DISABLED: PushRuleAction[] | undefined = undefined;
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ class VectorPushRuleDefinition {
|
||||
}
|
||||
|
||||
// Translate the rule actions and its enabled value into vector state
|
||||
public ruleToVectorState(rule: IAnnotatedPushRule): VectorState {
|
||||
public ruleToVectorState(rule: IAnnotatedPushRule): VectorState | undefined {
|
||||
let enabled = false;
|
||||
if (rule) {
|
||||
enabled = rule.enabled;
|
||||
|
@ -81,8 +81,8 @@ async function collectBugReport(opts: IOpts = {}, gzipLogs = true): Promise<Form
|
||||
}
|
||||
|
||||
if (client) {
|
||||
body.append("user_id", client.credentials.userId);
|
||||
body.append("device_id", client.deviceId);
|
||||
body.append("user_id", client.credentials.userId!);
|
||||
body.append("device_id", client.deviceId!);
|
||||
|
||||
// TODO: make this work with rust crypto
|
||||
if (client.isCryptoEnabled() && client.crypto) {
|
||||
@ -181,7 +181,7 @@ async function collectBugReport(opts: IOpts = {}, gzipLogs = true): Promise<Form
|
||||
}
|
||||
}
|
||||
|
||||
body.append("mx_local_settings", localStorage.getItem("mx_local_settings"));
|
||||
body.append("mx_local_settings", localStorage.getItem("mx_local_settings")!);
|
||||
|
||||
if (opts.sendLogs) {
|
||||
progressCallback(_t("Collecting logs"));
|
||||
@ -293,9 +293,9 @@ export async function submitFeedback(
|
||||
canContact = false,
|
||||
extraData: Record<string, string> = {},
|
||||
): Promise<void> {
|
||||
let version = "UNKNOWN";
|
||||
let version: string | undefined;
|
||||
try {
|
||||
version = await PlatformPeg.get().getAppVersion();
|
||||
version = await PlatformPeg.get()?.getAppVersion();
|
||||
} catch (err) {} // PlatformPeg already logs this.
|
||||
|
||||
const body = new FormData();
|
||||
@ -304,7 +304,7 @@ export async function submitFeedback(
|
||||
body.append("can_contact", canContact ? "yes" : "no");
|
||||
|
||||
body.append("app", "element-web");
|
||||
body.append("version", version);
|
||||
body.append("version", version || "UNKNOWN");
|
||||
body.append("platform", PlatformPeg.get().getHumanReadableName());
|
||||
body.append("user_id", MatrixClientPeg.get()?.getUserId());
|
||||
|
||||
|
@ -86,9 +86,9 @@ export default class Sizer {
|
||||
|
||||
public clearItemSize(item: HTMLElement): void {
|
||||
if (this.vertical) {
|
||||
item.style.height = null;
|
||||
item.style.height = null!;
|
||||
} else {
|
||||
item.style.width = null;
|
||||
item.style.width = null!;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -91,7 +91,7 @@ function getLevelOrder(setting: ISetting): SettingLevel[] {
|
||||
|
||||
export type CallbackFn = (
|
||||
settingName: string,
|
||||
roomId: string,
|
||||
roomId: string | null,
|
||||
atLevel: SettingLevel,
|
||||
newValAtLevel: any,
|
||||
newVal: any,
|
||||
@ -133,7 +133,7 @@ export default class SettingsStore {
|
||||
// when the setting changes. We track which rooms we're monitoring though to ensure we
|
||||
// don't duplicate updates on the bus.
|
||||
private static watchers = new Map<string, WatchCallbackFn>();
|
||||
private static monitors = new Map<string, Map<string, string>>(); // { settingName => { roomId => callbackRef } }
|
||||
private static monitors = new Map<string, Map<string | null, string>>(); // { settingName => { roomId => callbackRef } }
|
||||
|
||||
// Counter used for generation of watcher IDs
|
||||
private static watcherCount = 1;
|
||||
@ -205,7 +205,7 @@ export default class SettingsStore {
|
||||
return;
|
||||
}
|
||||
|
||||
defaultWatchManager.unwatchSetting(SettingsStore.watchers.get(watcherReference));
|
||||
defaultWatchManager.unwatchSetting(SettingsStore.watchers.get(watcherReference)!);
|
||||
SettingsStore.watchers.delete(watcherReference);
|
||||
}
|
||||
|
||||
@ -223,7 +223,7 @@ export default class SettingsStore {
|
||||
if (!this.monitors.has(settingName)) this.monitors.set(settingName, new Map());
|
||||
|
||||
const registerWatcher = (): void => {
|
||||
this.monitors.get(settingName).set(
|
||||
this.monitors.get(settingName)!.set(
|
||||
roomId,
|
||||
SettingsStore.watchSetting(
|
||||
settingName,
|
||||
@ -242,7 +242,7 @@ export default class SettingsStore {
|
||||
);
|
||||
};
|
||||
|
||||
const rooms = Array.from(this.monitors.get(settingName).keys());
|
||||
const rooms = Array.from(this.monitors.get(settingName)!.keys());
|
||||
const hasRoom = rooms.find((r) => r === roomId || r === null);
|
||||
if (!hasRoom) {
|
||||
registerWatcher();
|
||||
@ -250,9 +250,9 @@ export default class SettingsStore {
|
||||
if (roomId === null) {
|
||||
// Unregister all existing watchers and register the new one
|
||||
rooms.forEach((roomId) => {
|
||||
SettingsStore.unwatchSetting(this.monitors.get(settingName).get(roomId));
|
||||
SettingsStore.unwatchSetting(this.monitors.get(settingName)!.get(roomId));
|
||||
});
|
||||
this.monitors.get(settingName).clear();
|
||||
this.monitors.get(settingName)!.clear();
|
||||
registerWatcher();
|
||||
} // else a watcher is already registered for the room, so don't bother registering it again
|
||||
}
|
||||
@ -265,7 +265,7 @@ export default class SettingsStore {
|
||||
* The level to get the display name for; Defaults to 'default'.
|
||||
* @return {String} The display name for the setting, or null if not found.
|
||||
*/
|
||||
public static getDisplayName(settingName: string, atLevel = SettingLevel.DEFAULT): string {
|
||||
public static getDisplayName(settingName: string, atLevel = SettingLevel.DEFAULT): string | null {
|
||||
if (!SETTINGS[settingName] || !SETTINGS[settingName].displayName) return null;
|
||||
|
||||
let displayName = SETTINGS[settingName].displayName;
|
||||
@ -296,7 +296,7 @@ export default class SettingsStore {
|
||||
*/
|
||||
public static isFeature(settingName: string): boolean {
|
||||
if (!SETTINGS[settingName]) return false;
|
||||
return SETTINGS[settingName].isFeature;
|
||||
return !!SETTINGS[settingName].isFeature;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -319,7 +319,7 @@ export default class SettingsStore {
|
||||
}
|
||||
}
|
||||
|
||||
public static getLabGroup(settingName: string): LabGroup {
|
||||
public static getLabGroup(settingName: string): LabGroup | undefined {
|
||||
if (SettingsStore.isFeature(settingName)) {
|
||||
return (<IFeature>SETTINGS[settingName]).labsGroup;
|
||||
}
|
||||
@ -333,7 +333,7 @@ export default class SettingsStore {
|
||||
*/
|
||||
public static isEnabled(settingName: string): boolean {
|
||||
if (!SETTINGS[settingName]) return false;
|
||||
return SETTINGS[settingName].controller ? !SETTINGS[settingName].controller.settingDisabled : true;
|
||||
return !SETTINGS[settingName].controller?.settingDisabled ?? true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -559,7 +559,7 @@ export default class SettingsStore {
|
||||
throw new Error("Setting '" + settingName + "' does not appear to be a setting.");
|
||||
}
|
||||
|
||||
return level === SettingLevel.DEFAULT || setting.supportedLevels.includes(level);
|
||||
return level === SettingLevel.DEFAULT || !!setting.supportedLevels?.includes(level);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -568,7 +568,7 @@ export default class SettingsStore {
|
||||
* @param {string} settingName The setting name.
|
||||
* @return {SettingLevel}
|
||||
*/
|
||||
public static firstSupportedLevel(settingName: string): SettingLevel {
|
||||
public static firstSupportedLevel(settingName: string): SettingLevel | null {
|
||||
// Verify that the setting is actually a setting
|
||||
const setting = SETTINGS[settingName];
|
||||
if (!setting) {
|
||||
@ -723,10 +723,10 @@ export default class SettingsStore {
|
||||
logger.log(`--- END DEBUG`);
|
||||
}
|
||||
|
||||
private static getHandler(settingName: string, level: SettingLevel): SettingsHandler {
|
||||
private static getHandler(settingName: string, level: SettingLevel): SettingsHandler | null {
|
||||
const handlers = SettingsStore.getHandlers(settingName);
|
||||
if (!handlers[level]) return null;
|
||||
return handlers[level];
|
||||
return handlers[level]!;
|
||||
}
|
||||
|
||||
private static getHandlers(settingName: string): HandlerMap {
|
||||
|
@ -16,9 +16,9 @@ limitations under the License.
|
||||
|
||||
import { SettingLevel } from "./SettingLevel";
|
||||
|
||||
export type CallbackFn = (changedInRoomId: string, atLevel: SettingLevel, newValAtLevel: any) => void;
|
||||
export type CallbackFn = (changedInRoomId: string | null, atLevel: SettingLevel, newValAtLevel: any) => void;
|
||||
|
||||
const IRRELEVANT_ROOM: string = null;
|
||||
const IRRELEVANT_ROOM: string | null = null;
|
||||
|
||||
/**
|
||||
* Generalized management class for dealing with watchers on a per-handler (per-level)
|
||||
@ -26,13 +26,13 @@ const IRRELEVANT_ROOM: string = null;
|
||||
* class, which are then proxied outwards to any applicable watchers.
|
||||
*/
|
||||
export class WatchManager {
|
||||
private watchers = new Map<string, Map<string | symbol, CallbackFn[]>>(); // settingName -> roomId -> CallbackFn[]
|
||||
private watchers = new Map<string, Map<string | null, CallbackFn[]>>(); // settingName -> roomId -> CallbackFn[]
|
||||
|
||||
// Proxy for handlers to delegate changes to this manager
|
||||
public watchSetting(settingName: string, roomId: string | null, cb: CallbackFn): void {
|
||||
if (!this.watchers.has(settingName)) this.watchers.set(settingName, new Map());
|
||||
if (!this.watchers.get(settingName).has(roomId)) this.watchers.get(settingName).set(roomId, []);
|
||||
this.watchers.get(settingName).get(roomId).push(cb);
|
||||
if (!this.watchers.get(settingName)!.has(roomId)) this.watchers.get(settingName)!.set(roomId, []);
|
||||
this.watchers.get(settingName)!.get(roomId)!.push(cb);
|
||||
}
|
||||
|
||||
// Proxy for handlers to delegate changes to this manager
|
||||
@ -59,18 +59,18 @@ export class WatchManager {
|
||||
|
||||
if (!this.watchers.has(settingName)) return;
|
||||
|
||||
const roomWatchers = this.watchers.get(settingName);
|
||||
const callbacks = [];
|
||||
const roomWatchers = this.watchers.get(settingName)!;
|
||||
const callbacks: CallbackFn[] = [];
|
||||
|
||||
if (inRoomId !== null && roomWatchers.has(inRoomId)) {
|
||||
callbacks.push(...roomWatchers.get(inRoomId));
|
||||
callbacks.push(...roomWatchers.get(inRoomId)!);
|
||||
}
|
||||
|
||||
if (!inRoomId) {
|
||||
// Fire updates to all the individual room watchers too, as they probably care about the change higher up.
|
||||
callbacks.push(...Array.from(roomWatchers.values()).flat(1));
|
||||
} else if (roomWatchers.has(IRRELEVANT_ROOM)) {
|
||||
callbacks.push(...roomWatchers.get(IRRELEVANT_ROOM));
|
||||
callbacks.push(...roomWatchers.get(IRRELEVANT_ROOM)!);
|
||||
}
|
||||
|
||||
for (const callback of callbacks) {
|
||||
|
@ -20,6 +20,6 @@ import { SettingLevel } from "../SettingLevel";
|
||||
|
||||
export default class ReloadOnChangeController extends SettingController {
|
||||
public onChange(level: SettingLevel, roomId: string, newValue: any): void {
|
||||
PlatformPeg.get().reload();
|
||||
PlatformPeg.get()?.reload();
|
||||
}
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ export default abstract class SettingController {
|
||||
* @param {*} newValue The new value for the setting, may be null.
|
||||
* @return {boolean} Whether the settings change should be accepted.
|
||||
*/
|
||||
public async beforeChange(level: SettingLevel, roomId: string, newValue: any): Promise<boolean> {
|
||||
public async beforeChange(level: SettingLevel, roomId: string | null, newValue: any): Promise<boolean> {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -63,7 +63,7 @@ export default abstract class SettingController {
|
||||
* @param {String} roomId The room ID, may be null.
|
||||
* @param {*} newValue The new value for the setting, may be null.
|
||||
*/
|
||||
public onChange(level: SettingLevel, roomId: string, newValue: any): void {
|
||||
public onChange(level: SettingLevel, roomId: string | null, newValue: any): void {
|
||||
// do nothing by default
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,7 @@ import SettingsHandler from "./SettingsHandler";
|
||||
*/
|
||||
export default abstract class AbstractLocalStorageSettingsHandler extends SettingsHandler {
|
||||
// Shared cache between all subclass instances
|
||||
private static itemCache = new Map<string, string>();
|
||||
private static itemCache = new Map<string, string | null>();
|
||||
private static objectCache = new Map<string, object>();
|
||||
private static storageListenerBound = false;
|
||||
|
||||
@ -51,14 +51,14 @@ export default abstract class AbstractLocalStorageSettingsHandler extends Settin
|
||||
}
|
||||
}
|
||||
|
||||
protected getItem(key: string): string {
|
||||
protected getItem(key: string): string | null {
|
||||
if (!AbstractLocalStorageSettingsHandler.itemCache.has(key)) {
|
||||
const value = localStorage.getItem(key);
|
||||
AbstractLocalStorageSettingsHandler.itemCache.set(key, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
return AbstractLocalStorageSettingsHandler.itemCache.get(key);
|
||||
return AbstractLocalStorageSettingsHandler.itemCache.get(key)!;
|
||||
}
|
||||
|
||||
protected getBoolean(key: string): boolean | null {
|
||||
@ -72,7 +72,7 @@ export default abstract class AbstractLocalStorageSettingsHandler extends Settin
|
||||
protected getObject<T extends object>(key: string): T | null {
|
||||
if (!AbstractLocalStorageSettingsHandler.objectCache.has(key)) {
|
||||
try {
|
||||
const value = JSON.parse(localStorage.getItem(key));
|
||||
const value = JSON.parse(localStorage.getItem(key)!);
|
||||
AbstractLocalStorageSettingsHandler.objectCache.set(key, value);
|
||||
return value;
|
||||
} catch (err) {
|
||||
|
@ -40,7 +40,7 @@ export default class PlatformSettingsHandler extends SettingsHandler {
|
||||
this.store = {};
|
||||
// Load setting values as they are async and `getValue` must be synchronous
|
||||
Object.entries(SETTINGS).forEach(([key, setting]) => {
|
||||
if (setting.supportedLevels.includes(SettingLevel.PLATFORM) && payload.platform.supportsSetting(key)) {
|
||||
if (setting.supportedLevels?.includes(SettingLevel.PLATFORM) && payload.platform.supportsSetting(key)) {
|
||||
payload.platform.getSettingValue(key).then((value: any) => {
|
||||
this.store[key] = value;
|
||||
});
|
||||
@ -50,19 +50,19 @@ export default class PlatformSettingsHandler extends SettingsHandler {
|
||||
};
|
||||
|
||||
public canSetValue(settingName: string, roomId: string): boolean {
|
||||
return PlatformPeg.get().supportsSetting(settingName);
|
||||
return PlatformPeg.get()?.supportsSetting(settingName) ?? false;
|
||||
}
|
||||
|
||||
public getValue(settingName: string, roomId: string): any {
|
||||
return this.store[settingName];
|
||||
}
|
||||
|
||||
public setValue(settingName: string, roomId: string, newValue: any): Promise<void> {
|
||||
public async setValue(settingName: string, roomId: string, newValue: any): Promise<void> {
|
||||
this.store[settingName] = newValue; // keep cache up to date for synchronous access
|
||||
return PlatformPeg.get().setSettingValue(settingName, newValue);
|
||||
await PlatformPeg.get()?.setSettingValue(settingName, newValue);
|
||||
}
|
||||
|
||||
public isSupported(): boolean {
|
||||
return PlatformPeg.get().supportsSetting();
|
||||
return PlatformPeg.get()?.supportsSetting() ?? false;
|
||||
}
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ export default class RoomSettingsHandler extends MatrixClientBackedSettingsHandl
|
||||
}
|
||||
|
||||
private onEvent = (event: MatrixEvent, state: RoomState, prevEvent: MatrixEvent): void => {
|
||||
const roomId = event.getRoomId();
|
||||
const roomId = event.getRoomId()!;
|
||||
const room = this.client.getRoom(roomId);
|
||||
|
||||
// Note: in tests and during the encryption setup on initial load we might not have
|
||||
@ -124,7 +124,7 @@ export default class RoomSettingsHandler extends MatrixClientBackedSettingsHandl
|
||||
let eventType = DEFAULT_SETTINGS_EVENT_TYPE;
|
||||
if (settingName === "urlPreviewsEnabled") eventType = "org.matrix.room.preview_urls";
|
||||
|
||||
return room?.currentState.maySendStateEvent(eventType, this.client.getUserId()) ?? false;
|
||||
return room?.currentState.maySendStateEvent(eventType, this.client.getUserId()!) ?? false;
|
||||
}
|
||||
|
||||
public isSupported(): boolean {
|
||||
|
@ -32,7 +32,7 @@ export default abstract class SettingsHandler {
|
||||
* @param {String} roomId The room ID to read from, may be null.
|
||||
* @returns {*} The setting value, or null if not found.
|
||||
*/
|
||||
public abstract getValue(settingName: string, roomId: string): any;
|
||||
public abstract getValue(settingName: string, roomId: string | null): any;
|
||||
|
||||
/**
|
||||
* Sets the value for a particular setting at this level for a particular room.
|
||||
@ -45,7 +45,7 @@ export default abstract class SettingsHandler {
|
||||
* @param {*} newValue The new value for the setting, may be null.
|
||||
* @returns {Promise} Resolves when the setting has been saved.
|
||||
*/
|
||||
public abstract setValue(settingName: string, roomId: string, newValue: any): Promise<void>;
|
||||
public abstract setValue(settingName: string, roomId: string | null, newValue: any): Promise<void>;
|
||||
|
||||
/**
|
||||
* Determines if the current user is able to set the value of the given setting
|
||||
@ -54,7 +54,7 @@ export default abstract class SettingsHandler {
|
||||
* @param {String} roomId The room ID to check in, may be null
|
||||
* @returns {boolean} True if the setting can be set by the user, false otherwise.
|
||||
*/
|
||||
public abstract canSetValue(settingName: string, roomId: string): boolean;
|
||||
public abstract canSetValue(settingName: string, roomId: string | null): boolean;
|
||||
|
||||
/**
|
||||
* Determines if this level is supported on this device.
|
||||
|
@ -30,7 +30,7 @@ export class FontWatcher implements IWatcher {
|
||||
// Externally we tell the user the font is size 15. Internally we use 10.
|
||||
public static readonly SIZE_DIFF = 5;
|
||||
|
||||
private dispatcherRef: string;
|
||||
private dispatcherRef: string | null;
|
||||
|
||||
public constructor() {
|
||||
this.dispatcherRef = null;
|
||||
@ -42,6 +42,7 @@ export class FontWatcher implements IWatcher {
|
||||
}
|
||||
|
||||
public stop(): void {
|
||||
if (!this.dispatcherRef) return;
|
||||
dis.unregister(this.dispatcherRef);
|
||||
}
|
||||
|
||||
@ -77,7 +78,7 @@ export class FontWatcher implements IWatcher {
|
||||
if (fontSize !== size) {
|
||||
SettingsStore.setValue("baseFontSize", null, SettingLevel.DEVICE, fontSize);
|
||||
}
|
||||
document.querySelector<HTMLElement>(":root").style.fontSize = toPx(fontSize);
|
||||
document.querySelector<HTMLElement>(":root")!.style.fontSize = toPx(fontSize);
|
||||
};
|
||||
|
||||
private setSystemFont = ({
|
||||
|
@ -26,9 +26,9 @@ import { ActionPayload } from "../../dispatcher/payloads";
|
||||
import { SettingLevel } from "../SettingLevel";
|
||||
|
||||
export default class ThemeWatcher {
|
||||
private themeWatchRef: string;
|
||||
private systemThemeWatchRef: string;
|
||||
private dispatcherRef: string;
|
||||
private themeWatchRef: string | null;
|
||||
private systemThemeWatchRef: string | null;
|
||||
private dispatcherRef: string | null;
|
||||
|
||||
private preferDark: MediaQueryList;
|
||||
private preferLight: MediaQueryList;
|
||||
@ -67,9 +67,9 @@ export default class ThemeWatcher {
|
||||
this.preferLight.removeEventListener("change", this.onChange);
|
||||
this.preferHighContrast.removeEventListener("change", this.onChange);
|
||||
}
|
||||
SettingsStore.unwatchSetting(this.systemThemeWatchRef);
|
||||
SettingsStore.unwatchSetting(this.themeWatchRef);
|
||||
dis.unregister(this.dispatcherRef);
|
||||
if (this.systemThemeWatchRef) SettingsStore.unwatchSetting(this.systemThemeWatchRef);
|
||||
if (this.themeWatchRef) SettingsStore.unwatchSetting(this.themeWatchRef);
|
||||
if (this.dispatcherRef) dis.unregister(this.dispatcherRef);
|
||||
}
|
||||
|
||||
private onChange = (): void => {
|
||||
|
@ -92,7 +92,7 @@ export abstract class AsyncStore<T extends Object> extends EventEmitter {
|
||||
* @param {T|*} newState The new state of the store.
|
||||
* @param {boolean} quiet If true, the function will not raise an UPDATE_EVENT.
|
||||
*/
|
||||
protected async reset(newState: T | Object = null, quiet = false): Promise<void> {
|
||||
protected async reset(newState: T | Object | null = null, quiet = false): Promise<void> {
|
||||
await this.lock.acquireAsync();
|
||||
try {
|
||||
this.storeState = Object.freeze(<T>(newState || {}));
|
||||
|
@ -19,7 +19,7 @@ import { AsyncStore } from "./AsyncStore";
|
||||
import { ActionPayload } from "../dispatcher/payloads";
|
||||
|
||||
interface IState {
|
||||
hostSignupActive?: boolean;
|
||||
hostSignupActive: boolean;
|
||||
}
|
||||
|
||||
export class HostSignupStore extends AsyncStore<IState> {
|
||||
|
@ -22,7 +22,7 @@ import { ActionPayload } from "../dispatcher/payloads";
|
||||
import { DoAfterSyncPreparedPayload } from "../dispatcher/payloads/DoAfterSyncPreparedPayload";
|
||||
|
||||
interface IState {
|
||||
deferredAction: ActionPayload;
|
||||
deferredAction: ActionPayload | null;
|
||||
}
|
||||
|
||||
const INITIAL_STATE: IState = {
|
||||
@ -83,8 +83,8 @@ class LifecycleStore extends Store<ActionPayload> {
|
||||
}
|
||||
}
|
||||
|
||||
let singletonLifecycleStore = null;
|
||||
let singletonLifecycleStore: LifecycleStore | null = null;
|
||||
if (!singletonLifecycleStore) {
|
||||
singletonLifecycleStore = new LifecycleStore();
|
||||
}
|
||||
export default singletonLifecycleStore;
|
||||
export default singletonLifecycleStore!;
|
||||
|
@ -110,15 +110,15 @@ export class OwnBeaconStore extends AsyncStoreWithClient<OwnBeaconStoreState> {
|
||||
* ordered by creation time descending
|
||||
*/
|
||||
private liveBeaconIds: BeaconIdentifier[] = [];
|
||||
private locationInterval: number;
|
||||
private geolocationError: GeolocationError | undefined;
|
||||
private clearPositionWatch: ClearWatchCallback | undefined;
|
||||
private locationInterval?: number;
|
||||
private geolocationError?: GeolocationError;
|
||||
private clearPositionWatch?: ClearWatchCallback;
|
||||
/**
|
||||
* Track when the last position was published
|
||||
* So we can manually get position on slow interval
|
||||
* when the target is stationary
|
||||
*/
|
||||
private lastPublishedPositionTimestamp: number | undefined;
|
||||
private lastPublishedPositionTimestamp?: number;
|
||||
|
||||
public constructor() {
|
||||
super(defaultDispatcher);
|
||||
@ -231,7 +231,7 @@ export class OwnBeaconStore extends AsyncStoreWithClient<OwnBeaconStoreState> {
|
||||
*/
|
||||
|
||||
private onNewBeacon = (_event: MatrixEvent, beacon: Beacon): void => {
|
||||
if (!isOwnBeacon(beacon, this.matrixClient.getUserId())) {
|
||||
if (!isOwnBeacon(beacon, this.matrixClient.getUserId()!)) {
|
||||
return;
|
||||
}
|
||||
this.addBeacon(beacon);
|
||||
@ -242,7 +242,7 @@ export class OwnBeaconStore extends AsyncStoreWithClient<OwnBeaconStoreState> {
|
||||
* This will be called when a beacon is replaced
|
||||
*/
|
||||
private onUpdateBeacon = (_event: MatrixEvent, beacon: Beacon): void => {
|
||||
if (!isOwnBeacon(beacon, this.matrixClient.getUserId())) {
|
||||
if (!isOwnBeacon(beacon, this.matrixClient.getUserId()!)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -309,7 +309,7 @@ export class OwnBeaconStore extends AsyncStoreWithClient<OwnBeaconStoreState> {
|
||||
}
|
||||
|
||||
private initialiseBeaconState = (): void => {
|
||||
const userId = this.matrixClient.getUserId();
|
||||
const userId = this.matrixClient.getUserId()!;
|
||||
const visibleRooms = this.matrixClient.getVisibleRooms();
|
||||
|
||||
visibleRooms.forEach((room) => {
|
||||
@ -329,7 +329,7 @@ export class OwnBeaconStore extends AsyncStoreWithClient<OwnBeaconStoreState> {
|
||||
this.beaconsByRoomId.set(beacon.roomId, new Set<string>());
|
||||
}
|
||||
|
||||
this.beaconsByRoomId.get(beacon.roomId).add(beacon.identifier);
|
||||
this.beaconsByRoomId.get(beacon.roomId)!.add(beacon.identifier);
|
||||
|
||||
beacon.monitorLiveness();
|
||||
};
|
||||
@ -343,7 +343,7 @@ export class OwnBeaconStore extends AsyncStoreWithClient<OwnBeaconStoreState> {
|
||||
if (!this.beacons.has(beaconId)) {
|
||||
return;
|
||||
}
|
||||
this.beacons.get(beaconId).destroy();
|
||||
this.beacons.get(beaconId)!.destroy();
|
||||
this.beacons.delete(beaconId);
|
||||
|
||||
this.checkLiveness();
|
||||
|
@ -43,7 +43,7 @@ export class OwnProfileStore extends AsyncStoreWithClient<IState> {
|
||||
return instance;
|
||||
})();
|
||||
|
||||
private monitoredUser: User;
|
||||
private monitoredUser: User | null;
|
||||
|
||||
private constructor() {
|
||||
// seed from localstorage because otherwise we won't get these values until a whole network
|
||||
@ -62,7 +62,7 @@ export class OwnProfileStore extends AsyncStoreWithClient<IState> {
|
||||
/**
|
||||
* Gets the display name for the user, or null if not present.
|
||||
*/
|
||||
public get displayName(): string {
|
||||
public get displayName(): string | null {
|
||||
if (!this.matrixClient) return this.state.displayName || null;
|
||||
|
||||
if (this.matrixClient.isGuest()) {
|
||||
@ -81,7 +81,7 @@ export class OwnProfileStore extends AsyncStoreWithClient<IState> {
|
||||
/**
|
||||
* Gets the MXC URI of the user's avatar, or null if not present.
|
||||
*/
|
||||
public get avatarMxc(): string {
|
||||
public get avatarMxc(): string | null {
|
||||
return this.state.avatarUrl || null;
|
||||
}
|
||||
|
||||
@ -92,7 +92,7 @@ export class OwnProfileStore extends AsyncStoreWithClient<IState> {
|
||||
* will be returned as an HTTP URL.
|
||||
* @returns The HTTP URL of the user's avatar
|
||||
*/
|
||||
public getHttpAvatarUrl(size = 0): string {
|
||||
public getHttpAvatarUrl(size = 0): string | null {
|
||||
if (!this.avatarMxc) return null;
|
||||
const media = mediaFromMxc(this.avatarMxc);
|
||||
if (!size || size <= 0) {
|
||||
@ -112,7 +112,7 @@ export class OwnProfileStore extends AsyncStoreWithClient<IState> {
|
||||
}
|
||||
|
||||
protected async onReady(): Promise<void> {
|
||||
const myUserId = this.matrixClient.getUserId();
|
||||
const myUserId = this.matrixClient.getUserId()!;
|
||||
this.monitoredUser = this.matrixClient.getUser(myUserId);
|
||||
if (this.monitoredUser) {
|
||||
this.monitoredUser.on(UserEvent.DisplayName, this.onProfileUpdate);
|
||||
@ -134,7 +134,7 @@ export class OwnProfileStore extends AsyncStoreWithClient<IState> {
|
||||
async (): Promise<void> => {
|
||||
// We specifically do not use the User object we stored for profile info as it
|
||||
// could easily be wrong (such as per-room instead of global profile).
|
||||
const profileInfo = await this.matrixClient.getProfileInfo(this.matrixClient.getUserId());
|
||||
const profileInfo = await this.matrixClient.getProfileInfo(this.matrixClient.getUserId()!);
|
||||
if (profileInfo.displayname) {
|
||||
window.localStorage.setItem(KEY_DISPLAY_NAME, profileInfo.displayname);
|
||||
} else {
|
||||
|
@ -44,10 +44,12 @@ export enum Phase {
|
||||
export class SetupEncryptionStore extends EventEmitter {
|
||||
private started: boolean;
|
||||
public phase: Phase;
|
||||
public verificationRequest: VerificationRequest;
|
||||
public backupInfo: IKeyBackupInfo;
|
||||
public keyId: string;
|
||||
public keyInfo: ISecretStorageKeyInfo;
|
||||
public verificationRequest: VerificationRequest | null = null;
|
||||
public backupInfo: IKeyBackupInfo | null = null;
|
||||
// ID of the key that the secrets we want are encrypted with
|
||||
public keyId: string | null = null;
|
||||
// Descriptor of the key that the secrets we want are encrypted with
|
||||
public keyInfo: ISecretStorageKeyInfo | null = null;
|
||||
public hasDevicesToVerifyAgainst: boolean;
|
||||
|
||||
public static sharedInstance(): SetupEncryptionStore {
|
||||
@ -61,19 +63,12 @@ export class SetupEncryptionStore extends EventEmitter {
|
||||
}
|
||||
this.started = true;
|
||||
this.phase = Phase.Loading;
|
||||
this.verificationRequest = null;
|
||||
this.backupInfo = null;
|
||||
|
||||
// ID of the key that the secrets we want are encrypted with
|
||||
this.keyId = null;
|
||||
// Descriptor of the key that the secrets we want are encrypted with
|
||||
this.keyInfo = null;
|
||||
|
||||
const cli = MatrixClientPeg.get();
|
||||
cli.on(CryptoEvent.VerificationRequest, this.onVerificationRequest);
|
||||
cli.on(CryptoEvent.UserTrustStatusChanged, this.onUserTrustStatusChanged);
|
||||
|
||||
const requestsInProgress = cli.getVerificationRequestsToDeviceInProgress(cli.getUserId());
|
||||
const requestsInProgress = cli.getVerificationRequestsToDeviceInProgress(cli.getUserId()!);
|
||||
if (requestsInProgress.length) {
|
||||
// If there are multiple, we take the most recent. Equally if the user sends another request from
|
||||
// another device after this screen has been shown, we'll switch to the new one, so this
|
||||
@ -111,7 +106,7 @@ export class SetupEncryptionStore extends EventEmitter {
|
||||
|
||||
// do we have any other verified devices which are E2EE which we can verify against?
|
||||
const dehydratedDevice = await cli.getDehydratedDevice();
|
||||
const ownUserId = cli.getUserId();
|
||||
const ownUserId = cli.getUserId()!;
|
||||
const crossSigningInfo = cli.getStoredCrossSigningForUser(ownUserId);
|
||||
this.hasDevicesToVerifyAgainst = cli
|
||||
.getStoredDevicesForUser(ownUserId)
|
||||
@ -119,7 +114,7 @@ export class SetupEncryptionStore extends EventEmitter {
|
||||
(device) =>
|
||||
device.getIdentityKey() &&
|
||||
(!dehydratedDevice || device.deviceId != dehydratedDevice.device_id) &&
|
||||
crossSigningInfo.checkDeviceTrust(crossSigningInfo, device, false, true).isCrossSigningVerified(),
|
||||
crossSigningInfo?.checkDeviceTrust(crossSigningInfo, device, false, true).isCrossSigningVerified(),
|
||||
);
|
||||
|
||||
this.phase = Phase.Intro;
|
||||
@ -183,11 +178,11 @@ export class SetupEncryptionStore extends EventEmitter {
|
||||
};
|
||||
|
||||
public onVerificationRequestChange = (): void => {
|
||||
if (this.verificationRequest.cancelled) {
|
||||
if (this.verificationRequest?.cancelled) {
|
||||
this.verificationRequest.off(VerificationRequestEvent.Change, this.onVerificationRequestChange);
|
||||
this.verificationRequest = null;
|
||||
this.emit("update");
|
||||
} else if (this.verificationRequest.phase === VERIF_PHASE_DONE) {
|
||||
} else if (this.verificationRequest?.phase === VERIF_PHASE_DONE) {
|
||||
this.verificationRequest.off(VerificationRequestEvent.Change, this.onVerificationRequestChange);
|
||||
this.verificationRequest = null;
|
||||
// At this point, the verification has finished, we just need to wait for
|
||||
@ -259,7 +254,7 @@ export class SetupEncryptionStore extends EventEmitter {
|
||||
this.phase = Phase.Finished;
|
||||
this.emit("update");
|
||||
// async - ask other clients for keys, if necessary
|
||||
MatrixClientPeg.get().crypto.cancelAndResendAllOutgoingKeyRequests();
|
||||
MatrixClientPeg.get().crypto?.cancelAndResendAllOutgoingKeyRequests();
|
||||
}
|
||||
|
||||
private async setActiveVerificationRequest(request: VerificationRequest): Promise<void> {
|
||||
|
@ -104,9 +104,9 @@ export default class WidgetStore extends AsyncStoreWithClient<IState> {
|
||||
private generateApps(room: Room): IApp[] {
|
||||
return WidgetEchoStore.getEchoedRoomWidgets(room.roomId, WidgetUtils.getRoomWidgets(room)).map((ev) => {
|
||||
return WidgetUtils.makeAppConfig(
|
||||
ev.getStateKey(),
|
||||
ev.getStateKey()!,
|
||||
ev.getContent(),
|
||||
ev.getSender(),
|
||||
ev.getSender()!,
|
||||
ev.getRoomId(),
|
||||
ev.getId(),
|
||||
);
|
||||
@ -172,7 +172,7 @@ export default class WidgetStore extends AsyncStoreWithClient<IState> {
|
||||
|
||||
private onRoomStateEvents = (ev: MatrixEvent): void => {
|
||||
if (ev.getType() !== "im.vector.modular.widgets") return; // TODO: Support m.widget too
|
||||
const roomId = ev.getRoomId();
|
||||
const roomId = ev.getRoomId()!;
|
||||
this.initRoom(roomId);
|
||||
this.loadRoomWidgets(this.matrixClient.getRoom(roomId));
|
||||
this.emit(UPDATE_EVENT, roomId);
|
||||
|
@ -37,7 +37,7 @@ export abstract class EchoContext extends Whenable<ContextTransactionState> impl
|
||||
return this._state;
|
||||
}
|
||||
|
||||
public get firstFailedTime(): Date {
|
||||
public get firstFailedTime(): Date | null {
|
||||
const failedTxn = this.transactions.find((t) => t.didPreviouslyFail || t.status === TransactionStatus.Error);
|
||||
if (failedTxn) return failedTxn.startTime;
|
||||
return null;
|
||||
|
@ -28,19 +28,19 @@ export const PROPERTY_UPDATED = "property_updated";
|
||||
|
||||
export abstract class GenericEchoChamber<C extends EchoContext, K, V> extends EventEmitter {
|
||||
private cache = new Map<K, { txn: EchoTransaction; val: V }>();
|
||||
protected matrixClient: MatrixClient;
|
||||
protected matrixClient: MatrixClient | null;
|
||||
|
||||
protected constructor(public readonly context: C, private lookupFn: (key: K) => V) {
|
||||
super();
|
||||
}
|
||||
|
||||
public setClient(client: MatrixClient): void {
|
||||
public setClient(client: MatrixClient | null): void {
|
||||
const oldClient = this.matrixClient;
|
||||
this.matrixClient = client;
|
||||
this.onClientChanged(oldClient, client);
|
||||
}
|
||||
|
||||
protected abstract onClientChanged(oldClient: MatrixClient, newClient: MatrixClient): void;
|
||||
protected abstract onClientChanged(oldClient: MatrixClient | null, newClient: MatrixClient | null): void;
|
||||
|
||||
/**
|
||||
* Gets a value. If the key is in flight, the cached value will be returned. If
|
||||
@ -50,7 +50,7 @@ export abstract class GenericEchoChamber<C extends EchoContext, K, V> extends Ev
|
||||
* @returns The value for the key.
|
||||
*/
|
||||
public getValue(key: K): V {
|
||||
return this.cache.has(key) ? this.cache.get(key).val : this.lookupFn(key);
|
||||
return this.cache.has(key) ? this.cache.get(key)!.val : this.lookupFn(key);
|
||||
}
|
||||
|
||||
private cacheVal(key: K, val: V, txn: EchoTransaction): void {
|
||||
@ -60,7 +60,7 @@ export abstract class GenericEchoChamber<C extends EchoContext, K, V> extends Ev
|
||||
|
||||
private decacheKey(key: K): void {
|
||||
if (this.cache.has(key)) {
|
||||
this.context.disownTransaction(this.cache.get(key).txn);
|
||||
this.context.disownTransaction(this.cache.get(key)!.txn);
|
||||
this.cache.delete(key);
|
||||
this.emit(PROPERTY_UPDATED, key);
|
||||
}
|
||||
@ -68,7 +68,7 @@ export abstract class GenericEchoChamber<C extends EchoContext, K, V> extends Ev
|
||||
|
||||
protected markEchoReceived(key: K): void {
|
||||
if (this.cache.has(key)) {
|
||||
const txn = this.cache.get(key).txn;
|
||||
const txn = this.cache.get(key)!.txn;
|
||||
this.context.disownTransaction(txn);
|
||||
txn.cancel();
|
||||
}
|
||||
@ -78,7 +78,7 @@ export abstract class GenericEchoChamber<C extends EchoContext, K, V> extends Ev
|
||||
public setValue(auditName: string, key: K, targetVal: V, runFn: RunFn, revertFn: RunFn): void {
|
||||
// Cancel any pending transactions for the same key
|
||||
if (this.cache.has(key)) {
|
||||
this.cache.get(key).txn.cancel();
|
||||
this.cache.get(key)!.txn.cancel();
|
||||
}
|
||||
|
||||
const ctxn = this.context.beginTransaction(auditName, runFn);
|
||||
|
@ -34,7 +34,7 @@ export class RoomEchoChamber extends GenericEchoChamber<RoomEchoContext, CachedR
|
||||
super(context, (k) => this.properties.get(k));
|
||||
}
|
||||
|
||||
protected onClientChanged(oldClient: MatrixClient, newClient: MatrixClient): void {
|
||||
protected onClientChanged(oldClient: MatrixClient | null, newClient: MatrixClient | null): void {
|
||||
this.properties.clear();
|
||||
oldClient?.removeListener(ClientEvent.AccountData, this.onAccountData);
|
||||
if (newClient) {
|
||||
@ -57,7 +57,7 @@ export class RoomEchoChamber extends GenericEchoChamber<RoomEchoContext, CachedR
|
||||
};
|
||||
|
||||
private updateNotificationVolume(): void {
|
||||
const state = getRoomNotifsState(this.matrixClient, this.context.room.roomId);
|
||||
const state = this.matrixClient ? getRoomNotifsState(this.matrixClient, this.context.room.roomId) : null;
|
||||
if (state) this.properties.set(CachedRoomKey.NotificationVolume, state);
|
||||
else this.properties.delete(CachedRoomKey.NotificationVolume);
|
||||
this.markEchoReceived(CachedRoomKey.NotificationVolume);
|
||||
|
@ -62,7 +62,7 @@ export class RoomNotificationStateStore extends AsyncStoreWithClient<IState> {
|
||||
*/
|
||||
public getListState(tagId: TagID): ListNotificationState {
|
||||
if (this.listMap.has(tagId)) {
|
||||
return this.listMap.get(tagId);
|
||||
return this.listMap.get(tagId)!;
|
||||
}
|
||||
|
||||
// TODO: Update if/when invites move out of the room list.
|
||||
@ -86,14 +86,14 @@ export class RoomNotificationStateStore extends AsyncStoreWithClient<IState> {
|
||||
if (!this.roomMap.has(room)) {
|
||||
this.roomMap.set(room, new RoomNotificationState(room));
|
||||
}
|
||||
return this.roomMap.get(room);
|
||||
return this.roomMap.get(room)!;
|
||||
}
|
||||
|
||||
public static get instance(): RoomNotificationStateStore {
|
||||
return RoomNotificationStateStore.internalInstance;
|
||||
}
|
||||
|
||||
private onSync = (state: SyncState, prevState?: SyncState, data?: ISyncStateData): void => {
|
||||
private onSync = (state: SyncState, prevState: SyncState | null, data?: ISyncStateData): void => {
|
||||
// Only count visible rooms to not torment the user with notification counts in rooms they can't see.
|
||||
// This will include highlights from the previous version of the room internally
|
||||
const globalState = new SummarizedNotificationState();
|
||||
|
@ -58,20 +58,20 @@ export default class RightPanelStore extends ReadyWatchingStore {
|
||||
* Resets the store. Intended for test usage only.
|
||||
*/
|
||||
public reset(): void {
|
||||
this.global = null;
|
||||
this.global = undefined;
|
||||
this.byRoom = {};
|
||||
this.viewedRoomId = null;
|
||||
}
|
||||
|
||||
protected async onReady(): Promise<any> {
|
||||
this.viewedRoomId = SdkContextClass.instance.roomViewStore.getRoomId();
|
||||
this.matrixClient.on(CryptoEvent.VerificationRequest, this.onVerificationRequestUpdate);
|
||||
this.matrixClient?.on(CryptoEvent.VerificationRequest, this.onVerificationRequestUpdate);
|
||||
this.loadCacheFromSettings();
|
||||
this.emitAndUpdateSettings();
|
||||
}
|
||||
|
||||
protected async onNotReady(): Promise<any> {
|
||||
this.matrixClient.off(CryptoEvent.VerificationRequest, this.onVerificationRequestUpdate);
|
||||
this.matrixClient?.off(CryptoEvent.VerificationRequest, this.onVerificationRequestUpdate);
|
||||
}
|
||||
|
||||
protected onDispatcherAction(payload: ActionPayload): void {
|
||||
@ -376,7 +376,7 @@ export default class RightPanelStore extends ReadyWatchingStore {
|
||||
// the room member list.
|
||||
if (SettingsStore.getValue("feature_right_panel_default_open") && !this.byRoom[this.viewedRoomId]?.isOpen) {
|
||||
const history = [{ phase: RightPanelPhases.RoomMemberList }];
|
||||
const room = this.viewedRoomId && this.mxClient?.getRoom(this.viewedRoomId);
|
||||
const room = this.viewedRoomId ? this.mxClient?.getRoom(this.viewedRoomId) : undefined;
|
||||
if (!room?.isSpaceRoom()) {
|
||||
history.unshift({ phase: RightPanelPhases.RoomSummary });
|
||||
}
|
||||
|
@ -123,7 +123,7 @@ export class MessagePreviewStore extends AsyncStoreWithClient<IState> {
|
||||
* @param inTagId The tag ID in which the room resides
|
||||
* @returns The preview, or null if none present.
|
||||
*/
|
||||
public async getPreviewForRoom(room: Room, inTagId: TagID): Promise<string> {
|
||||
public async getPreviewForRoom(room: Room, inTagId: TagID): Promise<string | null> {
|
||||
if (!room) return null; // invalid room, just return nothing
|
||||
|
||||
if (!this.previews.has(room.roomId)) await this.generatePreview(room, inTagId);
|
||||
@ -132,14 +132,14 @@ export class MessagePreviewStore extends AsyncStoreWithClient<IState> {
|
||||
if (!previews) return null;
|
||||
|
||||
if (!previews.has(inTagId)) {
|
||||
return previews.get(TAG_ANY);
|
||||
return previews.get(TAG_ANY)!;
|
||||
}
|
||||
return previews.get(inTagId);
|
||||
return previews.get(inTagId) ?? null;
|
||||
}
|
||||
|
||||
public generatePreviewForEvent(event: MatrixEvent): string {
|
||||
const previewDef = PREVIEWS[event.getType()];
|
||||
return previewDef?.previewer.getTextFor(event, null, true) ?? "";
|
||||
return previewDef?.previewer.getTextFor(event, undefined, true) ?? "";
|
||||
}
|
||||
|
||||
private async generatePreview(room: Room, tagId?: TagID): Promise<void> {
|
||||
@ -171,7 +171,7 @@ export class MessagePreviewStore extends AsyncStoreWithClient<IState> {
|
||||
if (!previewDef) continue;
|
||||
if (previewDef.isState && isNullOrUndefined(event.getStateKey())) continue;
|
||||
|
||||
const anyPreview = previewDef.previewer.getTextFor(event, null);
|
||||
const anyPreview = previewDef.previewer.getTextFor(event);
|
||||
if (!anyPreview) continue; // not previewable for some reason
|
||||
|
||||
changed = changed || anyPreview !== map.get(TAG_ANY);
|
||||
@ -179,7 +179,7 @@ export class MessagePreviewStore extends AsyncStoreWithClient<IState> {
|
||||
|
||||
const tagsToGenerate = Array.from(map.keys()).filter((t) => t !== TAG_ANY); // we did the any tag above
|
||||
for (const genTagId of tagsToGenerate) {
|
||||
const realTagId: TagID = genTagId === TAG_ANY ? null : genTagId;
|
||||
const realTagId = genTagId === TAG_ANY ? undefined : genTagId;
|
||||
const preview = previewDef.previewer.getTextFor(event, realTagId);
|
||||
if (preview === anyPreview) {
|
||||
changed = changed || anyPreview !== map.get(genTagId);
|
||||
|
@ -116,7 +116,7 @@ export class Algorithm extends EventEmitter {
|
||||
* Awaitable version of the sticky room setter.
|
||||
* @param val The new room to sticky.
|
||||
*/
|
||||
public setStickyRoom(val: Room): void {
|
||||
public setStickyRoom(val: Room | null): void {
|
||||
try {
|
||||
this.updateStickyRoom(val);
|
||||
} catch (e) {
|
||||
|
@ -29,7 +29,7 @@ export class PollStartEventPreview implements IPreview {
|
||||
public static contextType = MatrixClientContext;
|
||||
public context!: React.ContextType<typeof MatrixClientContext>;
|
||||
|
||||
public getTextFor(event: MatrixEvent, tagId?: TagID, isThread?: boolean): string {
|
||||
public getTextFor(event: MatrixEvent, tagId?: TagID, isThread?: boolean): string | null {
|
||||
let eventContent = event.getContent();
|
||||
|
||||
if (event.isRelation("m.replace")) {
|
||||
@ -51,7 +51,7 @@ export class PollStartEventPreview implements IPreview {
|
||||
let question = poll.question.text.trim();
|
||||
question = sanitizeForTranslation(question);
|
||||
|
||||
if (isThread || isSelf(event) || !shouldPrefixMessagesIn(event.getRoomId(), tagId)) {
|
||||
if (isThread || isSelf(event) || !shouldPrefixMessagesIn(event.getRoomId()!, tagId)) {
|
||||
return question;
|
||||
} else {
|
||||
return _t("%(senderName)s: %(message)s", { senderName: getSenderName(event), message: question });
|
||||
|
@ -22,11 +22,11 @@ import { getSenderName, isSelf, shouldPrefixMessagesIn } from "./utils";
|
||||
import { _t } from "../../../languageHandler";
|
||||
|
||||
export class StickerEventPreview implements IPreview {
|
||||
public getTextFor(event: MatrixEvent, tagId?: TagID, isThread?: boolean): string {
|
||||
public getTextFor(event: MatrixEvent, tagId?: TagID, isThread?: boolean): string | null {
|
||||
const stickerName = event.getContent()["body"];
|
||||
if (!stickerName) return null;
|
||||
|
||||
if (isThread || isSelf(event) || !shouldPrefixMessagesIn(event.getRoomId(), tagId)) {
|
||||
if (isThread || isSelf(event) || !shouldPrefixMessagesIn(event.getRoomId()!, tagId)) {
|
||||
return stickerName;
|
||||
} else {
|
||||
return _t("%(senderName)s: %(stickerName)s", { senderName: getSenderName(event), stickerName });
|
||||
|
@ -78,7 +78,7 @@ const getSpaceContextKey = (space: SpaceKey): string => `mx_space_context_${spac
|
||||
|
||||
const partitionSpacesAndRooms = (arr: Room[]): [Room[], Room[]] => {
|
||||
// [spaces, rooms]
|
||||
return arr.reduce(
|
||||
return arr.reduce<[Room[], Room[]]>(
|
||||
(result, room: Room) => {
|
||||
result[room.isSpaceRoom() ? 0 : 1].push(room);
|
||||
return result;
|
||||
@ -165,7 +165,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
||||
return this.rootSpaces;
|
||||
}
|
||||
|
||||
public get activeSpace(): SpaceKey {
|
||||
public get activeSpace(): SpaceKey | undefined {
|
||||
return this._activeSpace;
|
||||
}
|
||||
|
||||
@ -228,7 +228,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
||||
public setActiveSpace(space: SpaceKey, contextSwitch = true): void {
|
||||
if (!space || !this.matrixClient || space === this.activeSpace) return;
|
||||
|
||||
let cliSpace: Room;
|
||||
let cliSpace: Room | null = null;
|
||||
if (!isMetaSpace(space)) {
|
||||
cliSpace = this.matrixClient.getRoom(space);
|
||||
if (!cliSpace?.isSpaceRoom()) return;
|
||||
@ -246,6 +246,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
||||
// else if the last viewed room in this space is joined then view that
|
||||
// else view space home or home depending on what is being clicked on
|
||||
if (
|
||||
roomId &&
|
||||
cliSpace?.getMyMembership() !== "invite" &&
|
||||
this.matrixClient.getRoom(roomId)?.getMyMembership() === "join" &&
|
||||
this.isRoomInSpace(space, roomId)
|
||||
@ -348,10 +349,10 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
||||
.filter((ev) => ev.getContent()?.via);
|
||||
return (
|
||||
sortBy(childEvents, (ev) => {
|
||||
return getChildOrder(ev.getContent().order, ev.getTs(), ev.getStateKey());
|
||||
return getChildOrder(ev.getContent().order, ev.getTs(), ev.getStateKey()!);
|
||||
})
|
||||
.map((ev) => {
|
||||
const history = this.matrixClient.getRoomUpgradeHistory(ev.getStateKey(), true);
|
||||
const history = this.matrixClient.getRoomUpgradeHistory(ev.getStateKey()!, true);
|
||||
return history[history.length - 1];
|
||||
})
|
||||
.filter((room) => {
|
||||
@ -373,7 +374,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
||||
const userId = this.matrixClient?.getUserId();
|
||||
const room = this.matrixClient?.getRoom(roomId);
|
||||
return (
|
||||
room?.currentState
|
||||
(room?.currentState
|
||||
.getStateEvents(EventType.SpaceParent)
|
||||
.map((ev) => {
|
||||
const content = ev.getContent();
|
||||
@ -396,7 +397,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
||||
|
||||
return parent;
|
||||
})
|
||||
.filter(Boolean) || []
|
||||
.filter(Boolean) as Room[]) || []
|
||||
);
|
||||
}
|
||||
|
||||
@ -467,7 +468,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
||||
space: SpaceKey,
|
||||
includeDescendantSpaces = true,
|
||||
useCache = true,
|
||||
): Set<string> => {
|
||||
): Set<string> | undefined => {
|
||||
if (space === MetaSpace.Home && this.allRoomsInHome) {
|
||||
return undefined;
|
||||
}
|
||||
@ -490,7 +491,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
||||
private markTreeChildren = (rootSpace: Room, unseen: Set<Room>): void => {
|
||||
const stack = [rootSpace];
|
||||
while (stack.length) {
|
||||
const space = stack.pop();
|
||||
const space = stack.pop()!;
|
||||
unseen.delete(space);
|
||||
this.getChildSpaces(space.roomId).forEach((space) => {
|
||||
if (unseen.has(space)) {
|
||||
@ -646,7 +647,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
||||
const enabledMetaSpaces = new Set(this.enabledMetaSpaces);
|
||||
const visibleRooms = this.matrixClient.getVisibleRooms();
|
||||
|
||||
let dmBadgeSpace: MetaSpace;
|
||||
let dmBadgeSpace: MetaSpace | undefined;
|
||||
// only show badges on dms on the most relevant space if such exists
|
||||
if (enabledMetaSpaces.has(MetaSpace.People)) {
|
||||
dmBadgeSpace = MetaSpace.People;
|
||||
@ -702,8 +703,8 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
||||
); // put all invites in the Home Space
|
||||
};
|
||||
|
||||
private static isInSpace(member: RoomMember): boolean {
|
||||
return member.membership === "join" || member.membership === "invite";
|
||||
private static isInSpace(member?: RoomMember | null): boolean {
|
||||
return member?.membership === "join" || member?.membership === "invite";
|
||||
}
|
||||
|
||||
// Method for resolving the impact of a single user's membership change in the given Space and its hierarchy
|
||||
@ -755,11 +756,14 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
||||
this.rootSpaces.forEach((s) => {
|
||||
// traverse each space tree in DFS to build up the supersets as you go up,
|
||||
// reusing results from like subtrees.
|
||||
const traverseSpace = (spaceId: string, parentPath: Set<string>): [Set<string>, Set<string>] => {
|
||||
const traverseSpace = (
|
||||
spaceId: string,
|
||||
parentPath: Set<string>,
|
||||
): [Set<string>, Set<string>] | undefined => {
|
||||
if (parentPath.has(spaceId)) return; // prevent cycles
|
||||
// reuse existing results if multiple similar branches exist
|
||||
if (this.roomIdsBySpace.has(spaceId) && this.userIdsBySpace.has(spaceId)) {
|
||||
return [this.roomIdsBySpace.get(spaceId), this.userIdsBySpace.get(spaceId)];
|
||||
return [this.roomIdsBySpace.get(spaceId)!, this.userIdsBySpace.get(spaceId)!];
|
||||
}
|
||||
|
||||
const [childSpaces, childRooms] = partitionSpacesAndRooms(this.getChildren(spaceId));
|
||||
@ -865,7 +869,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
||||
if (this.suggestedRooms.find((r) => r.room_id === roomId)) return;
|
||||
|
||||
// try to find the canonical parent first
|
||||
let parent: SpaceKey = this.getCanonicalParent(roomId)?.roomId;
|
||||
let parent: SpaceKey | undefined = this.getCanonicalParent(roomId)?.roomId;
|
||||
|
||||
// otherwise, try to find a root space which contains this room
|
||||
if (!parent) {
|
||||
|
@ -54,7 +54,7 @@ export interface ISuggestedRoom extends IHierarchyRoom {
|
||||
viaServers: string[];
|
||||
}
|
||||
|
||||
export function isMetaSpace(spaceKey: SpaceKey): boolean {
|
||||
export function isMetaSpace(spaceKey?: SpaceKey): boolean {
|
||||
return (
|
||||
spaceKey === MetaSpace.Home ||
|
||||
spaceKey === MetaSpace.Favourites ||
|
||||
|
@ -33,6 +33,7 @@ import {
|
||||
WidgetApiFromWidgetAction,
|
||||
WidgetKind,
|
||||
} from "matrix-widget-api";
|
||||
import { Optional } from "matrix-events-sdk";
|
||||
import { EventEmitter } from "events";
|
||||
import { MatrixClient } from "matrix-js-sdk/src/client";
|
||||
import { MatrixEvent, MatrixEventEvent } from "matrix-js-sdk/src/models/event";
|
||||
@ -156,7 +157,7 @@ export class ElementWidget extends Widget {
|
||||
|
||||
export class StopGapWidget extends EventEmitter {
|
||||
private client: MatrixClient;
|
||||
private messaging: ClientWidgetApi;
|
||||
private messaging: ClientWidgetApi | null;
|
||||
private mockWidget: ElementWidget;
|
||||
private scalarToken: string;
|
||||
private roomId?: string;
|
||||
@ -172,7 +173,7 @@ export class StopGapWidget extends EventEmitter {
|
||||
// Backwards compatibility: not all old widgets have a creatorUserId
|
||||
if (!app.creatorUserId) {
|
||||
app = objectShallowClone(app); // clone to prevent accidental mutation
|
||||
app.creatorUserId = this.client.getUserId();
|
||||
app.creatorUserId = this.client.getUserId()!;
|
||||
}
|
||||
|
||||
this.mockWidget = new ElementWidget(app);
|
||||
@ -181,7 +182,7 @@ export class StopGapWidget extends EventEmitter {
|
||||
this.virtual = app.eventId === undefined;
|
||||
}
|
||||
|
||||
private get eventListenerRoomId(): string {
|
||||
private get eventListenerRoomId(): Optional<string> {
|
||||
// When widgets are listening to events, we need to make sure they're only
|
||||
// receiving events for the right room. In particular, room widgets get locked
|
||||
// to the room they were added in while account widgets listen to the currently
|
||||
@ -192,7 +193,7 @@ export class StopGapWidget extends EventEmitter {
|
||||
return SdkContextClass.instance.roomViewStore.getRoomId();
|
||||
}
|
||||
|
||||
public get widgetApi(): ClientWidgetApi {
|
||||
public get widgetApi(): ClientWidgetApi | null {
|
||||
return this.messaging;
|
||||
}
|
||||
|
||||
@ -214,7 +215,7 @@ export class StopGapWidget extends EventEmitter {
|
||||
const fromCustomisation = WidgetVariableCustomisations?.provideVariables?.() ?? {};
|
||||
const defaults: ITemplateParams = {
|
||||
widgetRoomId: this.roomId,
|
||||
currentUserId: this.client.getUserId(),
|
||||
currentUserId: this.client.getUserId()!,
|
||||
userDisplayName: OwnProfileStore.instance.displayName,
|
||||
userHttpAvatarUrl: OwnProfileStore.instance.getHttpAvatarUrl(),
|
||||
clientId: ELEMENT_CLIENT_ID,
|
||||
@ -256,9 +257,9 @@ export class StopGapWidget extends EventEmitter {
|
||||
ev.preventDefault();
|
||||
if (ModalWidgetStore.instance.canOpenModalWidget()) {
|
||||
ModalWidgetStore.instance.openModalWidget(ev.detail.data, this.mockWidget, this.roomId);
|
||||
this.messaging.transport.reply(ev.detail, {}); // ack
|
||||
this.messaging?.transport.reply(ev.detail, {}); // ack
|
||||
} else {
|
||||
this.messaging.transport.reply(ev.detail, {
|
||||
this.messaging?.transport.reply(ev.detail, {
|
||||
error: {
|
||||
message: "Unable to open modal at this time",
|
||||
},
|
||||
@ -301,14 +302,14 @@ export class StopGapWidget extends EventEmitter {
|
||||
// Check up front if this is even a valid request
|
||||
const targetRoomId = (ev.detail.data || {}).room_id;
|
||||
if (!targetRoomId) {
|
||||
return this.messaging.transport.reply(ev.detail, <IWidgetApiErrorResponseData>{
|
||||
return this.messaging?.transport.reply(ev.detail, <IWidgetApiErrorResponseData>{
|
||||
error: { message: "Room ID not supplied." },
|
||||
});
|
||||
}
|
||||
|
||||
// Check the widget's permission
|
||||
if (!this.messaging.hasCapability(ElementWidgetCapabilities.CanChangeViewedRoom)) {
|
||||
return this.messaging.transport.reply(ev.detail, <IWidgetApiErrorResponseData>{
|
||||
if (!this.messaging?.hasCapability(ElementWidgetCapabilities.CanChangeViewedRoom)) {
|
||||
return this.messaging?.transport.reply(ev.detail, <IWidgetApiErrorResponseData>{
|
||||
error: { message: "This widget does not have permission for this action (denied)." },
|
||||
});
|
||||
}
|
||||
@ -332,7 +333,7 @@ export class StopGapWidget extends EventEmitter {
|
||||
const events = room.getLiveTimeline()?.getEvents() || [];
|
||||
const roomEvent = events[events.length - 1];
|
||||
if (!roomEvent) continue; // force later code to think the room is fresh
|
||||
this.readUpToMap[room.roomId] = roomEvent.getId();
|
||||
this.readUpToMap[room.roomId] = roomEvent.getId()!;
|
||||
}
|
||||
|
||||
// Attach listeners for feeding events - the underlying widget classes handle permissions for us
|
||||
@ -343,7 +344,7 @@ export class StopGapWidget extends EventEmitter {
|
||||
this.messaging.on(
|
||||
`action:${WidgetApiFromWidgetAction.UpdateAlwaysOnScreen}`,
|
||||
(ev: CustomEvent<IStickyActionRequest>) => {
|
||||
if (this.messaging.hasCapability(MatrixCapabilities.AlwaysOnScreen)) {
|
||||
if (this.messaging?.hasCapability(MatrixCapabilities.AlwaysOnScreen)) {
|
||||
ActiveWidgetStore.instance.setWidgetPersistence(
|
||||
this.mockWidget.id,
|
||||
this.roomId,
|
||||
@ -360,7 +361,7 @@ export class StopGapWidget extends EventEmitter {
|
||||
this.messaging.on(
|
||||
`action:${WidgetApiFromWidgetAction.SendSticker}`,
|
||||
(ev: CustomEvent<IStickerActionRequest>) => {
|
||||
if (this.messaging.hasCapability(MatrixCapabilities.StickerSending)) {
|
||||
if (this.messaging?.hasCapability(MatrixCapabilities.StickerSending)) {
|
||||
// Acknowledge first
|
||||
ev.preventDefault();
|
||||
this.messaging.transport.reply(ev.detail, <IWidgetApiRequestEmptyData>{});
|
||||
@ -381,7 +382,7 @@ export class StopGapWidget extends EventEmitter {
|
||||
(ev: CustomEvent<IWidgetApiRequest>) => {
|
||||
// Acknowledge first
|
||||
ev.preventDefault();
|
||||
this.messaging.transport.reply(ev.detail, <IWidgetApiRequestEmptyData>{});
|
||||
this.messaging?.transport.reply(ev.detail, <IWidgetApiRequestEmptyData>{});
|
||||
|
||||
// First close the stickerpicker
|
||||
defaultDispatcher.dispatch({ action: "stickerpicker_close" });
|
||||
@ -415,7 +416,7 @@ export class StopGapWidget extends EventEmitter {
|
||||
}),
|
||||
});
|
||||
}
|
||||
this.messaging.transport.reply(ev.detail, <IWidgetApiRequestEmptyData>{});
|
||||
this.messaging?.transport.reply(ev.detail, <IWidgetApiRequestEmptyData>{});
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -478,7 +479,7 @@ export class StopGapWidget extends EventEmitter {
|
||||
private onToDeviceEvent = async (ev: MatrixEvent): Promise<void> => {
|
||||
await this.client.decryptEventIfNeeded(ev);
|
||||
if (ev.isDecryptionFailure()) return;
|
||||
await this.messaging.feedToDevice(ev.getEffectiveEvent() as IRoomEvent, ev.isEncrypted());
|
||||
await this.messaging?.feedToDevice(ev.getEffectiveEvent() as IRoomEvent, ev.isEncrypted());
|
||||
};
|
||||
|
||||
private feedEvent(ev: MatrixEvent): void {
|
||||
@ -490,7 +491,7 @@ export class StopGapWidget extends EventEmitter {
|
||||
//
|
||||
// This approach of "read up to" prevents widgets receiving decryption spam from startup or
|
||||
// receiving out-of-order events from backfill and such.
|
||||
const upToEventId = this.readUpToMap[ev.getRoomId()];
|
||||
const upToEventId = this.readUpToMap[ev.getRoomId()!];
|
||||
if (upToEventId) {
|
||||
// Small optimization for exact match (prevent search)
|
||||
if (upToEventId === ev.getId()) {
|
||||
@ -501,7 +502,7 @@ export class StopGapWidget extends EventEmitter {
|
||||
|
||||
// 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 = this.client.getRoom(ev.getRoomId()!).getLiveTimeline();
|
||||
const events = arrayFastClone(timeline.getEvents()).reverse().slice(0, 100);
|
||||
|
||||
for (const timelineEvent of events) {
|
||||
|
@ -131,7 +131,7 @@ export class WidgetLayoutStore extends ReadyWatchingStore {
|
||||
protected async onReady(): Promise<void> {
|
||||
this.updateAllRooms();
|
||||
|
||||
this.matrixClient.on(RoomStateEvent.Events, this.updateRoomFromState);
|
||||
this.matrixClient?.on(RoomStateEvent.Events, this.updateRoomFromState);
|
||||
this.pinnedRef = SettingsStore.watchSetting("Widgets.pinned", null, this.updateFromSettings);
|
||||
this.layoutRef = SettingsStore.watchSetting("Widgets.layout", null, this.updateFromSettings);
|
||||
WidgetStore.instance.on(UPDATE_EVENT, this.updateFromWidgetStore);
|
||||
@ -155,7 +155,7 @@ export class WidgetLayoutStore extends ReadyWatchingStore {
|
||||
|
||||
private updateFromWidgetStore = (roomId?: string): void => {
|
||||
if (roomId) {
|
||||
const room = this.matrixClient.getRoom(roomId);
|
||||
const room = this.matrixClient?.getRoom(roomId);
|
||||
if (room) this.recalculateRoom(room);
|
||||
} else {
|
||||
this.updateAllRooms();
|
||||
@ -164,13 +164,13 @@ export class WidgetLayoutStore extends ReadyWatchingStore {
|
||||
|
||||
private updateRoomFromState = (ev: MatrixEvent): void => {
|
||||
if (ev.getType() !== WIDGET_LAYOUT_EVENT_TYPE) return;
|
||||
const room = this.matrixClient.getRoom(ev.getRoomId());
|
||||
const room = this.matrixClient?.getRoom(ev.getRoomId());
|
||||
if (room) this.recalculateRoom(room);
|
||||
};
|
||||
|
||||
private updateFromSettings = (settingName: string, roomId: string /* and other stuff */): void => {
|
||||
if (roomId) {
|
||||
const room = this.matrixClient.getRoom(roomId);
|
||||
const room = this.matrixClient?.getRoom(roomId);
|
||||
if (room) this.recalculateRoom(room);
|
||||
} else {
|
||||
this.updateAllRooms();
|
||||
@ -189,7 +189,7 @@ export class WidgetLayoutStore extends ReadyWatchingStore {
|
||||
|
||||
const layoutEv = room.currentState.getStateEvents(WIDGET_LAYOUT_EVENT_TYPE, "");
|
||||
const legacyPinned = SettingsStore.getValue("Widgets.pinned", room.roomId);
|
||||
let userLayout = SettingsStore.getValue<ILayoutSettings>("Widgets.layout", room.roomId);
|
||||
let userLayout = SettingsStore.getValue<ILayoutSettings | null>("Widgets.layout", room.roomId);
|
||||
|
||||
if (layoutEv && userLayout && userLayout.overrides !== layoutEv.getId()) {
|
||||
// For some other layout that we don't really care about. The user can reset this
|
||||
@ -197,7 +197,7 @@ export class WidgetLayoutStore extends ReadyWatchingStore {
|
||||
userLayout = null;
|
||||
}
|
||||
|
||||
const roomLayout: ILayoutStateEvent = layoutEv ? layoutEv.getContent() : null;
|
||||
const roomLayout = layoutEv?.getContent<ILayoutStateEvent>() ?? null;
|
||||
// We filter for the center container first.
|
||||
// (An error is raised, if there are multiple widgets marked for the center container)
|
||||
// For the right and top container multiple widgets are allowed.
|
||||
@ -218,9 +218,9 @@ export class WidgetLayoutStore extends ReadyWatchingStore {
|
||||
// The widget won't need to be put in any other container.
|
||||
continue;
|
||||
}
|
||||
let targetContainer = defaultContainer;
|
||||
let targetContainer: Container = defaultContainer;
|
||||
if (!!manualContainer || !!stateContainer) {
|
||||
targetContainer = manualContainer ? manualContainer : stateContainer;
|
||||
targetContainer = manualContainer ?? stateContainer!;
|
||||
} else if (isLegacyPinned && !stateContainer) {
|
||||
// Special legacy case
|
||||
targetContainer = Container.Top;
|
||||
@ -259,7 +259,7 @@ export class WidgetLayoutStore extends ReadyWatchingStore {
|
||||
|
||||
// Determine width distribution and height of the top container now (the only relevant one)
|
||||
const widths: number[] = [];
|
||||
let maxHeight = null; // null == default
|
||||
let maxHeight: number | null = null; // null == default
|
||||
let doAutobalance = true;
|
||||
for (let i = 0; i < topWidgets.length; i++) {
|
||||
const widget = topWidgets[i];
|
||||
@ -487,7 +487,7 @@ export class WidgetLayoutStore extends ReadyWatchingStore {
|
||||
|
||||
public canCopyLayoutToRoom(room: Room): boolean {
|
||||
if (!this.matrixClient) return false; // not ready yet
|
||||
return room.currentState.maySendStateEvent(WIDGET_LAYOUT_EVENT_TYPE, this.matrixClient.getUserId());
|
||||
return room.currentState.maySendStateEvent(WIDGET_LAYOUT_EVENT_TYPE, this.matrixClient.getUserId()!);
|
||||
}
|
||||
|
||||
public copyLayoutToRoom(room: Room): void {
|
||||
@ -508,7 +508,7 @@ export class WidgetLayoutStore extends ReadyWatchingStore {
|
||||
};
|
||||
}
|
||||
}
|
||||
this.matrixClient.sendStateEvent(room.roomId, WIDGET_LAYOUT_EVENT_TYPE, evContent, "");
|
||||
this.matrixClient?.sendStateEvent(room.roomId, WIDGET_LAYOUT_EVENT_TYPE, evContent, "");
|
||||
}
|
||||
|
||||
private getAllWidgets(room: Room): [IApp, Container][] {
|
||||
@ -516,7 +516,7 @@ export class WidgetLayoutStore extends ReadyWatchingStore {
|
||||
if (!containers) return [];
|
||||
|
||||
const ret: [IApp, Container][] = [];
|
||||
for (const container of Object.keys(containers)) {
|
||||
for (const container in containers) {
|
||||
const widgets = containers[container as Container].ordered;
|
||||
for (const widget of widgets) {
|
||||
ret.push([widget, container as Container]);
|
||||
|
@ -32,7 +32,7 @@ export class WidgetPermissionStore {
|
||||
// TODO (all functions here): Merge widgetKind with the widget definition
|
||||
|
||||
private packSettingKey(widget: Widget, kind: WidgetKind, roomId?: string): string {
|
||||
let location = roomId;
|
||||
let location: string | null | undefined = roomId;
|
||||
if (kind !== WidgetKind.Room) {
|
||||
location = this.context.client?.getUserId();
|
||||
}
|
||||
|
@ -133,7 +133,7 @@ function generateCustomFontFaceCSS(faces: IFontFaces[]): string {
|
||||
.map((face) => {
|
||||
const src = face.src
|
||||
?.map((srcElement) => {
|
||||
let format: string;
|
||||
let format = "";
|
||||
if (srcElement.format) {
|
||||
format = `format("${srcElement.format}")`;
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ interface Props {
|
||||
|
||||
export function IncomingCallToast({ callEvent }: Props): JSX.Element {
|
||||
const roomId = callEvent.getRoomId()!;
|
||||
const room = MatrixClientPeg.get().getRoom(roomId);
|
||||
const room = MatrixClientPeg.get().getRoom(roomId) ?? undefined;
|
||||
const call = useCall(roomId);
|
||||
|
||||
const dismissToast = useCallback((): void => {
|
||||
@ -107,7 +107,7 @@ export function IncomingCallToast({ callEvent }: Props): JSX.Element {
|
||||
|
||||
defaultDispatcher.dispatch<ViewRoomPayload>({
|
||||
action: Action.ViewRoom,
|
||||
room_id: room.roomId,
|
||||
room_id: room?.roomId,
|
||||
view_call: true,
|
||||
metricsTrigger: undefined,
|
||||
});
|
||||
|
@ -35,7 +35,7 @@ import { _t, _td, Tags, TranslatedString } from "../languageHandler";
|
||||
*/
|
||||
export function messageForResourceLimitError(
|
||||
limitType: string,
|
||||
adminContact: string,
|
||||
adminContact: string | undefined,
|
||||
strings: Record<string, string>,
|
||||
extraTranslations?: Tags,
|
||||
): TranslatedString {
|
||||
@ -57,7 +57,7 @@ export function messageForResourceLimitError(
|
||||
if (errString.includes("<a>")) {
|
||||
return _t(errString, {}, Object.assign({ a: linkSub }, extraTranslations));
|
||||
} else {
|
||||
return _t(errString, {}, extraTranslations);
|
||||
return _t(errString, {}, extraTranslations!);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@ export function presentableTextForFile(
|
||||
shortened = false,
|
||||
): string {
|
||||
let text = fallbackText;
|
||||
if (content.body?.length > 0) {
|
||||
if (content.body?.length) {
|
||||
// The content body should be the name of the file including a
|
||||
// file extension.
|
||||
text = content.body;
|
||||
|
@ -69,7 +69,7 @@ async function isColrFontSupported(): Promise<boolean> {
|
||||
|
||||
try {
|
||||
const canvas = document.createElement("canvas");
|
||||
const context = canvas.getContext("2d");
|
||||
const context = canvas.getContext("2d")!;
|
||||
const img = new Image();
|
||||
// eslint-disable-next-line
|
||||
const fontCOLR =
|
||||
|
@ -26,8 +26,8 @@ import { useEventEmitterState } from "../../hooks/useEventEmitter";
|
||||
export const useLiveBeacons = (roomId: Room["roomId"], matrixClient: MatrixClient): Beacon[] => {
|
||||
const room = matrixClient.getRoom(roomId);
|
||||
|
||||
const liveBeacons = useEventEmitterState(room.currentState, RoomStateEvent.BeaconLiveness, () =>
|
||||
room.currentState?.liveBeaconIds.map((beaconIdentifier) => room.currentState.beacons.get(beaconIdentifier)),
|
||||
const liveBeacons = useEventEmitterState(room?.currentState, RoomStateEvent.BeaconLiveness, () =>
|
||||
room?.currentState?.liveBeaconIds.map((beaconIdentifier) => room.currentState.beacons.get(beaconIdentifier)),
|
||||
);
|
||||
|
||||
return liveBeacons;
|
||||
|
@ -102,7 +102,7 @@ export abstract class Member {
|
||||
* Gets the MXC URL of this Member's avatar. For users this should be their profile's
|
||||
* avatar MXC URL or null if none set. For 3PIDs this should always be null.
|
||||
*/
|
||||
public abstract getMxcAvatarUrl(): string;
|
||||
public abstract getMxcAvatarUrl(): string | null;
|
||||
}
|
||||
|
||||
export class DirectoryMember extends Member {
|
||||
@ -127,8 +127,8 @@ export class DirectoryMember extends Member {
|
||||
return this._userId;
|
||||
}
|
||||
|
||||
public getMxcAvatarUrl(): string {
|
||||
return this.avatarUrl;
|
||||
public getMxcAvatarUrl(): string | null {
|
||||
return this.avatarUrl ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -156,7 +156,7 @@ export class ThreepidMember extends Member {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public getMxcAvatarUrl(): string {
|
||||
public getMxcAvatarUrl(): string | null {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -276,14 +276,14 @@ export default abstract class Exporter {
|
||||
protected isReply(event: MatrixEvent): boolean {
|
||||
const isEncrypted = event.isEncrypted();
|
||||
// If encrypted, in_reply_to lies in event.event.content
|
||||
const content = isEncrypted ? event.event.content : event.getContent();
|
||||
const content = isEncrypted ? event.event.content! : event.getContent();
|
||||
const relatesTo = content["m.relates_to"];
|
||||
return !!(relatesTo && relatesTo["m.in_reply_to"]);
|
||||
}
|
||||
|
||||
protected isAttachment(mxEv: MatrixEvent): boolean {
|
||||
const attachmentTypes = ["m.sticker", "m.image", "m.file", "m.video", "m.audio"];
|
||||
return mxEv.getType() === attachmentTypes[0] || attachmentTypes.includes(mxEv.getContent().msgtype);
|
||||
return mxEv.getType() === attachmentTypes[0] || attachmentTypes.includes(mxEv.getContent().msgtype!);
|
||||
}
|
||||
|
||||
public abstract export(): Promise<void>;
|
||||
|
@ -217,10 +217,10 @@ export default class HTMLExporter extends Exporter {
|
||||
</html>`;
|
||||
}
|
||||
|
||||
protected getAvatarURL(event: MatrixEvent): string | undefined {
|
||||
protected getAvatarURL(event: MatrixEvent): string | null {
|
||||
const member = event.sender;
|
||||
const avatarUrl = member?.getMxcAvatarUrl();
|
||||
return avatarUrl ? mediaFromMxc(avatarUrl).getThumbnailOfSourceHttp(30, 30, "crop") : undefined;
|
||||
return avatarUrl ? mediaFromMxc(avatarUrl).getThumbnailOfSourceHttp(30, 30, "crop") : null;
|
||||
}
|
||||
|
||||
protected async saveAvatarIfNeeded(event: MatrixEvent): Promise<void> {
|
||||
@ -386,7 +386,7 @@ export default class HTMLExporter extends Exporter {
|
||||
|
||||
protected async createHTML(events: MatrixEvent[], start: number): Promise<string> {
|
||||
let content = "";
|
||||
let prevEvent = null;
|
||||
let prevEvent: MatrixEvent | null = null;
|
||||
for (let i = start; i < Math.min(start + 1000, events.length); i++) {
|
||||
const event = events[i];
|
||||
this.updateProgress(
|
||||
|
@ -47,7 +47,7 @@ export default class JSONExporter extends Exporter {
|
||||
const creator = this.room.currentState.getStateEvents(EventType.RoomCreate, "")?.getSender();
|
||||
const creatorName = this.room?.getMember(creator)?.rawDisplayName || creator;
|
||||
const topic = this.room.currentState.getStateEvents(EventType.RoomTopic, "")?.getContent()?.topic || "";
|
||||
const exporter = this.client.getUserId();
|
||||
const exporter = this.client.getUserId()!;
|
||||
const exporterName = this.room?.getMember(exporter)?.rawDisplayName || exporter;
|
||||
const jsonObject = {
|
||||
room_name: this.room.name,
|
||||
|
@ -45,7 +45,7 @@ async function getRulesFromCssFile(path: string): Promise<CSSStyleSheet> {
|
||||
// the style will only be parsed once it is added to a document
|
||||
doc.body.appendChild(styleElement);
|
||||
|
||||
return styleElement.sheet;
|
||||
return styleElement.sheet!;
|
||||
}
|
||||
|
||||
// naively culls unused css rules based on which classes are present in the html,
|
||||
|
@ -19,6 +19,8 @@ import { IEncryptedFile } from "../customisations/models/IMediaEventContent";
|
||||
|
||||
type ThumbnailableElement = HTMLImageElement | HTMLVideoElement;
|
||||
|
||||
export const BLURHASH_FIELD = "xyz.amorgan.blurhash"; // MSC2448
|
||||
|
||||
interface IThumbnail {
|
||||
info: {
|
||||
thumbnail_info?: {
|
||||
@ -29,15 +31,13 @@ interface IThumbnail {
|
||||
};
|
||||
w: number;
|
||||
h: number;
|
||||
[BLURHASH_FIELD]: string;
|
||||
[BLURHASH_FIELD]?: string;
|
||||
thumbnail_url?: string;
|
||||
thumbnail_file?: IEncryptedFile;
|
||||
};
|
||||
thumbnail: Blob;
|
||||
}
|
||||
|
||||
export const BLURHASH_FIELD = "xyz.amorgan.blurhash"; // MSC2448
|
||||
|
||||
const MAX_WIDTH = 800;
|
||||
const MAX_HEIGHT = 600;
|
||||
|
||||
@ -88,7 +88,7 @@ export async function createThumbnail(
|
||||
canvas = document.createElement("canvas");
|
||||
canvas.width = targetWidth;
|
||||
canvas.height = targetHeight;
|
||||
context = canvas.getContext("2d");
|
||||
context = canvas.getContext("2d")!;
|
||||
}
|
||||
|
||||
context.drawImage(element, 0, 0, targetWidth, targetHeight);
|
||||
@ -97,7 +97,9 @@ export async function createThumbnail(
|
||||
if (window.OffscreenCanvas && canvas instanceof OffscreenCanvas) {
|
||||
thumbnailPromise = canvas.convertToBlob({ type: mimeType });
|
||||
} else {
|
||||
thumbnailPromise = new Promise<Blob>((resolve) => (canvas as HTMLCanvasElement).toBlob(resolve, mimeType));
|
||||
thumbnailPromise = new Promise<Blob>((resolve) =>
|
||||
(canvas as HTMLCanvasElement).toBlob(resolve as BlobCallback, mimeType),
|
||||
);
|
||||
}
|
||||
|
||||
const imageData = context.getImageData(0, 0, targetWidth, targetHeight);
|
||||
|
@ -345,7 +345,7 @@ describe("Notifier", () => {
|
||||
tweaks: {},
|
||||
});
|
||||
Notifier.start();
|
||||
Notifier.onSyncStateChange(SyncState.Syncing);
|
||||
Notifier.onSyncStateChange(SyncState.Syncing, null);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
@ -33,21 +33,23 @@ describe("SlashCommands", () => {
|
||||
let localRoom: LocalRoom;
|
||||
let command: Command;
|
||||
|
||||
const findCommand = (cmd: string): Command => {
|
||||
const findCommand = (cmd: string): Command | undefined => {
|
||||
return Commands.find((command: Command) => command.command === cmd);
|
||||
};
|
||||
|
||||
const setCurrentRoom = (): void => {
|
||||
mocked(SdkContextClass.instance.roomViewStore.getRoomId).mockReturnValue(roomId);
|
||||
mocked(client.getRoom).mockImplementation((rId: string): Room => {
|
||||
mocked(client.getRoom).mockImplementation((rId: string): Room | null => {
|
||||
if (rId === roomId) return room;
|
||||
return null;
|
||||
});
|
||||
};
|
||||
|
||||
const setCurrentLocalRoon = (): void => {
|
||||
mocked(SdkContextClass.instance.roomViewStore.getRoomId).mockReturnValue(localRoomId);
|
||||
mocked(client.getRoom).mockImplementation((rId: string): Room => {
|
||||
mocked(client.getRoom).mockImplementation((rId: string): Room | null => {
|
||||
if (rId === localRoomId) return localRoom;
|
||||
return null;
|
||||
});
|
||||
};
|
||||
|
||||
@ -57,8 +59,8 @@ describe("SlashCommands", () => {
|
||||
client = createTestClient();
|
||||
jest.spyOn(MatrixClientPeg, "get").mockReturnValue(client);
|
||||
|
||||
room = new Room(roomId, client, client.getUserId());
|
||||
localRoom = new LocalRoom(localRoomId, client, client.getUserId());
|
||||
room = new Room(roomId, client, client.getUserId()!);
|
||||
localRoom = new LocalRoom(localRoomId, client, client.getUserId()!);
|
||||
|
||||
jest.spyOn(SdkContextClass.instance.roomViewStore, "getRoomId");
|
||||
});
|
||||
@ -68,7 +70,7 @@ describe("SlashCommands", () => {
|
||||
const command = getCommand("/topic pizza");
|
||||
expect(command.cmd).toBeDefined();
|
||||
expect(command.args).toBeDefined();
|
||||
await command.cmd.run("room-id", null, command.args);
|
||||
await command.cmd!.run("room-id", null, command.args);
|
||||
expect(client.setRoomTopic).toHaveBeenCalledWith("room-id", "pizza", undefined);
|
||||
});
|
||||
});
|
||||
@ -96,7 +98,7 @@ describe("SlashCommands", () => {
|
||||
["converttoroom"],
|
||||
])("/%s", (commandName: string) => {
|
||||
beforeEach(() => {
|
||||
command = findCommand(commandName);
|
||||
command = findCommand(commandName)!;
|
||||
});
|
||||
|
||||
describe("isEnabled", () => {
|
||||
@ -114,7 +116,7 @@ describe("SlashCommands", () => {
|
||||
|
||||
describe("/tovirtual", () => {
|
||||
beforeEach(() => {
|
||||
command = findCommand("tovirtual");
|
||||
command = findCommand("tovirtual")!;
|
||||
});
|
||||
|
||||
describe("isEnabled", () => {
|
||||
@ -154,7 +156,7 @@ describe("SlashCommands", () => {
|
||||
|
||||
describe("/remakeolm", () => {
|
||||
beforeEach(() => {
|
||||
command = findCommand("remakeolm");
|
||||
command = findCommand("remakeolm")!;
|
||||
});
|
||||
|
||||
describe("isEnabled", () => {
|
||||
@ -198,39 +200,39 @@ describe("SlashCommands", () => {
|
||||
|
||||
describe("/part", () => {
|
||||
it("should part room matching alias if found", async () => {
|
||||
const room1 = new Room("room-id", client, client.getUserId());
|
||||
const room1 = new Room("room-id", client, client.getUserId()!);
|
||||
room1.getCanonicalAlias = jest.fn().mockReturnValue("#foo:bar");
|
||||
const room2 = new Room("other-room", client, client.getUserId());
|
||||
const room2 = new Room("other-room", client, client.getUserId()!);
|
||||
room2.getCanonicalAlias = jest.fn().mockReturnValue("#baz:bar");
|
||||
mocked(client.getRooms).mockReturnValue([room1, room2]);
|
||||
|
||||
const command = getCommand("/part #foo:bar");
|
||||
expect(command.cmd).toBeDefined();
|
||||
expect(command.args).toBeDefined();
|
||||
await command.cmd.run("room-id", null, command.args);
|
||||
await command.cmd!.run("room-id", null, command.args);
|
||||
expect(client.leaveRoomChain).toHaveBeenCalledWith("room-id", expect.anything());
|
||||
});
|
||||
|
||||
it("should part room matching alt alias if found", async () => {
|
||||
const room1 = new Room("room-id", client, client.getUserId());
|
||||
const room1 = new Room("room-id", client, client.getUserId()!);
|
||||
room1.getAltAliases = jest.fn().mockReturnValue(["#foo:bar"]);
|
||||
const room2 = new Room("other-room", client, client.getUserId());
|
||||
const room2 = new Room("other-room", client, client.getUserId()!);
|
||||
room2.getAltAliases = jest.fn().mockReturnValue(["#baz:bar"]);
|
||||
mocked(client.getRooms).mockReturnValue([room1, room2]);
|
||||
|
||||
const command = getCommand("/part #foo:bar");
|
||||
expect(command.cmd).toBeDefined();
|
||||
expect(command.args).toBeDefined();
|
||||
await command.cmd.run("room-id", null, command.args);
|
||||
await command.cmd!.run("room-id", null, command.args);
|
||||
expect(client.leaveRoomChain).toHaveBeenCalledWith("room-id", expect.anything());
|
||||
});
|
||||
});
|
||||
|
||||
describe.each(["rainbow", "rainbowme"])("/%s", (commandName: string) => {
|
||||
const command = findCommand(commandName);
|
||||
const command = findCommand(commandName)!;
|
||||
|
||||
it("should return usage if no args", () => {
|
||||
expect(command.run(roomId, null, null).error).toBe(command.getUsage());
|
||||
expect(command.run(roomId, null).error).toBe(command.getUsage());
|
||||
});
|
||||
|
||||
it("should make things rainbowy", () => {
|
||||
|
@ -50,19 +50,19 @@ describe("SlidingSyncManager", () => {
|
||||
});
|
||||
it("adds a custom subscription for a lazy-loadable room", async () => {
|
||||
const roomId = "!lazy:id";
|
||||
const room = new Room(roomId, client, client.getUserId());
|
||||
const room = new Room(roomId, client, client.getUserId()!);
|
||||
room.getLiveTimeline().initialiseState([
|
||||
new MatrixEvent({
|
||||
type: "m.room.create",
|
||||
state_key: "",
|
||||
event_id: "$abc123",
|
||||
sender: client.getUserId(),
|
||||
sender: client.getUserId()!,
|
||||
content: {
|
||||
creator: client.getUserId(),
|
||||
},
|
||||
}),
|
||||
]);
|
||||
mocked(client.getRoom).mockImplementation((r: string): Room => {
|
||||
mocked(client.getRoom).mockImplementation((r: string): Room | null => {
|
||||
if (roomId === r) {
|
||||
return room;
|
||||
}
|
||||
|
@ -73,16 +73,16 @@ describe("EmojiProvider", function () {
|
||||
add("😘"); //kissing_heart
|
||||
add("😘");
|
||||
add("😚"); //kissing_closed_eyes
|
||||
const emojiProvider = new EmojiProvider(null);
|
||||
const emojiProvider = new EmojiProvider(null!);
|
||||
|
||||
let completionsList = await emojiProvider.getCompletions(":kis", { beginning: true, end: 3, start: 3 });
|
||||
expect(completionsList[0].component.props.title).toEqual(":kissing_heart:");
|
||||
expect(completionsList[1].component.props.title).toEqual(":kissing_closed_eyes:");
|
||||
expect(completionsList[0].component!.props.title).toEqual(":kissing_heart:");
|
||||
expect(completionsList[1].component!.props.title).toEqual(":kissing_closed_eyes:");
|
||||
|
||||
completionsList = await emojiProvider.getCompletions(":kissing_c", { beginning: true, end: 3, start: 3 });
|
||||
expect(completionsList[0].component.props.title).toEqual(":kissing_closed_eyes:");
|
||||
expect(completionsList[0].component!.props.title).toEqual(":kissing_closed_eyes:");
|
||||
|
||||
completionsList = await emojiProvider.getCompletions(":so", { beginning: true, end: 2, start: 2 });
|
||||
expect(completionsList[0].component.props.title).toEqual(":sob:");
|
||||
expect(completionsList[0].component!.props.title).toEqual(":sob:");
|
||||
});
|
||||
});
|
||||
|
@ -46,7 +46,7 @@ jest.mock("../../../src/utils/beacon", () => ({
|
||||
const roomId = "!roomId:server_name";
|
||||
|
||||
describe("MessagePanel", function () {
|
||||
let clock: FakeTimers.InstalledClock | null = null;
|
||||
let clock: FakeTimers.InstalledClock;
|
||||
const realSetTimeout = window.setTimeout;
|
||||
const events = mkEvents();
|
||||
const userId = "@me:here";
|
||||
@ -117,14 +117,11 @@ describe("MessagePanel", function () {
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
if (clock) {
|
||||
clock.uninstall();
|
||||
clock = null;
|
||||
}
|
||||
clock?.uninstall();
|
||||
});
|
||||
|
||||
function mkEvents() {
|
||||
const events = [];
|
||||
const events: MatrixEvent[] = [];
|
||||
const ts0 = Date.now();
|
||||
for (let i = 0; i < 10; i++) {
|
||||
events.push(
|
||||
@ -141,7 +138,7 @@ describe("MessagePanel", function () {
|
||||
|
||||
// Just to avoid breaking Dateseparator tests that might run at 00hrs
|
||||
function mkOneDayEvents() {
|
||||
const events = [];
|
||||
const events: MatrixEvent[] = [];
|
||||
const ts0 = Date.parse("09 May 2004 00:12:00 GMT");
|
||||
for (let i = 0; i < 10; i++) {
|
||||
events.push(
|
||||
@ -158,7 +155,7 @@ describe("MessagePanel", function () {
|
||||
|
||||
// make a collection of events with some member events that should be collapsed with an EventListSummary
|
||||
function mkMelsEvents() {
|
||||
const events = [];
|
||||
const events: MatrixEvent[] = [];
|
||||
const ts0 = Date.now();
|
||||
|
||||
let i = 0;
|
||||
@ -200,7 +197,7 @@ describe("MessagePanel", function () {
|
||||
|
||||
// A list of membership events only with nothing else
|
||||
function mkMelsEventsOnly() {
|
||||
const events = [];
|
||||
const events: MatrixEvent[] = [];
|
||||
const ts0 = Date.now();
|
||||
|
||||
let i = 0;
|
||||
@ -323,7 +320,7 @@ describe("MessagePanel", function () {
|
||||
}
|
||||
|
||||
function isReadMarkerVisible(rmContainer?: Element) {
|
||||
return rmContainer?.children.length > 0;
|
||||
return !!rmContainer?.children.length;
|
||||
}
|
||||
|
||||
it("should show the events", function () {
|
||||
@ -466,8 +463,8 @@ describe("MessagePanel", function () {
|
||||
|
||||
it("should collapse creation events", function () {
|
||||
const events = mkCreationEvents();
|
||||
const createEvent = events.find((event) => event.getType() === "m.room.create");
|
||||
const encryptionEvent = events.find((event) => event.getType() === "m.room.encryption");
|
||||
const createEvent = events.find((event) => event.getType() === "m.room.create")!;
|
||||
const encryptionEvent = events.find((event) => event.getType() === "m.room.encryption")!;
|
||||
client.getRoom.mockImplementation((id) => (id === createEvent!.getRoomId() ? room : null));
|
||||
TestUtilsMatrix.upsertRoomStateEvents(room, events);
|
||||
|
||||
@ -493,8 +490,8 @@ describe("MessagePanel", function () {
|
||||
|
||||
it("should not collapse beacons as part of creation events", function () {
|
||||
const events = mkCreationEvents();
|
||||
const creationEvent = events.find((event) => event.getType() === "m.room.create");
|
||||
const beaconInfoEvent = makeBeaconInfoEvent(creationEvent.getSender(), creationEvent.getRoomId(), {
|
||||
const creationEvent = events.find((event) => event.getType() === "m.room.create")!;
|
||||
const beaconInfoEvent = makeBeaconInfoEvent(creationEvent.getSender()!, creationEvent.getRoomId()!, {
|
||||
isLive: true,
|
||||
});
|
||||
const combinedEvents = [...events, beaconInfoEvent];
|
||||
@ -550,7 +547,7 @@ describe("MessagePanel", function () {
|
||||
let els = container.getElementsByClassName("mx_GenericEventListSummary");
|
||||
expect(els.length).toEqual(1);
|
||||
expect(els[0].getAttribute("data-testid")).toEqual("eventlistsummary-" + events[0].getId());
|
||||
expect(els[0].getAttribute("data-scroll-tokens").split(",").length).toEqual(10);
|
||||
expect(els[0].getAttribute("data-scroll-tokens")?.split(",")).toHaveLength(10);
|
||||
|
||||
const updatedEvents = [
|
||||
...events,
|
||||
@ -570,7 +567,7 @@ describe("MessagePanel", function () {
|
||||
els = container.getElementsByClassName("mx_GenericEventListSummary");
|
||||
expect(els.length).toEqual(1);
|
||||
expect(els[0].getAttribute("data-testid")).toEqual("eventlistsummary-" + events[0].getId());
|
||||
expect(els[0].getAttribute("data-scroll-tokens").split(",").length).toEqual(11);
|
||||
expect(els[0].getAttribute("data-scroll-tokens")?.split(",")).toHaveLength(11);
|
||||
});
|
||||
|
||||
it("prepends events into summaries during backward pagination without changing key", () => {
|
||||
@ -580,7 +577,7 @@ describe("MessagePanel", function () {
|
||||
let els = container.getElementsByClassName("mx_GenericEventListSummary");
|
||||
expect(els.length).toEqual(1);
|
||||
expect(els[0].getAttribute("data-testid")).toEqual("eventlistsummary-" + events[0].getId());
|
||||
expect(els[0].getAttribute("data-scroll-tokens").split(",").length).toEqual(10);
|
||||
expect(els[0].getAttribute("data-scroll-tokens")?.split(",")).toHaveLength(10);
|
||||
|
||||
const updatedEvents = [
|
||||
TestUtilsMatrix.mkMembership({
|
||||
@ -600,7 +597,7 @@ describe("MessagePanel", function () {
|
||||
els = container.getElementsByClassName("mx_GenericEventListSummary");
|
||||
expect(els.length).toEqual(1);
|
||||
expect(els[0].getAttribute("data-testid")).toEqual("eventlistsummary-" + events[0].getId());
|
||||
expect(els[0].getAttribute("data-scroll-tokens").split(",").length).toEqual(11);
|
||||
expect(els[0].getAttribute("data-scroll-tokens")?.split(",")).toHaveLength(11);
|
||||
});
|
||||
|
||||
it("assigns different keys to summaries that get split up", () => {
|
||||
@ -610,7 +607,7 @@ describe("MessagePanel", function () {
|
||||
let els = container.getElementsByClassName("mx_GenericEventListSummary");
|
||||
expect(els.length).toEqual(1);
|
||||
expect(els[0].getAttribute("data-testid")).toEqual(`eventlistsummary-${events[0].getId()}`);
|
||||
expect(els[0].getAttribute("data-scroll-tokens").split(",").length).toEqual(10);
|
||||
expect(els[0].getAttribute("data-scroll-tokens")?.split(",")).toHaveLength(10);
|
||||
|
||||
const updatedEvents = [
|
||||
...events.slice(0, 5),
|
||||
@ -628,10 +625,10 @@ describe("MessagePanel", function () {
|
||||
els = container.getElementsByClassName("mx_GenericEventListSummary");
|
||||
expect(els.length).toEqual(2);
|
||||
expect(els[0].getAttribute("data-testid")).toEqual(`eventlistsummary-${events[0].getId()}`);
|
||||
expect(els[0].getAttribute("data-scroll-tokens").split(",").length).toEqual(5);
|
||||
expect(els[0].getAttribute("data-scroll-tokens")?.split(",")).toHaveLength(5);
|
||||
|
||||
expect(els[1].getAttribute("data-testid")).toEqual(`eventlistsummary-${events[5].getId()}`);
|
||||
expect(els[1].getAttribute("data-scroll-tokens").split(",").length).toEqual(5);
|
||||
expect(els[1].getAttribute("data-scroll-tokens")?.split(",")).toHaveLength(5);
|
||||
});
|
||||
|
||||
// We test this because setting lookups can be *slow*, and we don't want
|
||||
@ -681,7 +678,7 @@ describe("MessagePanel", function () {
|
||||
|
||||
const els = container.getElementsByClassName("mx_GenericEventListSummary");
|
||||
expect(els.length).toEqual(1);
|
||||
expect(els[0].getAttribute("data-scroll-tokens").split(",").length).toEqual(3);
|
||||
expect(els[0].getAttribute("data-scroll-tokens")?.split(",")).toHaveLength(3);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -68,7 +68,7 @@ describe("ThreadPanel", () => {
|
||||
const found = container.querySelector(".mx_ThreadPanel_dropdown");
|
||||
expect(found).toBeTruthy();
|
||||
expect(screen.queryByRole("menu")).toBeFalsy();
|
||||
fireEvent.click(found);
|
||||
fireEvent.click(found!);
|
||||
expect(screen.queryByRole("menu")).toBeTruthy();
|
||||
});
|
||||
|
||||
@ -80,11 +80,13 @@ describe("ThreadPanel", () => {
|
||||
setFilterOption={() => undefined}
|
||||
/>,
|
||||
);
|
||||
fireEvent.click(container.querySelector(".mx_ThreadPanel_dropdown"));
|
||||
fireEvent.click(container.querySelector(".mx_ThreadPanel_dropdown")!);
|
||||
const found = screen.queryAllByRole("menuitemradio");
|
||||
expect(found).toHaveLength(2);
|
||||
const foundButton = screen.queryByRole("menuitemradio", { checked: true });
|
||||
expect(foundButton.textContent).toEqual(`${_t("All threads")}${_t("Shows all threads from current room")}`);
|
||||
expect(foundButton?.textContent).toEqual(
|
||||
`${_t("All threads")}${_t("Shows all threads from current room")}`,
|
||||
);
|
||||
expect(foundButton).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
@ -111,7 +111,7 @@ describe("<LeftPanelLiveShareWarning />", () => {
|
||||
const { container } = getComponent();
|
||||
const dispatchSpy = jest.spyOn(dispatcher, "dispatch");
|
||||
|
||||
fireEvent.click(container.querySelector("[role=button]"));
|
||||
fireEvent.click(container.querySelector("[role=button]")!);
|
||||
|
||||
expect(dispatchSpy).toHaveBeenCalledWith({
|
||||
action: Action.ViewRoom,
|
||||
@ -144,7 +144,7 @@ describe("<LeftPanelLiveShareWarning />", () => {
|
||||
const { container } = getComponent();
|
||||
const dispatchSpy = jest.spyOn(dispatcher, "dispatch");
|
||||
|
||||
fireEvent.click(container.querySelector("[role=button]"));
|
||||
fireEvent.click(container.querySelector("[role=button]")!);
|
||||
|
||||
expect(dispatchSpy).toHaveBeenCalledWith({
|
||||
action: Action.ViewRoom,
|
||||
@ -163,7 +163,7 @@ describe("<LeftPanelLiveShareWarning />", () => {
|
||||
]);
|
||||
const { container, rerender } = getComponent();
|
||||
// error mode
|
||||
expect(container.querySelector(".mx_LeftPanelLiveShareWarning").textContent).toEqual(
|
||||
expect(container.querySelector(".mx_LeftPanelLiveShareWarning")?.textContent).toEqual(
|
||||
"An error occurred whilst sharing your live location",
|
||||
);
|
||||
|
||||
@ -175,7 +175,7 @@ describe("<LeftPanelLiveShareWarning />", () => {
|
||||
rerender(<LeftPanelLiveShareWarning />);
|
||||
|
||||
// default mode
|
||||
expect(container.querySelector(".mx_LeftPanelLiveShareWarning").textContent).toEqual(
|
||||
expect(container.querySelector(".mx_LeftPanelLiveShareWarning")?.textContent).toEqual(
|
||||
"You are sharing your live location",
|
||||
);
|
||||
});
|
||||
@ -252,7 +252,7 @@ describe("<LeftPanelLiveShareWarning />", () => {
|
||||
const { container } = getComponent();
|
||||
const dispatchSpy = jest.spyOn(dispatcher, "dispatch");
|
||||
|
||||
fireEvent.click(container.querySelector("[role=button]"));
|
||||
fireEvent.click(container.querySelector("[role=button]")!);
|
||||
|
||||
expect(dispatchSpy).toHaveBeenCalledWith({
|
||||
action: Action.ViewRoom,
|
||||
|
@ -74,13 +74,13 @@ describe("ForwardDialog", () => {
|
||||
|
||||
const mountForwardDialog = (message = defaultMessage, rooms = defaultRooms) => {
|
||||
mockClient.getVisibleRooms.mockReturnValue(rooms);
|
||||
mockClient.getRoom.mockImplementation((roomId) => rooms.find((room) => room.roomId === roomId));
|
||||
mockClient.getRoom.mockImplementation((roomId) => rooms.find((room) => room.roomId === roomId) || null);
|
||||
|
||||
const wrapper: RenderResult = render(
|
||||
<ForwardDialog
|
||||
matrixClient={mockClient}
|
||||
event={message}
|
||||
permalinkCreator={new RoomPermalinkCreator(undefined, sourceRoom)}
|
||||
permalinkCreator={new RoomPermalinkCreator(undefined!, sourceRoom)}
|
||||
onFinished={jest.fn()}
|
||||
/>,
|
||||
);
|
||||
@ -135,8 +135,8 @@ describe("ForwardDialog", () => {
|
||||
}),
|
||||
);
|
||||
|
||||
let firstButton: Element;
|
||||
let secondButton: Element;
|
||||
let firstButton!: Element;
|
||||
let secondButton!: Element;
|
||||
const update = () => {
|
||||
[firstButton, secondButton] = container.querySelectorAll(".mx_ForwardList_sendButton");
|
||||
};
|
||||
@ -253,7 +253,6 @@ describe("ForwardDialog", () => {
|
||||
[M_ASSET.name]: { type: LocationAssetType.Pin },
|
||||
[M_LOCATION.name]: {
|
||||
uri: geoUri,
|
||||
description: undefined as string,
|
||||
},
|
||||
};
|
||||
expect(mockClient.sendEvent).toHaveBeenCalledWith(
|
||||
@ -269,7 +268,7 @@ describe("ForwardDialog", () => {
|
||||
expect(container.querySelector(".mx_MLocationBody")).toBeTruthy();
|
||||
sendToFirstRoom(container);
|
||||
|
||||
const timestamp = M_TIMESTAMP.findIn<number>(modernLocationEvent.getContent());
|
||||
const timestamp = M_TIMESTAMP.findIn<number>(modernLocationEvent.getContent())!;
|
||||
// text and description from original event are removed
|
||||
// text gets new default message from event values
|
||||
const text = `Location ${geoUri} at ${new Date(timestamp).toISOString()}`;
|
||||
@ -280,7 +279,6 @@ describe("ForwardDialog", () => {
|
||||
[M_ASSET.name]: { type: LocationAssetType.Pin },
|
||||
[M_LOCATION.name]: {
|
||||
uri: geoUri,
|
||||
description: undefined as string,
|
||||
},
|
||||
};
|
||||
expect(mockClient.sendEvent).toHaveBeenCalledWith(
|
||||
@ -301,7 +299,6 @@ describe("ForwardDialog", () => {
|
||||
[M_ASSET.name]: { type: LocationAssetType.Pin },
|
||||
[M_LOCATION.name]: {
|
||||
uri: geoUri,
|
||||
description: undefined as string,
|
||||
},
|
||||
geo_uri: geoUri,
|
||||
[M_TIMESTAMP.name]: timestamp,
|
||||
|
@ -92,7 +92,7 @@ function mockClient({
|
||||
(it) =>
|
||||
!searchTerm ||
|
||||
it.user_id.toLowerCase().includes(searchTerm) ||
|
||||
it.display_name.toLowerCase().includes(searchTerm),
|
||||
it.display_name?.toLowerCase().includes(searchTerm),
|
||||
);
|
||||
return Promise.resolve({
|
||||
results: results.slice(0, limit ?? +Infinity),
|
||||
@ -138,7 +138,7 @@ describe("Spotlight Dialog", () => {
|
||||
mockedClient = mockClient({ rooms: [testPublicRoom], users: [testPerson] });
|
||||
testRoom = mkRoom(mockedClient, "!test23:example.com");
|
||||
mocked(testRoom.getMyMembership).mockReturnValue("join");
|
||||
testLocalRoom = new LocalRoom(LOCAL_ROOM_ID_PREFIX + "test23", mockedClient, mockedClient.getUserId());
|
||||
testLocalRoom = new LocalRoom(LOCAL_ROOM_ID_PREFIX + "test23", mockedClient, mockedClient.getUserId()!);
|
||||
testLocalRoom.updateMyMembership("join");
|
||||
mocked(mockedClient.getVisibleRooms).mockReturnValue([testRoom, testLocalRoom]);
|
||||
|
||||
@ -149,7 +149,7 @@ describe("Spotlight Dialog", () => {
|
||||
|
||||
describe("should apply filters supplied via props", () => {
|
||||
it("without filter", async () => {
|
||||
const wrapper = mount(<SpotlightDialog initialFilter={null} onFinished={() => null} />);
|
||||
const wrapper = mount(<SpotlightDialog onFinished={() => null} />);
|
||||
await act(async () => {
|
||||
await sleep(200);
|
||||
});
|
||||
|
@ -61,7 +61,7 @@ describe("<StyledRadioGroup />", () => {
|
||||
value: optionC.value,
|
||||
});
|
||||
|
||||
expect(getCheckedInput(component).value).toEqual(optionC.value);
|
||||
expect(getCheckedInput(component)?.value).toEqual(optionC.value);
|
||||
});
|
||||
|
||||
it("selects correct buttons when definitions have checked prop", () => {
|
||||
@ -99,7 +99,7 @@ describe("<StyledRadioGroup />", () => {
|
||||
onChange,
|
||||
});
|
||||
|
||||
fireEvent.click(getInputByValue(component, optionB.value));
|
||||
fireEvent.click(getInputByValue(component, optionB.value)!);
|
||||
|
||||
expect(onChange).toHaveBeenCalledWith(optionB.value);
|
||||
});
|
||||
|
@ -62,7 +62,7 @@ describe("<TooltipTarget />", () => {
|
||||
|
||||
const alignmentKeys = Object.keys(Alignment).filter((o: any) => isNaN(o));
|
||||
it.each(alignmentKeys)("displays %s aligned tooltip on mouseover", async (alignment: any) => {
|
||||
const wrapper = getComponent({ alignment: Alignment[alignment] });
|
||||
const wrapper = getComponent({ alignment: Alignment[alignment] })!;
|
||||
act(() => {
|
||||
Simulate.mouseOver(wrapper);
|
||||
});
|
||||
@ -70,7 +70,7 @@ describe("<TooltipTarget />", () => {
|
||||
});
|
||||
|
||||
it("hides tooltip on mouseleave", () => {
|
||||
const wrapper = getComponent();
|
||||
const wrapper = getComponent()!;
|
||||
act(() => {
|
||||
Simulate.mouseOver(wrapper);
|
||||
});
|
||||
@ -82,7 +82,7 @@ describe("<TooltipTarget />", () => {
|
||||
});
|
||||
|
||||
it("displays tooltip on focus", () => {
|
||||
const wrapper = getComponent();
|
||||
const wrapper = getComponent()!;
|
||||
act(() => {
|
||||
Simulate.focus(wrapper);
|
||||
});
|
||||
@ -90,7 +90,7 @@ describe("<TooltipTarget />", () => {
|
||||
});
|
||||
|
||||
it("hides tooltip on blur", async () => {
|
||||
const wrapper = getComponent();
|
||||
const wrapper = getComponent()!;
|
||||
act(() => {
|
||||
Simulate.focus(wrapper);
|
||||
});
|
||||
|
@ -53,7 +53,7 @@ describe("shareLocation", () => {
|
||||
},
|
||||
);
|
||||
|
||||
shareLocationFn = shareLocation(client, roomId, shareType, null, () => {});
|
||||
shareLocationFn = shareLocation(client, roomId, shareType, undefined, () => {});
|
||||
});
|
||||
|
||||
it("should forward the call to doMaybeLocalRoomAction", () => {
|
||||
|
@ -52,7 +52,7 @@ describe("EncryptionEvent", () => {
|
||||
event = mkMessage({
|
||||
event: true,
|
||||
room: roomId,
|
||||
user: client.getUserId(),
|
||||
user: client.getUserId()!,
|
||||
});
|
||||
jest.spyOn(DMRoomMap, "shared").mockReturnValue({
|
||||
getUserIdForRoomId: jest.fn(),
|
||||
@ -61,9 +61,9 @@ describe("EncryptionEvent", () => {
|
||||
|
||||
describe("for an encrypted room", () => {
|
||||
beforeEach(() => {
|
||||
event.event.content.algorithm = algorithm;
|
||||
event.event.content!.algorithm = algorithm;
|
||||
mocked(client.isRoomEncrypted).mockReturnValue(true);
|
||||
const room = new Room(roomId, client, client.getUserId());
|
||||
const room = new Room(roomId, client, client.getUserId()!);
|
||||
mocked(client.getRoom).mockReturnValue(room);
|
||||
});
|
||||
|
||||
@ -91,7 +91,7 @@ describe("EncryptionEvent", () => {
|
||||
|
||||
describe("with unknown algorithm", () => {
|
||||
beforeEach(() => {
|
||||
event.event.content.algorithm = "unknown";
|
||||
event.event.content!.algorithm = "unknown";
|
||||
});
|
||||
|
||||
it("should show the expected texts", () => {
|
||||
@ -115,9 +115,9 @@ describe("EncryptionEvent", () => {
|
||||
|
||||
describe("for an encrypted local room", () => {
|
||||
beforeEach(() => {
|
||||
event.event.content.algorithm = algorithm;
|
||||
event.event.content!.algorithm = algorithm;
|
||||
mocked(client.isRoomEncrypted).mockReturnValue(true);
|
||||
const localRoom = new LocalRoom(roomId, client, client.getUserId());
|
||||
const localRoom = new LocalRoom(roomId, client, client.getUserId()!);
|
||||
mocked(client.getRoom).mockReturnValue(localRoom);
|
||||
renderEncryptionEvent(client, event);
|
||||
});
|
||||
|
@ -236,9 +236,7 @@ describe("<MessageActionBar />", () => {
|
||||
|
||||
it("opens message context menu on click", () => {
|
||||
const { getByTestId, queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent });
|
||||
act(() => {
|
||||
fireEvent.click(queryByLabelText("Options"));
|
||||
});
|
||||
fireEvent.click(queryByLabelText("Options")!);
|
||||
expect(getByTestId("mx_MessageContextMenu")).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -269,9 +267,7 @@ describe("<MessageActionBar />", () => {
|
||||
it("dispatches reply event on click", () => {
|
||||
const { queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent });
|
||||
|
||||
act(() => {
|
||||
fireEvent.click(queryByLabelText("Reply"));
|
||||
});
|
||||
fireEvent.click(queryByLabelText("Reply")!);
|
||||
|
||||
expect(dispatcher.dispatch).toHaveBeenCalledWith({
|
||||
action: "reply_to_event",
|
||||
@ -306,9 +302,7 @@ describe("<MessageActionBar />", () => {
|
||||
|
||||
it("opens reaction picker on click", () => {
|
||||
const { queryByLabelText, getByTestId } = getComponent({ mxEvent: alicesMessageEvent });
|
||||
act(() => {
|
||||
fireEvent.click(queryByLabelText("React"));
|
||||
});
|
||||
fireEvent.click(queryByLabelText("React")!);
|
||||
expect(getByTestId("mx_EmojiPicker")).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -421,9 +415,7 @@ describe("<MessageActionBar />", () => {
|
||||
jest.spyOn(SettingsStore, "getValue").mockReturnValue(false);
|
||||
const { getByLabelText } = getComponent({ mxEvent: alicesMessageEvent });
|
||||
|
||||
act(() => {
|
||||
fireEvent.click(getByLabelText("Reply in thread"));
|
||||
});
|
||||
fireEvent.click(getByLabelText("Reply in thread"));
|
||||
|
||||
expect(dispatcher.dispatch).toHaveBeenCalledWith({
|
||||
action: Action.ViewUserSettings,
|
||||
@ -453,9 +445,7 @@ describe("<MessageActionBar />", () => {
|
||||
it("opens thread on click", () => {
|
||||
const { getByLabelText } = getComponent({ mxEvent: alicesMessageEvent });
|
||||
|
||||
act(() => {
|
||||
fireEvent.click(getByLabelText("Reply in thread"));
|
||||
});
|
||||
fireEvent.click(getByLabelText("Reply in thread"));
|
||||
|
||||
expect(dispatcher.dispatch).toHaveBeenCalledWith({
|
||||
action: Action.ShowThread,
|
||||
@ -482,9 +472,7 @@ describe("<MessageActionBar />", () => {
|
||||
} as unknown as Thread);
|
||||
const { getByLabelText } = getComponent({ mxEvent: threadReplyEvent });
|
||||
|
||||
act(() => {
|
||||
fireEvent.click(getByLabelText("Reply in thread"));
|
||||
});
|
||||
fireEvent.click(getByLabelText("Reply in thread"));
|
||||
|
||||
expect(dispatcher.dispatch).toHaveBeenCalledWith({
|
||||
action: Action.ShowThread,
|
||||
@ -501,7 +489,7 @@ describe("<MessageActionBar />", () => {
|
||||
describe("favourite button", () => {
|
||||
//for multiple event usecase
|
||||
const favButton = (evt: MatrixEvent) => {
|
||||
return getComponent({ mxEvent: evt }).getByTestId(evt.getId());
|
||||
return getComponent({ mxEvent: evt }).getByTestId(evt.getId()!);
|
||||
};
|
||||
|
||||
describe("when favourite_messages feature is enabled", () => {
|
||||
@ -538,9 +526,7 @@ describe("<MessageActionBar />", () => {
|
||||
expect(localStorageMock.getItem("io_element_favouriteMessages")).toBeNull();
|
||||
|
||||
//if only alice's event is fired
|
||||
act(() => {
|
||||
fireEvent.click(alicesAction);
|
||||
});
|
||||
fireEvent.click(alicesAction);
|
||||
|
||||
expect(alicesAction.classList).toContain("mx_MessageActionBar_favouriteButton_fillstar");
|
||||
expect(bobsAction.classList).not.toContain("mx_MessageActionBar_favouriteButton_fillstar");
|
||||
@ -550,9 +536,7 @@ describe("<MessageActionBar />", () => {
|
||||
);
|
||||
|
||||
//when bob's event is fired,both should be styled and stored in localStorage
|
||||
act(() => {
|
||||
fireEvent.click(bobsAction);
|
||||
});
|
||||
fireEvent.click(bobsAction);
|
||||
|
||||
expect(alicesAction.classList).toContain("mx_MessageActionBar_favouriteButton_fillstar");
|
||||
expect(bobsAction.classList).toContain("mx_MessageActionBar_favouriteButton_fillstar");
|
||||
@ -567,9 +551,7 @@ describe("<MessageActionBar />", () => {
|
||||
);
|
||||
|
||||
//if decided to unfavourite bob's event by clicking again
|
||||
act(() => {
|
||||
fireEvent.click(bobsAction);
|
||||
});
|
||||
fireEvent.click(bobsAction);
|
||||
expect(bobsAction.classList).not.toContain("mx_MessageActionBar_favouriteButton_fillstar");
|
||||
expect(alicesAction.classList).toContain("mx_MessageActionBar_favouriteButton_fillstar");
|
||||
expect(localStorageMock.getItem("io_element_favouriteMessages")).toEqual('["$alices_message"]');
|
||||
@ -599,9 +581,7 @@ describe("<MessageActionBar />", () => {
|
||||
event.preventDefault = jest.fn();
|
||||
|
||||
const { queryByTestId, queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent });
|
||||
act(() => {
|
||||
fireEvent(queryByLabelText(buttonLabel), event);
|
||||
});
|
||||
fireEvent(queryByLabelText(buttonLabel)!, event);
|
||||
expect(event.stopPropagation).toHaveBeenCalled();
|
||||
expect(event.preventDefault).toHaveBeenCalled();
|
||||
expect(queryByTestId("mx_MessageContextMenu")).toBeFalsy();
|
||||
@ -610,9 +590,7 @@ describe("<MessageActionBar />", () => {
|
||||
|
||||
it("does shows context menu when right-clicking options", () => {
|
||||
const { queryByTestId, queryByLabelText } = getComponent({ mxEvent: alicesMessageEvent });
|
||||
act(() => {
|
||||
fireEvent.contextMenu(queryByLabelText("Options"));
|
||||
});
|
||||
fireEvent.contextMenu(queryByLabelText("Options")!);
|
||||
expect(queryByTestId("mx_MessageContextMenu")).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
@ -44,11 +44,11 @@ describe("MemberList", () => {
|
||||
return room;
|
||||
}
|
||||
|
||||
let parentDiv: HTMLDivElement = null;
|
||||
let client: MatrixClient = null;
|
||||
let root: Component = null;
|
||||
let parentDiv: HTMLDivElement;
|
||||
let client: MatrixClient;
|
||||
let root: Component;
|
||||
let memberListRoom: Room;
|
||||
let memberList: MemberList = null;
|
||||
let memberList: MemberList;
|
||||
|
||||
let adminUsers: RoomMember[] = [];
|
||||
let moderatorUsers: RoomMember[] = [];
|
||||
@ -140,14 +140,13 @@ describe("MemberList", () => {
|
||||
if (parentDiv) {
|
||||
ReactDOM.unmountComponentAtNode(parentDiv);
|
||||
parentDiv.remove();
|
||||
parentDiv = null;
|
||||
}
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
function expectOrderedByPresenceAndPowerLevel(memberTiles: MemberTile[], isPresenceEnabled: boolean) {
|
||||
let prevMember = null;
|
||||
let prevMember: RoomMember | undefined;
|
||||
for (const tile of memberTiles) {
|
||||
const memberA = prevMember;
|
||||
const memberB = tile.props.member;
|
||||
@ -160,8 +159,8 @@ describe("MemberList", () => {
|
||||
console.log(memberList.memberString(memberA));
|
||||
console.log(memberList.memberString(memberB));
|
||||
|
||||
const userA = memberA.user;
|
||||
const userB = memberB.user;
|
||||
const userA = memberA.user!;
|
||||
const userB = memberB.user!;
|
||||
|
||||
let groupChange = false;
|
||||
|
||||
@ -229,16 +228,16 @@ describe("MemberList", () => {
|
||||
const onlineUsers = [adminUsers[0]];
|
||||
const offlineUsers = [...moderatorUsers, ...adminUsers.slice(1), ...defaultUsers.slice(1)];
|
||||
activeUsers.forEach((u) => {
|
||||
u.user.currentlyActive = true;
|
||||
u.user.presence = "online";
|
||||
u.user!.currentlyActive = true;
|
||||
u.user!.presence = "online";
|
||||
});
|
||||
onlineUsers.forEach((u) => {
|
||||
u.user.currentlyActive = false;
|
||||
u.user.presence = "online";
|
||||
u.user!.currentlyActive = false;
|
||||
u.user!.presence = "online";
|
||||
});
|
||||
offlineUsers.forEach((u) => {
|
||||
u.user.currentlyActive = false;
|
||||
u.user.presence = "offline";
|
||||
u.user!.currentlyActive = false;
|
||||
u.user!.presence = "offline";
|
||||
});
|
||||
|
||||
// Bypass all the event listeners and skip to the good part
|
||||
@ -268,18 +267,18 @@ describe("MemberList", () => {
|
||||
const inactiveUsers = [...moderatorUsers, ...adminUsers.slice(1), ...defaultUsers.slice(1)];
|
||||
activeUsers.forEach((u) => {
|
||||
u.powerLevel = 100; // set everyone to the same PL to avoid running that check
|
||||
u.user.lastPresenceTs = 1000;
|
||||
u.user.lastActiveAgo = 0;
|
||||
u.user!.lastPresenceTs = 1000;
|
||||
u.user!.lastActiveAgo = 0;
|
||||
});
|
||||
semiActiveUsers.forEach((u) => {
|
||||
u.powerLevel = 100;
|
||||
u.user.lastPresenceTs = 1000;
|
||||
u.user.lastActiveAgo = 50;
|
||||
u.user!.lastPresenceTs = 1000;
|
||||
u.user!.lastActiveAgo = 50;
|
||||
});
|
||||
inactiveUsers.forEach((u) => {
|
||||
u.powerLevel = 100;
|
||||
u.user.lastPresenceTs = 1000;
|
||||
u.user.lastActiveAgo = 100;
|
||||
u.user!.lastPresenceTs = 1000;
|
||||
u.user!.lastActiveAgo = 100;
|
||||
});
|
||||
|
||||
// Bypass all the event listeners and skip to the good part
|
||||
@ -294,10 +293,10 @@ describe("MemberList", () => {
|
||||
// Intentionally put everyone on the same level to force a name comparison
|
||||
const allUsers = [...adminUsers, ...moderatorUsers, ...defaultUsers];
|
||||
allUsers.forEach((u) => {
|
||||
u.user.currentlyActive = true;
|
||||
u.user.presence = "online";
|
||||
u.user.lastPresenceTs = 1000;
|
||||
u.user.lastActiveAgo = 0;
|
||||
u.user!.currentlyActive = true;
|
||||
u.user!.presence = "online";
|
||||
u.user!.lastPresenceTs = 1000;
|
||||
u.user!.lastActiveAgo = 0;
|
||||
u.powerLevel = 100;
|
||||
});
|
||||
|
||||
|
@ -37,13 +37,13 @@ describe("NotificationBadge", () => {
|
||||
/>,
|
||||
);
|
||||
|
||||
fireEvent.click(container.firstChild);
|
||||
fireEvent.click(container.firstChild!);
|
||||
expect(cb).toHaveBeenCalledTimes(1);
|
||||
|
||||
fireEvent.mouseEnter(container.firstChild);
|
||||
fireEvent.mouseEnter(container.firstChild!);
|
||||
expect(cb).toHaveBeenCalledTimes(2);
|
||||
|
||||
fireEvent.mouseLeave(container.firstChild);
|
||||
fireEvent.mouseLeave(container.firstChild!);
|
||||
expect(cb).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
|
||||
|
@ -217,7 +217,7 @@ function createRoom(info: IRoomCreationInfo) {
|
||||
const client: MatrixClient = MatrixClientPeg.get();
|
||||
|
||||
const roomId = "!1234567890:domain";
|
||||
const userId = client.getUserId();
|
||||
const userId = client.getUserId()!;
|
||||
if (info.isDm) {
|
||||
client.getAccountData = (eventType) => {
|
||||
expect(eventType).toEqual("m.direct");
|
||||
@ -231,7 +231,7 @@ function createRoom(info: IRoomCreationInfo) {
|
||||
pendingEventOrdering: PendingEventOrdering.Detached,
|
||||
});
|
||||
|
||||
const otherJoinEvents = [];
|
||||
const otherJoinEvents: MatrixEvent[] = [];
|
||||
for (const otherUserId of info.userIds) {
|
||||
otherJoinEvents.push(mkJoinEvent(roomId, otherUserId));
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ describe("<SendMessageComposer/>", () => {
|
||||
const documentOffset = new DocumentOffset(11, true);
|
||||
model.update("hello world", "insertText", documentOffset);
|
||||
|
||||
const content = createMessageContent(model, null, undefined, permalinkCreator);
|
||||
const content = createMessageContent(model, undefined, undefined, permalinkCreator);
|
||||
|
||||
expect(content).toEqual({
|
||||
body: "hello world",
|
||||
@ -101,7 +101,7 @@ describe("<SendMessageComposer/>", () => {
|
||||
const documentOffset = new DocumentOffset(13, true);
|
||||
model.update("hello *world*", "insertText", documentOffset);
|
||||
|
||||
const content = createMessageContent(model, null, undefined, permalinkCreator);
|
||||
const content = createMessageContent(model, undefined, undefined, permalinkCreator);
|
||||
|
||||
expect(content).toEqual({
|
||||
body: "hello *world*",
|
||||
@ -116,7 +116,7 @@ describe("<SendMessageComposer/>", () => {
|
||||
const documentOffset = new DocumentOffset(22, true);
|
||||
model.update("/me blinks __quickly__", "insertText", documentOffset);
|
||||
|
||||
const content = createMessageContent(model, null, undefined, permalinkCreator);
|
||||
const content = createMessageContent(model, undefined, undefined, permalinkCreator);
|
||||
|
||||
expect(content).toEqual({
|
||||
body: "blinks __quickly__",
|
||||
@ -132,7 +132,7 @@ describe("<SendMessageComposer/>", () => {
|
||||
model.update("/me ✨sparkles✨", "insertText", documentOffset);
|
||||
expect(model.parts.length).toEqual(4); // Emoji count as non-text
|
||||
|
||||
const content = createMessageContent(model, null, undefined, permalinkCreator);
|
||||
const content = createMessageContent(model, undefined, undefined, permalinkCreator);
|
||||
|
||||
expect(content).toEqual({
|
||||
body: "✨sparkles✨",
|
||||
@ -146,7 +146,7 @@ describe("<SendMessageComposer/>", () => {
|
||||
|
||||
model.update("//dev/null is my favourite place", "insertText", documentOffset);
|
||||
|
||||
const content = createMessageContent(model, null, undefined, permalinkCreator);
|
||||
const content = createMessageContent(model, undefined, undefined, permalinkCreator);
|
||||
|
||||
expect(content).toEqual({
|
||||
body: "/dev/null is my favourite place",
|
||||
@ -216,7 +216,7 @@ describe("<SendMessageComposer/>", () => {
|
||||
|
||||
// ensure the right state was persisted to localStorage
|
||||
unmount();
|
||||
expect(JSON.parse(localStorage.getItem(key))).toStrictEqual({
|
||||
expect(JSON.parse(localStorage.getItem(key)!)).toStrictEqual({
|
||||
parts: [{ type: "plain", text: "Test Text" }],
|
||||
replyEventId: mockEvent.getId(),
|
||||
});
|
||||
@ -249,7 +249,7 @@ describe("<SendMessageComposer/>", () => {
|
||||
|
||||
// ensure the right state was persisted to localStorage
|
||||
window.dispatchEvent(new Event("beforeunload"));
|
||||
expect(JSON.parse(localStorage.getItem(key))).toStrictEqual({
|
||||
expect(JSON.parse(localStorage.getItem(key)!)).toStrictEqual({
|
||||
parts: [{ type: "plain", text: "Hello World" }],
|
||||
});
|
||||
});
|
||||
@ -260,7 +260,7 @@ describe("<SendMessageComposer/>", () => {
|
||||
const { container } = getComponent({ replyToEvent: mockEvent });
|
||||
|
||||
addTextToComposer(container, "This is a message");
|
||||
fireEvent.keyDown(container.querySelector(".mx_SendMessageComposer"), { key: "Enter" });
|
||||
fireEvent.keyDown(container.querySelector(".mx_SendMessageComposer")!, { key: "Enter" });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(spyDispatcher).toHaveBeenCalledWith({
|
||||
@ -271,7 +271,7 @@ describe("<SendMessageComposer/>", () => {
|
||||
});
|
||||
|
||||
expect(container.textContent).toBe("");
|
||||
const str = sessionStorage.getItem(`mx_cider_history_${mockRoom.roomId}[0]`);
|
||||
const str = sessionStorage.getItem(`mx_cider_history_${mockRoom.roomId}[0]`)!;
|
||||
expect(JSON.parse(str)).toStrictEqual({
|
||||
parts: [{ type: "plain", text: "This is a message" }],
|
||||
replyEventId: mockEvent.getId(),
|
||||
@ -289,7 +289,7 @@ describe("<SendMessageComposer/>", () => {
|
||||
const { container } = getComponent();
|
||||
|
||||
addTextToComposer(container, "test message");
|
||||
fireEvent.keyDown(container.querySelector(".mx_SendMessageComposer"), { key: "Enter" });
|
||||
fireEvent.keyDown(container.querySelector(".mx_SendMessageComposer")!, { key: "Enter" });
|
||||
|
||||
expect(mockClient.sendMessage).toHaveBeenCalledWith("myfakeroom", null, {
|
||||
body: "test message",
|
||||
|
@ -310,11 +310,8 @@ describe("<Notifications />", () => {
|
||||
it("enables email notification when toggling on", async () => {
|
||||
await getComponentAndWait();
|
||||
|
||||
const emailToggle = screen.getByTestId("notif-email-switch").querySelector('div[role="switch"]');
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(emailToggle);
|
||||
});
|
||||
const emailToggle = screen.getByTestId("notif-email-switch").querySelector('div[role="switch"]')!;
|
||||
fireEvent.click(emailToggle);
|
||||
|
||||
expect(mockClient.setPusher).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
@ -332,11 +329,8 @@ describe("<Notifications />", () => {
|
||||
mockClient.setPusher.mockRejectedValue({});
|
||||
await getComponentAndWait();
|
||||
|
||||
const emailToggle = screen.getByTestId("notif-email-switch").querySelector('div[role="switch"]');
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(emailToggle);
|
||||
});
|
||||
const emailToggle = screen.getByTestId("notif-email-switch").querySelector('div[role="switch"]')!;
|
||||
fireEvent.click(emailToggle);
|
||||
|
||||
// force render
|
||||
await flushPromises();
|
||||
@ -349,11 +343,8 @@ describe("<Notifications />", () => {
|
||||
mockClient.getPushers.mockResolvedValue({ pushers: [testPusher] });
|
||||
await getComponentAndWait();
|
||||
|
||||
const emailToggle = screen.getByTestId("notif-email-switch").querySelector('div[role="switch"]');
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(emailToggle);
|
||||
});
|
||||
const emailToggle = screen.getByTestId("notif-email-switch").querySelector('div[role="switch"]')!;
|
||||
fireEvent.click(emailToggle);
|
||||
|
||||
expect(mockClient.setPusher).toHaveBeenCalledWith({
|
||||
...testPusher,
|
||||
@ -364,21 +355,19 @@ describe("<Notifications />", () => {
|
||||
|
||||
it("toggles and sets settings correctly", async () => {
|
||||
await getComponentAndWait();
|
||||
let audioNotifsToggle: HTMLDivElement;
|
||||
let audioNotifsToggle!: HTMLDivElement;
|
||||
|
||||
const update = () => {
|
||||
audioNotifsToggle = screen
|
||||
.getByTestId("notif-setting-audioNotificationsEnabled")
|
||||
.querySelector('div[role="switch"]');
|
||||
.querySelector('div[role="switch"]')!;
|
||||
};
|
||||
update();
|
||||
|
||||
expect(audioNotifsToggle.getAttribute("aria-checked")).toEqual("true");
|
||||
expect(SettingsStore.getValue("audioNotificationsEnabled")).toEqual(true);
|
||||
|
||||
act(() => {
|
||||
fireEvent.click(audioNotifsToggle);
|
||||
});
|
||||
fireEvent.click(audioNotifsToggle);
|
||||
update();
|
||||
|
||||
expect(audioNotifsToggle.getAttribute("aria-checked")).toEqual("false");
|
||||
@ -422,7 +411,7 @@ describe("<Notifications />", () => {
|
||||
const oneToOneRuleElement = screen.getByTestId(section + oneToOneRule.rule_id);
|
||||
|
||||
await act(async () => {
|
||||
const offToggle = oneToOneRuleElement.querySelector('input[type="radio"]');
|
||||
const offToggle = oneToOneRuleElement.querySelector('input[type="radio"]')!;
|
||||
fireEvent.click(offToggle);
|
||||
});
|
||||
|
||||
|
@ -55,7 +55,7 @@ describe("RolesRoomSettingsTab", () => {
|
||||
};
|
||||
|
||||
const getElementCallSwitch = (tab: RenderResult): HTMLElement => {
|
||||
return tab.container.querySelector("[data-testid='element-call-switch']");
|
||||
return tab.container.querySelector("[data-testid='element-call-switch']")!;
|
||||
};
|
||||
|
||||
describe("correct state", () => {
|
||||
@ -87,7 +87,7 @@ describe("RolesRoomSettingsTab", () => {
|
||||
|
||||
const tab = renderTab();
|
||||
|
||||
fireEvent.click(getElementCallSwitch(tab).querySelector(".mx_ToggleSwitch"));
|
||||
fireEvent.click(getElementCallSwitch(tab).querySelector(".mx_ToggleSwitch")!);
|
||||
await waitFor(() =>
|
||||
expect(cli.sendStateEvent).toHaveBeenCalledWith(
|
||||
room.roomId,
|
||||
@ -107,7 +107,7 @@ describe("RolesRoomSettingsTab", () => {
|
||||
|
||||
const tab = renderTab();
|
||||
|
||||
fireEvent.click(getElementCallSwitch(tab).querySelector(".mx_ToggleSwitch"));
|
||||
fireEvent.click(getElementCallSwitch(tab).querySelector(".mx_ToggleSwitch")!);
|
||||
await waitFor(() =>
|
||||
expect(cli.sendStateEvent).toHaveBeenCalledWith(
|
||||
room.roomId,
|
||||
@ -128,7 +128,7 @@ describe("RolesRoomSettingsTab", () => {
|
||||
|
||||
const tab = renderTab();
|
||||
|
||||
fireEvent.click(getElementCallSwitch(tab).querySelector(".mx_ToggleSwitch"));
|
||||
fireEvent.click(getElementCallSwitch(tab).querySelector(".mx_ToggleSwitch")!);
|
||||
await waitFor(() =>
|
||||
expect(cli.sendStateEvent).toHaveBeenCalledWith(
|
||||
room.roomId,
|
||||
|
@ -94,13 +94,13 @@ describe("<SpaceSettingsVisibilityTab />", () => {
|
||||
|
||||
const getByTestId = (container: Element, id: string) => container.querySelector(`[data-test-id=${id}]`);
|
||||
const toggleGuestAccessSection = async (component: Element) => {
|
||||
const toggleButton = getByTestId(component, "toggle-guest-access-btn");
|
||||
const toggleButton = getByTestId(component, "toggle-guest-access-btn")!;
|
||||
await act(async () => {
|
||||
Simulate.click(toggleButton);
|
||||
});
|
||||
};
|
||||
const getGuestAccessToggle = (component: Element) => component.querySelector('[aria-label="Enable guest access"');
|
||||
const getHistoryVisibilityToggle = (component: Element) => component.querySelector('[aria-label="Preview Space"');
|
||||
const getGuestAccessToggle = (component: Element) => component.querySelector('[aria-label="Enable guest access"]');
|
||||
const getHistoryVisibilityToggle = (component: Element) => component.querySelector('[aria-label="Preview Space"]');
|
||||
const getErrorMessage = (component: Element) => getByTestId(component, "space-settings-error")?.textContent;
|
||||
|
||||
beforeEach(() => {
|
||||
@ -150,10 +150,10 @@ describe("<SpaceSettingsVisibilityTab />", () => {
|
||||
await toggleGuestAccessSection(component);
|
||||
const guestAccessInput = getGuestAccessToggle(component);
|
||||
|
||||
expect(guestAccessInput.getAttribute("aria-checked")).toEqual("true");
|
||||
expect(guestAccessInput?.getAttribute("aria-checked")).toEqual("true");
|
||||
|
||||
await act(async () => {
|
||||
Simulate.click(guestAccessInput);
|
||||
Simulate.click(guestAccessInput!);
|
||||
});
|
||||
|
||||
expect(mockMatrixClient.sendStateEvent).toHaveBeenCalledWith(
|
||||
@ -165,7 +165,7 @@ describe("<SpaceSettingsVisibilityTab />", () => {
|
||||
);
|
||||
|
||||
// toggled off
|
||||
expect(guestAccessInput.getAttribute("aria-checked")).toEqual("false");
|
||||
expect(guestAccessInput?.getAttribute("aria-checked")).toEqual("false");
|
||||
});
|
||||
|
||||
it("renders error message when update fails", async () => {
|
||||
@ -174,7 +174,7 @@ describe("<SpaceSettingsVisibilityTab />", () => {
|
||||
const component = getComponent({ space });
|
||||
await toggleGuestAccessSection(component);
|
||||
await act(async () => {
|
||||
Simulate.click(getGuestAccessToggle(component));
|
||||
Simulate.click(getGuestAccessToggle(component)!);
|
||||
});
|
||||
|
||||
expect(getErrorMessage(component)).toEqual("Failed to update the guest access of this space");
|
||||
@ -187,7 +187,7 @@ describe("<SpaceSettingsVisibilityTab />", () => {
|
||||
|
||||
await toggleGuestAccessSection(component);
|
||||
|
||||
expect(getGuestAccessToggle(component).getAttribute("aria-disabled")).toEqual("true");
|
||||
expect(getGuestAccessToggle(component)?.getAttribute("aria-disabled")).toEqual("true");
|
||||
});
|
||||
});
|
||||
|
||||
@ -197,7 +197,7 @@ describe("<SpaceSettingsVisibilityTab />", () => {
|
||||
const component = getComponent({ space });
|
||||
|
||||
// toggle off because space settings is != WorldReadable
|
||||
expect(getHistoryVisibilityToggle(component).getAttribute("aria-checked")).toEqual("false");
|
||||
expect(getHistoryVisibilityToggle(component)?.getAttribute("aria-checked")).toEqual("false");
|
||||
});
|
||||
|
||||
it("updates history visibility on toggle", async () => {
|
||||
@ -205,10 +205,10 @@ describe("<SpaceSettingsVisibilityTab />", () => {
|
||||
const component = getComponent({ space });
|
||||
|
||||
// toggle off because space settings is != WorldReadable
|
||||
expect(getHistoryVisibilityToggle(component).getAttribute("aria-checked")).toEqual("false");
|
||||
expect(getHistoryVisibilityToggle(component)?.getAttribute("aria-checked")).toEqual("false");
|
||||
|
||||
await act(async () => {
|
||||
Simulate.click(getHistoryVisibilityToggle(component));
|
||||
Simulate.click(getHistoryVisibilityToggle(component)!);
|
||||
});
|
||||
|
||||
expect(mockMatrixClient.sendStateEvent).toHaveBeenCalledWith(
|
||||
@ -218,7 +218,7 @@ describe("<SpaceSettingsVisibilityTab />", () => {
|
||||
"",
|
||||
);
|
||||
|
||||
expect(getHistoryVisibilityToggle(component).getAttribute("aria-checked")).toEqual("true");
|
||||
expect(getHistoryVisibilityToggle(component)?.getAttribute("aria-checked")).toEqual("true");
|
||||
});
|
||||
|
||||
it("renders error message when history update fails", async () => {
|
||||
@ -227,7 +227,7 @@ describe("<SpaceSettingsVisibilityTab />", () => {
|
||||
const component = getComponent({ space });
|
||||
|
||||
await act(async () => {
|
||||
Simulate.click(getHistoryVisibilityToggle(component));
|
||||
Simulate.click(getHistoryVisibilityToggle(component)!);
|
||||
});
|
||||
|
||||
expect(getErrorMessage(component)).toEqual("Failed to update the history visibility of this space");
|
||||
@ -237,7 +237,7 @@ describe("<SpaceSettingsVisibilityTab />", () => {
|
||||
const space = makeMockSpace(mockMatrixClient, joinRule, guestRule, historyRule);
|
||||
(space.currentState.maySendStateEvent as jest.Mock).mockReturnValue(false);
|
||||
const component = getComponent({ space });
|
||||
expect(getHistoryVisibilityToggle(component).getAttribute("aria-disabled")).toEqual("true");
|
||||
expect(getHistoryVisibilityToggle(component)?.getAttribute("aria-disabled")).toEqual("true");
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -34,7 +34,7 @@ describe("languageHandler", function () {
|
||||
const plurals = "and %(count)s others...";
|
||||
const variableSub = "You are now ignoring %(userId)s";
|
||||
|
||||
type TestCase = [string, string, Record<string, unknown>, Record<string, unknown>, TranslatedString];
|
||||
type TestCase = [string, string, Record<string, unknown>, Record<string, unknown> | undefined, TranslatedString];
|
||||
const testCasesEn: TestCase[] = [
|
||||
// description of the test case, translationString, variables, tags, expected result
|
||||
["translates a basic string", basicString, {}, undefined, "Rooms"],
|
||||
@ -82,7 +82,7 @@ describe("languageHandler", function () {
|
||||
],
|
||||
];
|
||||
|
||||
let oldNodeEnv: string;
|
||||
let oldNodeEnv: string | undefined;
|
||||
beforeAll(() => {
|
||||
oldNodeEnv = process.env.NODE_ENV;
|
||||
process.env.NODE_ENV = "test";
|
||||
@ -110,7 +110,7 @@ describe("languageHandler", function () {
|
||||
});
|
||||
|
||||
it.each(testCasesEn)("%s", (_d, translationString, variables, tags, result) => {
|
||||
expect(_t(translationString, variables, tags)).toEqual(result);
|
||||
expect(_t(translationString, variables, tags!)).toEqual(result);
|
||||
});
|
||||
|
||||
it("replacements in the wrong order", function () {
|
||||
@ -168,25 +168,25 @@ describe("languageHandler", function () {
|
||||
|
||||
describe("_t", () => {
|
||||
it("translated correctly when plural string exists for count", () => {
|
||||
expect(_t(lvExistingPlural, { count: 1, filename: "test.txt" }, undefined)).toEqual(
|
||||
expect(_t(lvExistingPlural, { count: 1, filename: "test.txt" })).toEqual(
|
||||
"Качване на test.txt и 1 друг",
|
||||
);
|
||||
});
|
||||
it.each(pluralCases)("%s", (_d, translationString, variables, tags, result) => {
|
||||
expect(_t(translationString, variables, tags)).toEqual(result);
|
||||
expect(_t(translationString, variables, tags!)).toEqual(result);
|
||||
});
|
||||
});
|
||||
|
||||
describe("_tDom()", () => {
|
||||
it("translated correctly when plural string exists for count", () => {
|
||||
expect(_tDom(lvExistingPlural, { count: 1, filename: "test.txt" }, undefined)).toEqual(
|
||||
expect(_tDom(lvExistingPlural, { count: 1, filename: "test.txt" })).toEqual(
|
||||
"Качване на test.txt и 1 друг",
|
||||
);
|
||||
});
|
||||
it.each(pluralCases)(
|
||||
"%s and translates with fallback locale, attributes fallback locale",
|
||||
(_d, translationString, variables, tags, result) => {
|
||||
expect(_tDom(translationString, variables, tags)).toEqual(<span lang="en">{result}</span>);
|
||||
expect(_tDom(translationString, variables, tags!)).toEqual(<span lang="en">{result}</span>);
|
||||
},
|
||||
);
|
||||
});
|
||||
@ -197,7 +197,7 @@ describe("languageHandler", function () {
|
||||
it.each(testCasesEn)(
|
||||
"%s and translates with fallback locale",
|
||||
(_d, translationString, variables, tags, result) => {
|
||||
expect(_t(translationString, variables, tags)).toEqual(result);
|
||||
expect(_t(translationString, variables, tags!)).toEqual(result);
|
||||
},
|
||||
);
|
||||
});
|
||||
@ -206,7 +206,7 @@ describe("languageHandler", function () {
|
||||
it.each(testCasesEn)(
|
||||
"%s and translates with fallback locale, attributes fallback locale",
|
||||
(_d, translationString, variables, tags, result) => {
|
||||
expect(_tDom(translationString, variables, tags)).toEqual(<span lang="en">{result}</span>);
|
||||
expect(_tDom(translationString, variables, tags!)).toEqual(<span lang="en">{result}</span>);
|
||||
},
|
||||
);
|
||||
});
|
||||
@ -216,12 +216,12 @@ describe("languageHandler", function () {
|
||||
describe("when languages dont load", () => {
|
||||
it("_t", () => {
|
||||
const STRING_NOT_IN_THE_DICTIONARY = "a string that isn't in the translations dictionary";
|
||||
expect(_t(STRING_NOT_IN_THE_DICTIONARY, {}, undefined)).toEqual(STRING_NOT_IN_THE_DICTIONARY);
|
||||
expect(_t(STRING_NOT_IN_THE_DICTIONARY, {})).toEqual(STRING_NOT_IN_THE_DICTIONARY);
|
||||
});
|
||||
|
||||
it("_tDom", () => {
|
||||
const STRING_NOT_IN_THE_DICTIONARY = "a string that isn't in the translations dictionary";
|
||||
expect(_tDom(STRING_NOT_IN_THE_DICTIONARY, {}, undefined)).toEqual(
|
||||
expect(_tDom(STRING_NOT_IN_THE_DICTIONARY, {})).toEqual(
|
||||
<span lang="en">{STRING_NOT_IN_THE_DICTIONARY}</span>,
|
||||
);
|
||||
});
|
||||
|
@ -383,7 +383,7 @@ describe("JitsiCall", () => {
|
||||
await waitFor(
|
||||
() =>
|
||||
expect(
|
||||
room.currentState.getStateEvents(JitsiCall.MEMBER_EVENT_TYPE, alice.userId).getContent(),
|
||||
room.currentState.getStateEvents(JitsiCall.MEMBER_EVENT_TYPE, alice.userId)?.getContent(),
|
||||
).toEqual({
|
||||
devices: [client.getDeviceId()],
|
||||
expires_ts: now1 + call.STUCK_DEVICE_TIMEOUT_MS,
|
||||
@ -396,7 +396,7 @@ describe("JitsiCall", () => {
|
||||
await waitFor(
|
||||
() =>
|
||||
expect(
|
||||
room.currentState.getStateEvents(JitsiCall.MEMBER_EVENT_TYPE, alice.userId).getContent(),
|
||||
room.currentState.getStateEvents(JitsiCall.MEMBER_EVENT_TYPE, alice.userId)?.getContent(),
|
||||
).toEqual({
|
||||
devices: [],
|
||||
expires_ts: now2 + call.STUCK_DEVICE_TIMEOUT_MS,
|
||||
@ -495,7 +495,7 @@ describe("JitsiCall", () => {
|
||||
});
|
||||
const expectDevices = (devices: IMyDevice[]) =>
|
||||
expect(
|
||||
room.currentState.getStateEvents(JitsiCall.MEMBER_EVENT_TYPE, alice.userId).getContent(),
|
||||
room.currentState.getStateEvents(JitsiCall.MEMBER_EVENT_TYPE, alice.userId)?.getContent(),
|
||||
).toEqual({
|
||||
expires_ts: expect.any(Number),
|
||||
devices: devices.map((d) => d.device_id),
|
||||
|
@ -37,12 +37,12 @@ function makeMatchMedia(values: any) {
|
||||
}
|
||||
|
||||
return function matchMedia(query: string) {
|
||||
return new FakeMediaQueryList(query);
|
||||
return new FakeMediaQueryList(query) as unknown as MediaQueryList;
|
||||
};
|
||||
}
|
||||
|
||||
function makeGetValue(values: any) {
|
||||
return function getValue<T = any>(settingName: string, _roomId: string = null, _excludeDefault = false): T {
|
||||
return function getValue<T = any>(settingName: string, _roomId: string | null = null, _excludeDefault = false): T {
|
||||
return values[settingName];
|
||||
};
|
||||
}
|
||||
@ -51,7 +51,7 @@ function makeGetValueAt(values: any) {
|
||||
return function getValueAt(
|
||||
_level: SettingLevel,
|
||||
settingName: string,
|
||||
_roomId: string = null,
|
||||
_roomId: string | null = null,
|
||||
_explicit = false,
|
||||
_excludeDefault = false,
|
||||
): any {
|
||||
|
Loading…
Reference in New Issue
Block a user