Conform more of the codebase with strictNullChecks (#10703)

This commit is contained in:
Michael Telatynski 2023-04-25 09:28:48 +01:00 committed by GitHub
parent db40479910
commit 619a9e8542
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 108 additions and 77 deletions

View file

@ -118,7 +118,7 @@ export default class IdentityAuthClient {
} }
private async checkToken(token: string): Promise<void> { private async checkToken(token: string): Promise<void> {
const identityServerUrl = this.matrixClient.getIdentityServerUrl(); const identityServerUrl = this.matrixClient.getIdentityServerUrl()!;
try { try {
await this.matrixClient.getIdentityAccount(token); await this.matrixClient.getIdentityAccount(token);

View file

@ -579,7 +579,7 @@ export default class LegacyCallHandler extends EventEmitter {
}); });
}); });
call.on(CallEvent.Hangup, () => { call.on(CallEvent.Hangup, () => {
if (!this.matchesCallForThisRoom(call)) return; if (!mappedRoomId || !this.matchesCallForThisRoom(call)) return;
this.removeCallForRoom(mappedRoomId); this.removeCallForRoom(mappedRoomId);
}); });
@ -587,7 +587,7 @@ export default class LegacyCallHandler extends EventEmitter {
this.onCallStateChanged(newState, oldState, call); this.onCallStateChanged(newState, oldState, call);
}); });
call.on(CallEvent.Replaced, (newCall: MatrixCall) => { call.on(CallEvent.Replaced, (newCall: MatrixCall) => {
if (!this.matchesCallForThisRoom(call)) return; if (!mappedRoomId || !this.matchesCallForThisRoom(call)) return;
logger.log(`Call ID ${call.callId} is being replaced by call ID ${newCall.callId}`); logger.log(`Call ID ${call.callId} is being replaced by call ID ${newCall.callId}`);
@ -603,7 +603,7 @@ export default class LegacyCallHandler extends EventEmitter {
this.setCallState(newCall, newCall.state); this.setCallState(newCall, newCall.state);
}); });
call.on(CallEvent.AssertedIdentityChanged, async (): Promise<void> => { call.on(CallEvent.AssertedIdentityChanged, async (): Promise<void> => {
if (!this.matchesCallForThisRoom(call)) return; if (!mappedRoomId || !this.matchesCallForThisRoom(call)) return;
logger.log(`Call ID ${call.callId} got new asserted identity:`, call.getRemoteAssertedIdentity()); logger.log(`Call ID ${call.callId} got new asserted identity:`, call.getRemoteAssertedIdentity());
@ -634,7 +634,7 @@ export default class LegacyCallHandler extends EventEmitter {
const newMappedRoomId = this.roomIdForCall(call); const newMappedRoomId = this.roomIdForCall(call);
logger.log(`Old room ID: ${mappedRoomId}, new room ID: ${newMappedRoomId}`); logger.log(`Old room ID: ${mappedRoomId}, new room ID: ${newMappedRoomId}`);
if (newMappedRoomId !== mappedRoomId) { if (newMappedRoomId && newMappedRoomId !== mappedRoomId) {
this.removeCallForRoom(mappedRoomId); this.removeCallForRoom(mappedRoomId);
mappedRoomId = newMappedRoomId; mappedRoomId = newMappedRoomId;
logger.log("Moving call to room " + mappedRoomId); logger.log("Moving call to room " + mappedRoomId);
@ -1116,6 +1116,14 @@ export default class LegacyCallHandler extends EventEmitter {
public async startTransferToMatrixID(call: MatrixCall, destination: string, consultFirst: boolean): Promise<void> { public async startTransferToMatrixID(call: MatrixCall, destination: string, consultFirst: boolean): Promise<void> {
if (consultFirst) { if (consultFirst) {
const dmRoomId = await ensureDMExists(MatrixClientPeg.get(), destination); const dmRoomId = await ensureDMExists(MatrixClientPeg.get(), destination);
if (!dmRoomId) {
logger.log("Failed to transfer call, could not ensure dm exists");
Modal.createDialog(ErrorDialog, {
title: _t("Transfer Failed"),
description: _t("Failed to transfer call"),
});
return;
}
this.placeCall(dmRoomId, call.type, call); this.placeCall(dmRoomId, call.type, call);
dis.dispatch<ViewRoomPayload>({ dis.dispatch<ViewRoomPayload>({

View file

@ -176,7 +176,7 @@ export async function loadSession(opts: ILoadSessionOpts = {}): Promise<boolean>
*/ */
export async function getStoredSessionOwner(): Promise<[string, boolean] | [null, null]> { export async function getStoredSessionOwner(): Promise<[string, boolean] | [null, null]> {
const { hsUrl, userId, hasAccessToken, isGuest } = await getStoredSessionVars(); const { hsUrl, userId, hasAccessToken, isGuest } = await getStoredSessionVars();
return hsUrl && userId && hasAccessToken ? [userId, isGuest] : [null, null]; return hsUrl && userId && hasAccessToken ? [userId, !!isGuest] : [null, null];
} }
/** /**
@ -343,9 +343,9 @@ export interface IStoredSession {
* may not be valid, as it is not tested for consistency here. * may not be valid, as it is not tested for consistency here.
* @returns {Object} Information about the session - see implementation for variables. * @returns {Object} Information about the session - see implementation for variables.
*/ */
export async function getStoredSessionVars(): Promise<IStoredSession> { export async function getStoredSessionVars(): Promise<Partial<IStoredSession>> {
const hsUrl = localStorage.getItem(HOMESERVER_URL_KEY); const hsUrl = localStorage.getItem(HOMESERVER_URL_KEY) ?? undefined;
const isUrl = localStorage.getItem(ID_SERVER_URL_KEY); const isUrl = localStorage.getItem(ID_SERVER_URL_KEY) ?? undefined;
let accessToken: string | undefined; let accessToken: string | undefined;
try { try {
accessToken = await StorageManager.idbLoad("account", "mx_access_token"); accessToken = await StorageManager.idbLoad("account", "mx_access_token");
@ -367,8 +367,8 @@ export async function getStoredSessionVars(): Promise<IStoredSession> {
// if we pre-date storing "mx_has_access_token", but we retrieved an access // if we pre-date storing "mx_has_access_token", but we retrieved an access
// token, then we should say we have an access token // token, then we should say we have an access token
const hasAccessToken = localStorage.getItem("mx_has_access_token") === "true" || !!accessToken; const hasAccessToken = localStorage.getItem("mx_has_access_token") === "true" || !!accessToken;
const userId = localStorage.getItem("mx_user_id"); const userId = localStorage.getItem("mx_user_id") ?? undefined;
const deviceId = localStorage.getItem("mx_device_id"); const deviceId = localStorage.getItem("mx_device_id") ?? undefined;
let isGuest: boolean; let isGuest: boolean;
if (localStorage.getItem("mx_is_guest") !== null) { if (localStorage.getItem("mx_is_guest") !== null) {
@ -447,7 +447,7 @@ export async function restoreFromLocalStorage(opts?: { ignoreGuest?: boolean }):
} }
let decryptedAccessToken = accessToken; let decryptedAccessToken = accessToken;
const pickleKey = await PlatformPeg.get()?.getPickleKey(userId, deviceId); const pickleKey = await PlatformPeg.get()?.getPickleKey(userId, deviceId ?? "");
if (pickleKey) { if (pickleKey) {
logger.log("Got pickle key"); logger.log("Got pickle key");
if (typeof accessToken !== "string") { if (typeof accessToken !== "string") {
@ -740,7 +740,7 @@ export function logout(): void {
_isLoggingOut = true; _isLoggingOut = true;
const client = MatrixClientPeg.get(); const client = MatrixClientPeg.get();
PlatformPeg.get()?.destroyPickleKey(client.getSafeUserId(), client.getDeviceId()); PlatformPeg.get()?.destroyPickleKey(client.getSafeUserId(), client.getDeviceId() ?? "");
client.logout(true).then(onLoggedOut, (err) => { client.logout(true).then(onLoggedOut, (err) => {
// Just throwing an error here is going to be very unhelpful // Just throwing an error here is going to be very unhelpful
// if you're trying to log out because your server's down and // if you're trying to log out because your server's down and

View file

@ -50,7 +50,7 @@ export default class ScalarAuthClient {
} }
private writeTokenToStore(): void { private writeTokenToStore(): void {
window.localStorage.setItem("mx_scalar_token_at_" + this.apiUrl, this.scalarToken); window.localStorage.setItem("mx_scalar_token_at_" + this.apiUrl, this.scalarToken ?? "");
if (this.isDefaultManager) { if (this.isDefaultManager) {
// We remove the old token from storage to migrate upwards. This is safe // We remove the old token from storage to migrate upwards. This is safe
// to do because even if the user switches to /app when this is on /develop // to do because even if the user switches to /app when this is on /develop
@ -260,7 +260,7 @@ export default class ScalarAuthClient {
const roomId = room.roomId; const roomId = room.roomId;
const roomName = room.name; const roomName = room.name;
let url = this.uiUrl; let url = this.uiUrl;
url += "?scalar_token=" + encodeURIComponent(this.scalarToken); if (this.scalarToken) url += "?scalar_token=" + encodeURIComponent(this.scalarToken);
url += "&room_id=" + encodeURIComponent(roomId); url += "&room_id=" + encodeURIComponent(roomId);
url += "&room_name=" + encodeURIComponent(roomName); url += "&room_name=" + encodeURIComponent(roomName);
url += "&theme=" + encodeURIComponent(SettingsStore.getValue("theme")); url += "&theme=" + encodeURIComponent(SettingsStore.getValue("theme"));
@ -274,6 +274,7 @@ export default class ScalarAuthClient {
} }
public getStarterLink(starterLinkUrl: string): string { public getStarterLink(starterLinkUrl: string): string {
if (!this.scalarToken) return starterLinkUrl;
return starterLinkUrl + "?scalar_token=" + encodeURIComponent(this.scalarToken); return starterLinkUrl + "?scalar_token=" + encodeURIComponent(this.scalarToken);
} }
} }

View file

@ -69,7 +69,7 @@ export default class CountryDropdown extends React.Component<IProps, IState> {
const locale = new Intl.Locale(navigator.language ?? navigator.languages[0]); const locale = new Intl.Locale(navigator.language ?? navigator.languages[0]);
const code = locale.region ?? locale.language ?? locale.baseName; const code = locale.region ?? locale.language ?? locale.baseName;
const displayNames = new Intl.DisplayNames(["en"], { type: "region" }); const displayNames = new Intl.DisplayNames(["en"], { type: "region" });
const displayName = displayNames.of(code).toUpperCase(); const displayName = displayNames.of(code)?.toUpperCase();
defaultCountry = COUNTRIES.find( defaultCountry = COUNTRIES.find(
(c) => c.iso2 === code.toUpperCase() || c.name.toUpperCase() === displayName, (c) => c.iso2 === code.toUpperCase() || c.name.toUpperCase() === displayName,
); );

View file

@ -49,7 +49,7 @@ export default class ServerPickerDialog extends React.PureComponent<IProps, ISta
super(props); super(props);
const config = SdkConfig.get(); const config = SdkConfig.get();
this.defaultServer = config["validated_server_config"]; this.defaultServer = config["validated_server_config"]!;
const { serverConfig } = this.props; const { serverConfig } = this.props;
let otherHomeserver = ""; let otherHomeserver = "";
@ -152,11 +152,11 @@ export default class ServerPickerDialog extends React.PureComponent<IProps, ISta
private onSubmit = async (ev: SyntheticEvent): Promise<void> => { private onSubmit = async (ev: SyntheticEvent): Promise<void> => {
ev.preventDefault(); ev.preventDefault();
const valid = await this.fieldRef.current.validate({ allowEmpty: false }); const valid = await this.fieldRef.current?.validate({ allowEmpty: false });
if (!valid && !this.state.defaultChosen) { if (!valid && !this.state.defaultChosen) {
this.fieldRef.current.focus(); this.fieldRef.current?.focus();
this.fieldRef.current.validate({ allowEmpty: false, focused: true }); this.fieldRef.current?.validate({ allowEmpty: false, focused: true });
return; return;
} }

View file

@ -1089,7 +1089,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
ev.stopPropagation(); ev.stopPropagation();
ev.preventDefault(); ev.preventDefault();
if (rovingContext.state.refs.length > 0) { if (rovingContext.state.activeRef && rovingContext.state.refs.length > 0) {
let refs = rovingContext.state.refs; let refs = rovingContext.state.refs;
if (!query && !filter !== null) { if (!query && !filter !== null) {
// If the current selection is not in the recently viewed row then only include the // If the current selection is not in the recently viewed row then only include the
@ -1112,6 +1112,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
if ( if (
!query && !query &&
!filter !== null && !filter !== null &&
rovingContext.state.activeRef &&
rovingContext.state.refs.length > 0 && rovingContext.state.refs.length > 0 &&
refIsForRecentlyViewed(rovingContext.state.activeRef) refIsForRecentlyViewed(rovingContext.state.activeRef)
) { ) {

View file

@ -25,7 +25,7 @@ import AccessibleTooltipButton from "./AccessibleTooltipButton";
interface IProps { interface IProps {
children?: React.ReactNode; children?: React.ReactNode;
getTextToCopy: () => string; getTextToCopy: () => string | null;
border?: boolean; border?: boolean;
className?: string; className?: string;
} }
@ -35,7 +35,8 @@ const CopyableText: React.FC<IProps> = ({ children, getTextToCopy, border = true
const onCopyClickInternal = async (e: ButtonEvent): Promise<void> => { const onCopyClickInternal = async (e: ButtonEvent): Promise<void> => {
e.preventDefault(); e.preventDefault();
const successful = await copyPlaintext(getTextToCopy()); const text = getTextToCopy();
const successful = !!text && (await copyPlaintext(text));
setTooltip(successful ? _t("Copied!") : _t("Failed to copy")); setTooltip(successful ? _t("Copied!") : _t("Failed to copy"));
}; };

View file

@ -162,9 +162,9 @@ class EmojiPicker extends React.Component<IProps, IState> {
}; };
private keyboardNavigation(ev: React.KeyboardEvent, state: RovingState, dispatch: Dispatch<RovingAction>): void { private keyboardNavigation(ev: React.KeyboardEvent, state: RovingState, dispatch: Dispatch<RovingAction>): void {
const node = state.activeRef.current; const node = state.activeRef?.current;
const parent = node.parentElement; const parent = node?.parentElement;
if (!parent) return; if (!parent || !state.activeRef) return;
const rowIndex = Array.from(parent.children).indexOf(node); const rowIndex = Array.from(parent.children).indexOf(node);
const refIndex = state.refs.indexOf(state.activeRef); const refIndex = state.refs.indexOf(state.activeRef);
@ -173,12 +173,12 @@ class EmojiPicker extends React.Component<IProps, IState> {
switch (ev.key) { switch (ev.key) {
case Key.ARROW_LEFT: case Key.ARROW_LEFT:
focusRef = state.refs[refIndex - 1]; focusRef = state.refs[refIndex - 1];
newParent = focusRef?.current?.parentElement; newParent = focusRef?.current?.parentElement ?? undefined;
break; break;
case Key.ARROW_RIGHT: case Key.ARROW_RIGHT:
focusRef = state.refs[refIndex + 1]; focusRef = state.refs[refIndex + 1];
newParent = focusRef?.current?.parentElement; newParent = focusRef?.current?.parentElement ?? undefined;
break; break;
case Key.ARROW_UP: case Key.ARROW_UP:
@ -188,7 +188,7 @@ class EmojiPicker extends React.Component<IProps, IState> {
ev.key === Key.ARROW_UP ev.key === Key.ARROW_UP
? state.refs[refIndex - rowIndex - 1] ? state.refs[refIndex - rowIndex - 1]
: state.refs[refIndex - rowIndex + EMOJIS_PER_ROW]; : state.refs[refIndex - rowIndex + EMOJIS_PER_ROW];
newParent = ref?.current?.parentElement; newParent = ref?.current?.parentElement ?? undefined;
const newTarget = newParent?.children[clamp(rowIndex, 0, newParent.children.length - 1)]; const newTarget = newParent?.children[clamp(rowIndex, 0, newParent.children.length - 1)];
focusRef = state.refs.find((r) => r.current === newTarget); focusRef = state.refs.find((r) => r.current === newTarget);
break; break;

View file

@ -386,8 +386,8 @@ class EditMessageComposer extends React.Component<IEditMessageComposerProps, ISt
// in case we're currently editing a pending event // in case we're currently editing a pending event
const sel = document.getSelection()!; const sel = document.getSelection()!;
let caret: DocumentOffset | undefined; let caret: DocumentOffset | undefined;
if (sel.focusNode) { if (sel.focusNode && this.editorRef.current?.editorRef.current) {
caret = getCaretOffsetAndText(this.editorRef.current?.editorRef.current, sel).caret; caret = getCaretOffsetAndText(this.editorRef.current.editorRef.current, sel).caret;
} }
const parts = this.model.serializeParts(); const parts = this.model.serializeParts();
// if caret is undefined because for some reason there isn't a valid selection, // if caret is undefined because for some reason there isn't a valid selection,
@ -458,12 +458,15 @@ class EditMessageComposer extends React.Component<IEditMessageComposerProps, ISt
}; };
public render(): React.ReactNode { public render(): React.ReactNode {
const room = this.getRoom();
if (!room) return null;
return ( return (
<div className={classNames("mx_EditMessageComposer", this.props.className)} onKeyDown={this.onKeyDown}> <div className={classNames("mx_EditMessageComposer", this.props.className)} onKeyDown={this.onKeyDown}>
<BasicMessageComposer <BasicMessageComposer
ref={this.editorRef} ref={this.editorRef}
model={this.model} model={this.model}
room={this.getRoom()} room={room}
threadId={this.props.editState?.getEvent()?.getThread()?.id} threadId={this.props.editState?.getEvent()?.getThread()?.id}
initialCaret={this.props.editState.getCaret() ?? undefined} initialCaret={this.props.editState.getCaret() ?? undefined}
label={_t("Edit message")} label={_t("Edit message")}

View file

@ -16,7 +16,7 @@ limitations under the License.
import { EventType, RoomType } from "matrix-js-sdk/src/@types/event"; import { EventType, RoomType } from "matrix-js-sdk/src/@types/event";
import { Room } from "matrix-js-sdk/src/models/room"; import { Room } from "matrix-js-sdk/src/models/room";
import React, { ComponentType, createRef, ReactComponentElement, RefObject, SyntheticEvent } from "react"; import React, { ComponentType, createRef, ReactComponentElement, SyntheticEvent } from "react";
import { IState as IRovingTabIndexState, RovingTabIndexProvider } from "../../../accessibility/RovingTabIndex"; import { IState as IRovingTabIndexState, RovingTabIndexProvider } from "../../../accessibility/RovingTabIndex";
import MatrixClientContext from "../../../contexts/MatrixClientContext"; import MatrixClientContext from "../../../contexts/MatrixClientContext";
@ -106,8 +106,8 @@ type TagAestheticsMap = Partial<{
[tagId in TagID]: ITagAesthetics; [tagId in TagID]: ITagAesthetics;
}>; }>;
const auxButtonContextMenuPosition = (handle: RefObject<HTMLDivElement>): MenuProps => { const auxButtonContextMenuPosition = (handle: HTMLDivElement): MenuProps => {
const rect = handle.current.getBoundingClientRect(); const rect = handle.getBoundingClientRect();
return { return {
chevronFace: ChevronFace.None, chevronFace: ChevronFace.None,
left: rect.left - 7, left: rect.left - 7,
@ -126,11 +126,11 @@ const DmAuxButton: React.FC<IAuxButtonProps> = ({ tabIndex, dispatcher = default
if (activeSpace && (showCreateRooms || showInviteUsers)) { if (activeSpace && (showCreateRooms || showInviteUsers)) {
let contextMenu: JSX.Element | undefined; let contextMenu: JSX.Element | undefined;
if (menuDisplayed) { if (menuDisplayed && handle.current) {
const canInvite = shouldShowSpaceInvite(activeSpace); const canInvite = shouldShowSpaceInvite(activeSpace);
contextMenu = ( contextMenu = (
<IconizedContextMenu {...auxButtonContextMenuPosition(handle)} onFinished={closeMenu} compact> <IconizedContextMenu {...auxButtonContextMenuPosition(handle.current)} onFinished={closeMenu} compact>
<IconizedContextMenuOptionList first> <IconizedContextMenuOptionList first>
{showCreateRooms && ( {showCreateRooms && (
<IconizedContextMenuOption <IconizedContextMenuOption
@ -357,9 +357,9 @@ const UntaggedAuxButton: React.FC<IAuxButtonProps> = ({ tabIndex }) => {
} }
let contextMenu: JSX.Element | null = null; let contextMenu: JSX.Element | null = null;
if (menuDisplayed) { if (menuDisplayed && handle.current) {
contextMenu = ( contextMenu = (
<IconizedContextMenu {...auxButtonContextMenuPosition(handle)} onFinished={closeMenu} compact> <IconizedContextMenu {...auxButtonContextMenuPosition(handle.current)} onFinished={closeMenu} compact>
{contextMenuContent} {contextMenuContent}
</IconizedContextMenu> </IconizedContextMenu>
); );
@ -491,6 +491,7 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
if (payload.action === Action.ViewRoomDelta) { if (payload.action === Action.ViewRoomDelta) {
const viewRoomDeltaPayload = payload as ViewRoomDeltaPayload; const viewRoomDeltaPayload = payload as ViewRoomDeltaPayload;
const currentRoomId = SdkContextClass.instance.roomViewStore.getRoomId(); const currentRoomId = SdkContextClass.instance.roomViewStore.getRoomId();
if (!currentRoomId) return;
const room = this.getRoomDelta(currentRoomId, viewRoomDeltaPayload.delta, viewRoomDeltaPayload.unread); const room = this.getRoomDelta(currentRoomId, viewRoomDeltaPayload.delta, viewRoomDeltaPayload.unread);
if (room) { if (room) {
defaultDispatcher.dispatch<ViewRoomPayload>({ defaultDispatcher.dispatch<ViewRoomPayload>({

View file

@ -207,7 +207,8 @@ export class RoomTile extends React.PureComponent<ClassProps, State> {
return; return;
} }
const messagePreview = await MessagePreviewStore.instance.getPreviewForRoom(this.props.room, this.props.tag); const messagePreview =
(await MessagePreviewStore.instance.getPreviewForRoom(this.props.room, this.props.tag)) ?? undefined;
this.setState({ messagePreview }); this.setState({ messagePreview });
} }

View file

@ -38,7 +38,7 @@ export function usePlainTextListeners(
onChange?: (content: string) => void, onChange?: (content: string) => void,
onSend?: () => void, onSend?: () => void,
): { ): {
ref: RefObject<HTMLDivElement | null>; ref: RefObject<HTMLDivElement>;
content?: string; content?: string;
onInput(event: SyntheticEvent<HTMLDivElement, InputEvent | ClipboardEvent>): void; onInput(event: SyntheticEvent<HTMLDivElement, InputEvent | ClipboardEvent>): void;
onPaste(event: SyntheticEvent<HTMLDivElement, InputEvent | ClipboardEvent>): void; onPaste(event: SyntheticEvent<HTMLDivElement, InputEvent | ClipboardEvent>): void;

View file

@ -295,7 +295,7 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> {
const verify = (sub: string): JSX.Element => ( const verify = (sub: string): JSX.Element => (
<span <span
className={ className={
sig.device && sig.deviceTrust.isVerified() sig.device && sig.deviceTrust?.isVerified()
? "mx_SecureBackupPanel_deviceVerified" ? "mx_SecureBackupPanel_deviceVerified"
: "mx_SecureBackupPanel_deviceNotVerified" : "mx_SecureBackupPanel_deviceNotVerified"
} }
@ -347,7 +347,7 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> {
{}, {},
{ validity }, { validity },
); );
} else if (sig.valid && sig.deviceTrust.isVerified()) { } else if (sig.valid && sig.deviceTrust?.isVerified()) {
sigStatus = _t( sigStatus = _t(
"Backup has a <validity>valid</validity> signature from " + "Backup has a <validity>valid</validity> signature from " +
"<verify>verified</verify> session <device></device>", "<verify>verified</verify> session <device></device>",
@ -361,7 +361,7 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> {
{}, {},
{ validity, verify, device }, { validity, verify, device },
); );
} else if (!sig.valid && sig.deviceTrust.isVerified()) { } else if (!sig.valid && sig.deviceTrust?.isVerified()) {
sigStatus = _t( sigStatus = _t(
"Backup has an <validity>invalid</validity> signature from " + "Backup has an <validity>invalid</validity> signature from " +
"<verify>verified</verify> session <device></device>", "<verify>verified</verify> session <device></device>",

View file

@ -67,7 +67,7 @@ export default class LabsUserSettingsTab extends React.Component<{}> {
const groups = new EnhancedMap<LabGroup, JSX.Element[]>(); const groups = new EnhancedMap<LabGroup, JSX.Element[]>();
this.labs.forEach((f) => { this.labs.forEach((f) => {
groups groups
.getOrCreate(SettingsStore.getLabGroup(f), []) .getOrCreate(SettingsStore.getLabGroup(f)!, [])
.push(<SettingsFlag level={SettingLevel.DEVICE} name={f} key={f} />); .push(<SettingsFlag level={SettingLevel.DEVICE} name={f} key={f} />);
}); });

View file

@ -68,14 +68,14 @@ const QuickSettingsButton: React.FC<{
{_t("All settings")} {_t("All settings")}
</AccessibleButton> </AccessibleButton>
{SettingsStore.getValue("developerMode") && ( {SettingsStore.getValue("developerMode") && SdkContextClass.instance.roomViewStore.getRoomId() && (
<AccessibleButton <AccessibleButton
onClick={() => { onClick={() => {
closeMenu(); closeMenu();
Modal.createDialog( Modal.createDialog(
DevtoolsDialog, DevtoolsDialog,
{ {
roomId: SdkContextClass.instance.roomViewStore.getRoomId(), roomId: SdkContextClass.instance.roomViewStore.getRoomId()!,
}, },
"mx_DevtoolsDialog_wrapper", "mx_DevtoolsDialog_wrapper",
); );

View file

@ -122,7 +122,7 @@ export const SpaceButton = forwardRef<HTMLElement, IButtonProps>(
} }
let contextMenu: JSX.Element | undefined; let contextMenu: JSX.Element | undefined;
if (menuDisplayed && handle.current && ContextMenuComponent) { if (space && menuDisplayed && handle.current && ContextMenuComponent) {
contextMenu = ( contextMenu = (
<ContextMenuComponent <ContextMenuComponent
{...toRightOf(handle.current.getBoundingClientRect(), 0)} {...toRightOf(handle.current.getBoundingClientRect(), 0)}

View file

@ -74,10 +74,12 @@ export default class VerificationRequestToast extends React.PureComponent<IProps
if (request.isSelfVerification) { if (request.isSelfVerification) {
const cli = MatrixClientPeg.get(); const cli = MatrixClientPeg.get();
const device = await cli.getDevice(request.channel.deviceId); const device = request.channel.deviceId ? await cli.getDevice(request.channel.deviceId) : null;
const ip = device.last_seen_ip; const ip = device?.last_seen_ip;
this.setState({ this.setState({
device: cli.getStoredDevice(cli.getUserId()!, request.channel.deviceId) ?? undefined, device:
(request.channel.deviceId && cli.getStoredDevice(cli.getSafeUserId(), request.channel.deviceId)) ||
undefined,
ip, ip,
}); });
} }

View file

@ -42,7 +42,10 @@ function getSecretStorageKey(): Uint8Array | null {
} }
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */ /* eslint-disable-next-line @typescript-eslint/no-unused-vars */
function getDehydrationKey(keyInfo: ISecretStorageKeyInfo): Promise<Uint8Array | null> { function getDehydrationKey(
keyInfo: ISecretStorageKeyInfo,
checkFunc: (key: Uint8Array) => void,
): Promise<Uint8Array | null> {
return Promise.resolve(null); return Promise.resolve(null);
} }

View file

@ -153,7 +153,7 @@ export class IntegrationManagers {
if (kind === Kind.Account) { if (kind === Kind.Account) {
// Order by state_keys (IDs) // Order by state_keys (IDs)
managers.sort((a, b) => compare(a.id, b.id)); managers.sort((a, b) => compare(a.id ?? "", b.id ?? ""));
} }
ordered.push(...managers); ordered.push(...managers);
@ -199,7 +199,7 @@ export class IntegrationManagers {
logger.log("Looking up integration manager via .well-known"); logger.log("Looking up integration manager via .well-known");
if (domainName.startsWith("http:") || domainName.startsWith("https:")) { if (domainName.startsWith("http:") || domainName.startsWith("https:")) {
// trim off the scheme and just use the domain // trim off the scheme and just use the domain
domainName = url.parse(domainName).host; domainName = url.parse(domainName).host!;
} }
let wkConfig: IClientWellKnown; let wkConfig: IClientWellKnown;

View file

@ -16,7 +16,7 @@ limitations under the License.
*/ */
import * as linkifyjs from "linkifyjs"; import * as linkifyjs from "linkifyjs";
import { Opts, registerCustomProtocol, registerPlugin } from "linkifyjs"; import { EventListeners, Opts, registerCustomProtocol, registerPlugin } from "linkifyjs";
import linkifyElement from "linkify-element"; import linkifyElement from "linkify-element";
import linkifyString from "linkify-string"; import linkifyString from "linkify-string";
import { User } from "matrix-js-sdk/src/matrix"; import { User } from "matrix-js-sdk/src/matrix";
@ -136,7 +136,7 @@ export const ELEMENT_URL_PATTERN =
")(#.*)"; ")(#.*)";
export const options: Opts = { export const options: Opts = {
events: function (href: string, type: string): Partial<GlobalEventHandlers> { events: function (href: string, type: string): EventListeners {
switch (type as Type) { switch (type as Type) {
case Type.URL: { case Type.URL: {
// intercept local permalinks to users and show them like userids (in userinfo of current room) // intercept local permalinks to users and show them like userids (in userinfo of current room)
@ -185,9 +185,11 @@ export const options: Opts = {
}, },
}; };
} }
return {};
}, },
formatHref: function (href: string, type: Type | string): string { formatHref: function (href: string, type: Type | string): string | null {
switch (type) { switch (type) {
case Type.RoomAlias: case Type.RoomAlias:
case Type.UserId: case Type.UserId:
@ -205,7 +207,7 @@ export const options: Opts = {
className: "linkified", className: "linkified",
target: function (href: string, type: Type | string): string { target: function (href: string, type: Type | string): string | null {
if (type === Type.URL) { if (type === Type.URL) {
try { try {
const transformed = tryTransformPermalinkToLocalHref(href); const transformed = tryTransformPermalinkToLocalHref(href);

View file

@ -228,6 +228,7 @@ export class RoomViewStore extends EventEmitter {
} }
private doMaybeSetCurrentVoiceBroadcastPlayback(room: Room): void { private doMaybeSetCurrentVoiceBroadcastPlayback(room: Room): void {
if (!this.stores.client) return;
doMaybeSetCurrentVoiceBroadcastPlayback( doMaybeSetCurrentVoiceBroadcastPlayback(
room, room,
this.stores.client, this.stores.client,
@ -532,8 +533,8 @@ export class RoomViewStore extends EventEmitter {
const cli = MatrixClientPeg.get(); const cli = MatrixClientPeg.get();
// take a copy of roomAlias & roomId as they may change by the time the join is complete // take a copy of roomAlias & roomId as they may change by the time the join is complete
const { roomAlias, roomId } = this.state; const { roomAlias, roomId = payload.roomId } = this.state;
const address = roomAlias || roomId; const address = roomAlias || roomId!;
const viaServers = this.state.viaServers || []; const viaServers = this.state.viaServers || [];
try { try {
await retry<Room, MatrixError>( await retry<Room, MatrixError>(
@ -554,7 +555,7 @@ export class RoomViewStore extends EventEmitter {
// room. // room.
this.dis.dispatch<JoinRoomReadyPayload>({ this.dis.dispatch<JoinRoomReadyPayload>({
action: Action.JoinRoomReady, action: Action.JoinRoomReady,
roomId, roomId: roomId!,
metricsTrigger: payload.metricsTrigger, metricsTrigger: payload.metricsTrigger,
}); });
} catch (err) { } catch (err) {

View file

@ -83,7 +83,11 @@ export default class ThreepidInviteStore extends EventEmitter {
for (let i = 0; i < localStorage.length; i++) { for (let i = 0; i < localStorage.length; i++) {
const keyName = localStorage.key(i); const keyName = localStorage.key(i);
if (!keyName?.startsWith(STORAGE_PREFIX)) continue; if (!keyName?.startsWith(STORAGE_PREFIX)) continue;
results.push(JSON.parse(localStorage.getItem(keyName)) as IPersistedThreepidInvite); try {
results.push(JSON.parse(localStorage.getItem(keyName)!) as IPersistedThreepidInvite);
} catch (e) {
console.warn("Failed to parse 3pid invite", e);
}
} }
return results; return results;
} }

View file

@ -144,7 +144,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
private _activeSpace: SpaceKey = MetaSpace.Home; // set properly by onReady private _activeSpace: SpaceKey = MetaSpace.Home; // set properly by onReady
private _suggestedRooms: ISuggestedRoom[] = []; private _suggestedRooms: ISuggestedRoom[] = [];
private _invitedSpaces = new Set<Room>(); private _invitedSpaces = new Set<Room>();
private spaceOrderLocalEchoMap = new Map<string, string>(); private spaceOrderLocalEchoMap = new Map<string, string | undefined>();
// The following properties are set by onReady as they live in account_data // The following properties are set by onReady as they live in account_data
private _allRoomsInHome = false; private _allRoomsInHome = false;
private _enabledMetaSpaces: MetaSpace[] = []; private _enabledMetaSpaces: MetaSpace[] = [];
@ -338,7 +338,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
}; };
public addRoomToSpace(space: Room, roomId: string, via: string[], suggested = false): Promise<ISendEventResponse> { public addRoomToSpace(space: Room, roomId: string, via: string[], suggested = false): Promise<ISendEventResponse> {
return this.matrixClient.sendStateEvent( return this.matrixClient!.sendStateEvent(
space.roomId, space.roomId,
EventType.SpaceChild, EventType.SpaceChild,
{ {
@ -359,7 +359,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
return getChildOrder(ev.getContent().order, ev.getTs(), ev.getStateKey()!); return getChildOrder(ev.getContent().order, ev.getTs(), ev.getStateKey()!);
}) })
.map((ev) => { .map((ev) => {
const history = this.matrixClient.getRoomUpgradeHistory( const history = this.matrixClient!.getRoomUpgradeHistory(
ev.getStateKey()!, ev.getStateKey()!,
true, true,
this._msc3946ProcessDynamicPredecessor, this._msc3946ProcessDynamicPredecessor,
@ -463,7 +463,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
): Set<string> => { ): Set<string> => {
if (space === MetaSpace.Home && this.allRoomsInHome) { if (space === MetaSpace.Home && this.allRoomsInHome) {
return new Set( return new Set(
this.matrixClient.getVisibleRooms(this._msc3946ProcessDynamicPredecessor).map((r) => r.roomId), this.matrixClient!.getVisibleRooms(this._msc3946ProcessDynamicPredecessor).map((r) => r.roomId),
); );
} }
@ -612,8 +612,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
this.roomIdsBySpace.delete(MetaSpace.Home); this.roomIdsBySpace.delete(MetaSpace.Home);
} else { } else {
const rooms = new Set( const rooms = new Set(
this.matrixClient this.matrixClient!.getVisibleRooms(this._msc3946ProcessDynamicPredecessor)
.getVisibleRooms(this._msc3946ProcessDynamicPredecessor)
.filter(this.showInHomeSpace) .filter(this.showInHomeSpace)
.map((r) => r.roomId), .map((r) => r.roomId),
); );
@ -813,9 +812,11 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
// Expand room IDs to all known versions of the given rooms // Expand room IDs to all known versions of the given rooms
const expandedRoomIds = new Set( const expandedRoomIds = new Set(
Array.from(roomIds).flatMap((roomId) => { Array.from(roomIds).flatMap((roomId) => {
return this.matrixClient return this.matrixClient!.getRoomUpgradeHistory(
.getRoomUpgradeHistory(roomId, true, this._msc3946ProcessDynamicPredecessor) roomId,
.map((r) => r.roomId); true,
this._msc3946ProcessDynamicPredecessor,
).map((r) => r.roomId);
}), }),
); );
@ -1217,7 +1218,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
// Persist last viewed room from a space // Persist last viewed room from a space
// we don't await setActiveSpace above as we only care about this.activeSpace being up to date // we don't await setActiveSpace above as we only care about this.activeSpace being up to date
// synchronously for the below code - everything else can and should be async. // synchronously for the below code - everything else can and should be async.
window.localStorage.setItem(getSpaceContextKey(this.activeSpace), payload.room_id); window.localStorage.setItem(getSpaceContextKey(this.activeSpace), payload.room_id ?? "");
break; break;
} }
@ -1294,10 +1295,12 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
} }
case "Spaces.showPeopleInSpace": case "Spaces.showPeopleInSpace":
// getSpaceFilteredUserIds will return the appropriate value if (payload.roomId) {
this.emit(payload.roomId); // getSpaceFilteredUserIds will return the appropriate value
if (!this.enabledMetaSpaces.some((s) => s === MetaSpace.Home || s === MetaSpace.People)) { this.emit(payload.roomId);
this.updateNotificationStates([payload.roomId]); if (!this.enabledMetaSpaces.some((s) => s === MetaSpace.Home || s === MetaSpace.People)) {
this.updateNotificationStates([payload.roomId]);
}
} }
break; break;
@ -1353,7 +1356,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
return sortBy(spaces, [this.getSpaceTagOrdering, "roomId"]); return sortBy(spaces, [this.getSpaceTagOrdering, "roomId"]);
} }
private async setRootSpaceOrder(space: Room, order: string): Promise<void> { private async setRootSpaceOrder(space: Room, order?: string): Promise<void> {
this.spaceOrderLocalEchoMap.set(space.roomId, order); this.spaceOrderLocalEchoMap.set(space.roomId, order);
try { try {
await this.matrixClient?.setRoomAccountData(space.roomId, EventType.SpaceOrder, { order }); await this.matrixClient?.setRoomAccountData(space.roomId, EventType.SpaceOrder, { order });