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
89 changed files with 520 additions and 551 deletions
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue