Conform more of the codebase to strictNullChecks (#10800)

pull/28788/head^2
Michael Telatynski 2023-05-10 08:41:55 +01:00 committed by GitHub
parent adb29b38a3
commit 456c66db5b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 147 additions and 123 deletions

View File

@ -652,9 +652,9 @@ export default class LegacyCallHandler extends EventEmitter {
}
private onCallStateChanged = (newState: CallState, oldState: CallState | null, call: MatrixCall): void => {
if (!this.matchesCallForThisRoom(call)) return;
const mappedRoomId = this.roomIdForCall(call);
if (!mappedRoomId || !this.matchesCallForThisRoom(call)) return;
this.setCallState(call, newState);
dis.dispatch({
action: "call_state",

View File

@ -60,7 +60,7 @@ export class PlaybackClock implements IDestroyable {
private stopped = true;
private lastCheck = 0;
private observable = new SimpleObservable<number[]>();
private timerId: number;
private timerId?: number;
private clipDuration = 0;
private placeholderDuration = 0;

View File

@ -33,9 +33,9 @@ export interface IUpload {
* This class can be used to record a single voice message.
*/
export class VoiceMessageRecording implements IDestroyable {
private lastUpload: IUpload;
private lastUpload?: IUpload;
private buffer = new Uint8Array(0); // use this.audioBuffer to access
private playback: Playback;
private playback?: Playback;
public constructor(private matrixClient: MatrixClient, private voiceRecording: VoiceRecording) {
this.voiceRecording.onDataAvailable = this.onDataAvailable;

View File

@ -243,7 +243,7 @@ export class VoiceRecording extends EventEmitter implements IDestroyable {
return;
}
const secondsLeft = TARGET_MAX_LENGTH - this.recorderSeconds;
const secondsLeft = TARGET_MAX_LENGTH - this.recorderSeconds!;
if (secondsLeft < 0) {
// go over to make sure we definitely capture that last frame
// noinspection JSIgnoredPromiseFromCall - we aren't concerned with it overlapping
@ -259,7 +259,8 @@ export class VoiceRecording extends EventEmitter implements IDestroyable {
/**
* {@link https://github.com/chris-rudmin/opus-recorder#instance-fields ref for recorderSeconds}
*/
public get recorderSeconds(): number {
public get recorderSeconds(): number | undefined {
if (!this.recorder) return undefined;
return this.recorder.encodedSamplePosition / 48000;
}

View File

@ -44,7 +44,7 @@ export default class IndicatorScrollbar<T extends keyof JSX.IntrinsicElements> e
IState
> {
private autoHideScrollbar = createRef<AutoHideScrollbar<any>>();
private scrollElement: HTMLDivElement;
private scrollElement?: HTMLDivElement;
private likelyTrackpadUser: boolean | null = null;
private checkAgainForTrackpad = 0; // ts in milliseconds to recheck this._likelyTrackpadUser
@ -85,6 +85,7 @@ export default class IndicatorScrollbar<T extends keyof JSX.IntrinsicElements> e
}
private checkOverflow = (): void => {
if (!this.scrollElement) return;
const hasTopOverflow = this.scrollElement.scrollTop > 0;
const hasBottomOverflow =
this.scrollElement.scrollHeight > this.scrollElement.scrollTop + this.scrollElement.clientHeight;

View File

@ -73,7 +73,7 @@ export function buildLegacyCallEventGroupers(
export default class LegacyCallEventGrouper extends EventEmitter {
private events: Set<MatrixEvent> = new Set<MatrixEvent>();
private call: MatrixCall | null = null;
public state: CallState | CustomCallState;
public state?: CallState | CustomCallState;
public constructor() {
super();

View File

@ -96,10 +96,10 @@ interface IProps {
autoJoin?: boolean;
threepidInvite?: IThreepidInvite;
roomOobData?: IOOBData;
currentRoomId: string;
currentRoomId: string | null;
collapseLhs: boolean;
config: ConfigOptions;
currentUserId: string;
currentUserId: string | null;
justRegistered?: boolean;
roomJustCreatedOpts?: IOpts;
forceTimeline?: boolean; // see props on MatrixChat
@ -131,10 +131,10 @@ class LoggedInView extends React.Component<IProps, IState> {
protected readonly _roomView: React.RefObject<RoomViewType>;
protected readonly _resizeContainer: React.RefObject<HTMLDivElement>;
protected readonly resizeHandler: React.RefObject<HTMLDivElement>;
protected layoutWatcherRef: string;
protected compactLayoutWatcherRef: string;
protected backgroundImageWatcherRef: string;
protected resizer: Resizer;
protected layoutWatcherRef?: string;
protected compactLayoutWatcherRef?: string;
protected backgroundImageWatcherRef?: string;
protected resizer?: Resizer;
public constructor(props: IProps) {
super(props);
@ -200,10 +200,10 @@ class LoggedInView extends React.Component<IProps, IState> {
this._matrixClient.removeListener(ClientEvent.Sync, this.onSync);
this._matrixClient.removeListener(RoomStateEvent.Events, this.onRoomStateEvents);
OwnProfileStore.instance.off(UPDATE_EVENT, this.refreshBackgroundImage);
SettingsStore.unwatchSetting(this.layoutWatcherRef);
SettingsStore.unwatchSetting(this.compactLayoutWatcherRef);
SettingsStore.unwatchSetting(this.backgroundImageWatcherRef);
this.resizer.detach();
if (this.layoutWatcherRef) SettingsStore.unwatchSetting(this.layoutWatcherRef);
if (this.compactLayoutWatcherRef) SettingsStore.unwatchSetting(this.compactLayoutWatcherRef);
if (this.backgroundImageWatcherRef) SettingsStore.unwatchSetting(this.backgroundImageWatcherRef);
this.resizer?.detach();
}
private onCallState = (): void => {
@ -274,7 +274,7 @@ class LoggedInView extends React.Component<IProps, IState> {
if (isNaN(lhsSize)) {
lhsSize = 350;
}
this.resizer.forHandleWithId("lp-resizer")?.resize(lhsSize);
this.resizer?.forHandleWithId("lp-resizer")?.resize(lhsSize);
}
private onAccountData = (event: MatrixEvent): void => {
@ -645,7 +645,11 @@ class LoggedInView extends React.Component<IProps, IState> {
break;
case PageTypes.UserView:
pageElement = <UserView userId={this.props.currentUserId} resizeNotifier={this.props.resizeNotifier} />;
if (!!this.props.currentUserId) {
pageElement = (
<UserView userId={this.props.currentUserId} resizeNotifier={this.props.resizeNotifier} />
);
}
break;
}

View File

@ -231,7 +231,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
private focusComposer: boolean;
private subTitleStatus: string;
private prevWindowWidth: number;
private voiceBroadcastResumer: VoiceBroadcastResumer;
private voiceBroadcastResumer?: VoiceBroadcastResumer;
private readonly loggedInView: React.RefObject<LoggedInViewType>;
private readonly dispatcherRef: string;
@ -441,7 +441,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
window.removeEventListener("resize", this.onWindowResized);
this.stores.accountPasswordStore.clearPassword();
if (this.voiceBroadcastResumer) this.voiceBroadcastResumer.destroy();
this.voiceBroadcastResumer?.destroy();
}
private onWindowResized = (): void => {
@ -1935,7 +1935,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
private setPageSubtitle(subtitle = ""): void {
if (this.state.currentRoomId) {
const client = MatrixClientPeg.get();
const room = client && client.getRoom(this.state.currentRoomId);
const room = client?.getRoom(this.state.currentRoomId);
if (room) {
subtitle = `${this.subTitleStatus} | ${room.name} ${subtitle}`;
}

View File

@ -179,7 +179,7 @@ export default class ScrollPanel extends React.Component<IProps> {
};
private readonly itemlist = createRef<HTMLOListElement>();
private unmounted = false;
private scrollTimeout: Timer;
private scrollTimeout?: Timer;
// Are we currently trying to backfill?
private isFilling: boolean;
// Is the current fill request caused by a props update?
@ -189,8 +189,8 @@ export default class ScrollPanel extends React.Component<IProps> {
// Is that next fill request scheduled because of a props update?
private pendingFillDueToPropsUpdate: boolean;
private scrollState: IScrollState;
private preventShrinkingState: IPreventShrinkingState | null;
private unfillDebouncer: number | null;
private preventShrinkingState: IPreventShrinkingState | null = null;
private unfillDebouncer: number | null = null;
private bottomGrowth: number;
private minListHeight: number;
private heightUpdateInProgress: boolean;
@ -234,7 +234,7 @@ export default class ScrollPanel extends React.Component<IProps> {
// skip scroll events caused by resizing
if (this.props.resizeNotifier && this.props.resizeNotifier.isResizing) return;
debuglog("onScroll called past resize gate; scroll node top:", this.getScrollNode().scrollTop);
this.scrollTimeout.restart();
this.scrollTimeout?.restart();
this.saveScrollState();
this.updatePreventShrinking();
this.props.onScroll?.(ev);
@ -725,7 +725,7 @@ export default class ScrollPanel extends React.Component<IProps> {
// need a better name that also indicates this will change scrollTop? Rebalance height? Reveal content?
private async updateHeight(): Promise<void> {
// wait until user has stopped scrolling
if (this.scrollTimeout.isRunning()) {
if (this.scrollTimeout?.isRunning()) {
debuglog("updateHeight waiting for scrolling to end ... ");
await this.scrollTimeout.finished();
debuglog("updateHeight actually running now");

View File

@ -88,9 +88,9 @@ export default class UserMenu extends React.Component<IProps, IState> {
public static contextType = SDKContext;
public context!: React.ContextType<typeof SDKContext>;
private dispatcherRef: string;
private themeWatcherRef: string;
private readonly dndWatcherRef: string;
private dispatcherRef?: string;
private themeWatcherRef?: string;
private readonly dndWatcherRef?: string;
private buttonRef: React.RefObject<HTMLButtonElement> = createRef();
public constructor(props: IProps, context: React.ContextType<typeof SDKContext>) {

View File

@ -125,7 +125,7 @@ interface IState {
export default class Registration extends React.Component<IProps, IState> {
private readonly loginLogic: Login;
// `replaceClient` tracks latest serverConfig to spot when it changes under the async method which fetches flows
private latestServerConfig: ValidatedServerConfig;
private latestServerConfig?: ValidatedServerConfig;
public constructor(props: IProps) {
super(props);

View File

@ -556,8 +556,8 @@ export class MsisdnAuthEntry extends React.Component<IMsisdnAuthEntryProps, IMsi
public static LOGIN_TYPE = AuthType.Msisdn;
private submitUrl?: string;
private sid: string;
private msisdn: string;
private sid?: string;
private msisdn?: string;
public constructor(props: IMsisdnAuthEntryProps) {
super(props);
@ -615,8 +615,8 @@ export class MsisdnAuthEntry extends React.Component<IMsisdnAuthEntryProps, IMsi
});
try {
let result;
if (this.submitUrl) {
let result: { success: boolean };
if (this.submitUrl && this.sid) {
result = await this.props.matrixClient.submitMsisdnTokenOtherUrl(
this.submitUrl,
this.sid,

View File

@ -19,7 +19,7 @@ import React, { ChangeEvent } from "react";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { logger } from "matrix-js-sdk/src/logger";
import { _t } from "../../../languageHandler";
import { _t, UserFriendlyError } from "../../../languageHandler";
import { ensureDMExists } from "../../../createRoom";
import { MatrixClientPeg } from "../../../MatrixClientPeg";
import SdkConfig from "../../../SdkConfig";
@ -245,6 +245,10 @@ export default class ReportEventDialog extends React.Component<IProps, IState> {
// Report to moderators through to the dedicated bot,
// as configured in the room's state events.
const dmRoomId = await ensureDMExists(client, this.moderation.moderationBotUserId);
if (!dmRoomId) {
throw new UserFriendlyError("Unable to create room with moderation bot");
}
await client.sendEvent(dmRoomId, ABUSE_EVENT_TYPE, {
event_id: ev.getId(),
room_id: ev.getRoomId(),

View File

@ -62,7 +62,7 @@ interface IState {
}
class RoomSettingsDialog extends React.Component<IProps, IState> {
private dispatcherRef: string;
private dispatcherRef?: string;
public constructor(props: IProps) {
super(props);

View File

@ -35,7 +35,7 @@ interface IState {
}
export default class RoomUpgradeDialog extends React.Component<IProps, IState> {
private targetVersion: string;
private targetVersion?: string;
public state = {
busy: true,
@ -53,7 +53,7 @@ export default class RoomUpgradeDialog extends React.Component<IProps, IState> {
private onUpgradeClick = (): void => {
this.setState({ busy: true });
upgradeRoom(this.props.room, this.targetVersion, false, false)
upgradeRoom(this.props.room, this.targetVersion!, false, false)
.then(() => {
this.props.onFinished(true);
})
@ -69,7 +69,7 @@ export default class RoomUpgradeDialog extends React.Component<IProps, IState> {
};
public render(): React.ReactNode {
let buttons;
let buttons: JSX.Element;
if (this.state.busy) {
buttons = <Spinner />;
} else {

View File

@ -44,7 +44,7 @@ interface IState {
export default class ServerPickerDialog extends React.PureComponent<IProps, IState> {
private readonly defaultServer: ValidatedServerConfig;
private readonly fieldRef = createRef<Field>();
private validatedConf: ValidatedServerConfig;
private validatedConf?: ValidatedServerConfig;
public constructor(props: IProps) {
super(props);
@ -85,7 +85,7 @@ export default class ServerPickerDialog extends React.PureComponent<IProps, ISta
// find their homeserver without demanding they use "https://matrix.org"
private validate = withValidation<this, { error?: string }>({
deriveData: async ({ value }): Promise<{ error?: string }> => {
let hsUrl = value.trim(); // trim to account for random whitespace
let hsUrl = (value ?? "").trim(); // trim to account for random whitespace
// if the URL has no protocol, try validate it as a serverName via well-known
if (!hsUrl.includes("://")) {

View File

@ -45,7 +45,7 @@ interface IState {
* On success, `onFinished(true)` is called.
*/
export default class SetEmailDialog extends React.Component<IProps, IState> {
private addThreepid: AddThreepid;
private addThreepid?: AddThreepid;
public constructor(props: IProps) {
super(props);
@ -109,7 +109,7 @@ export default class SetEmailDialog extends React.Component<IProps, IState> {
};
private verifyEmailAddress(): void {
this.addThreepid.checkEmailLinkClicked().then(
this.addThreepid?.checkEmailLinkClicked().then(
() => {
this.props.onFinished(true);
},

View File

@ -121,11 +121,11 @@ export default class AppTile extends React.Component<IProps, IState> {
};
private contextMenuButton = createRef<any>();
private iframe: HTMLIFrameElement; // ref to the iframe (callback style)
private allowedWidgetsWatchRef: string;
private iframe?: HTMLIFrameElement; // ref to the iframe (callback style)
private allowedWidgetsWatchRef?: string;
private persistKey: string;
private sgWidget: StopGapWidget | null;
private dispatcherRef: string;
private dispatcherRef?: string;
private unmounted: boolean;
public constructor(props: IProps) {
@ -305,7 +305,7 @@ export default class AppTile extends React.Component<IProps, IState> {
this.context.off(RoomEvent.MyMembership, this.onMyMembership);
}
SettingsStore.unwatchSetting(this.allowedWidgetsWatchRef);
if (this.allowedWidgetsWatchRef) SettingsStore.unwatchSetting(this.allowedWidgetsWatchRef);
OwnProfileStore.instance.removeListener(UPDATE_EVENT, this.onUserReady);
}
@ -344,7 +344,7 @@ export default class AppTile extends React.Component<IProps, IState> {
private startMessaging(): void {
try {
this.sgWidget?.startMessaging(this.iframe);
this.sgWidget?.startMessaging(this.iframe!);
} catch (e) {
logger.error("Failed to start widget", e);
}

View File

@ -88,7 +88,7 @@ export interface PickerIProps {
type TabId = "screen" | "window";
export default class DesktopCapturerSourcePicker extends React.Component<PickerIProps, PickerIState> {
public interval: number;
public interval?: number;
public constructor(props: PickerIProps) {
super(props);

View File

@ -75,8 +75,8 @@ interface IProps {
export default class PersistedElement extends React.Component<IProps> {
private resizeObserver: ResizeObserver;
private dispatcherRef: string;
private childContainer: HTMLDivElement;
private child: HTMLDivElement;
private childContainer?: HTMLDivElement;
private child?: HTMLDivElement;
public constructor(props: IProps) {
super(props);
@ -172,12 +172,12 @@ export default class PersistedElement extends React.Component<IProps> {
ReactDOM.render(content, getOrCreateContainer("mx_persistedElement_" + this.props.persistKey));
}
private updateChildVisibility(child: HTMLDivElement, visible: boolean): void {
private updateChildVisibility(child?: HTMLDivElement, visible = false): void {
if (!child) return;
child.style.display = visible ? "block" : "none";
}
private updateChildPosition(child: HTMLDivElement, parent: HTMLDivElement): void {
private updateChildPosition(child?: HTMLDivElement, parent?: HTMLDivElement): void {
if (!child || !parent) return;
const parentRect = parent.getBoundingClientRect();

View File

@ -17,5 +17,5 @@ limitations under the License.
import { MediaEventHelper } from "../../../utils/MediaEventHelper";
export interface IMediaBody {
getMediaHelper(): MediaEventHelper;
getMediaHelper(): MediaEventHelper | undefined;
}

View File

@ -48,7 +48,7 @@ interface IState {
export default class LegacyCallEvent extends React.PureComponent<IProps, IState> {
private wrapperElement = createRef<HTMLDivElement>();
private resizeObserver: ResizeObserver;
private resizeObserver?: ResizeObserver;
public constructor(props: IProps) {
super(props);
@ -75,7 +75,7 @@ export default class LegacyCallEvent extends React.PureComponent<IProps, IState>
this.props.callEventGrouper.removeListener(LegacyCallEventGrouperEvent.SilencedChanged, this.onSilencedChanged);
this.props.callEventGrouper.removeListener(LegacyCallEventGrouperEvent.LengthChanged, this.onLengthChanged);
this.resizeObserver.disconnect();
this.resizeObserver?.disconnect();
}
private onLengthChanged = (length: number): void => {

View File

@ -69,7 +69,7 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
private unmounted = true;
private image = createRef<HTMLImageElement>();
private timeout?: number;
private sizeWatcher: string;
private sizeWatcher?: string;
private reconnectedListener: ClientEventHandlerMap[ClientEvent.Sync];
public constructor(props: IBodyProps) {
@ -367,7 +367,7 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
this.unmounted = true;
MatrixClientPeg.get().off(ClientEvent.Sync, this.reconnectedListener);
this.clearBlurhashTimeout();
SettingsStore.unwatchSetting(this.sizeWatcher);
if (this.sizeWatcher) SettingsStore.unwatchSetting(this.sizeWatcher);
if (this.state.isAnimated && this.state.thumbUrl) {
URL.revokeObjectURL(this.state.thumbUrl);
}

View File

@ -45,7 +45,7 @@ export default class MVideoBody extends React.PureComponent<IBodyProps, IState>
public context!: React.ContextType<typeof RoomContext>;
private videoRef = React.createRef<HTMLVideoElement>();
private sizeWatcher: string;
private sizeWatcher?: string;
public constructor(props: IBodyProps) {
super(props);
@ -187,7 +187,7 @@ export default class MVideoBody extends React.PureComponent<IBodyProps, IState>
}
public componentWillUnmount(): void {
SettingsStore.unwatchSetting(this.sizeWatcher);
if (this.sizeWatcher) SettingsStore.unwatchSetting(this.sizeWatcher);
}
private videoOnPlay = async (): Promise<void> => {

View File

@ -82,7 +82,7 @@ const baseEvTypes = new Map<string, React.ComponentType<Partial<IBodyProps>>>([
export default class MessageEvent extends React.Component<IProps> implements IMediaBody, IOperableEventTile {
private body: React.RefObject<React.Component | IOperableEventTile> = createRef();
private mediaHelper: MediaEventHelper;
private mediaHelper?: MediaEventHelper;
private bodyTypes = new Map<string, typeof React.Component>(baseBodyTypes.entries());
private evTypes = new Map<string, React.ComponentType<Partial<IBodyProps>>>(baseEvTypes.entries());
@ -133,7 +133,7 @@ export default class MessageEvent extends React.Component<IProps> implements IMe
return (this.body.current as IOperableEventTile)?.getEventTileOps?.() || null;
};
public getMediaHelper(): MediaEventHelper {
public getMediaHelper(): MediaEventHelper | undefined {
return this.mediaHelper;
}

View File

@ -57,9 +57,9 @@ interface IState {
export default class AppsDrawer extends React.Component<IProps, IState> {
private unmounted = false;
private resizeContainer: HTMLDivElement;
private resizeContainer?: HTMLDivElement;
private resizer: Resizer;
private dispatcherRef: string;
private dispatcherRef?: string;
public static defaultProps: Partial<IProps> = {
showApps: true,
};
@ -113,11 +113,11 @@ export default class AppsDrawer extends React.Component<IProps, IState> {
};
const collapseConfig = {
onResizeStart: () => {
this.resizeContainer.classList.add("mx_AppsDrawer_resizing");
this.resizeContainer?.classList.add("mx_AppsDrawer_resizing");
this.setState({ resizingHorizontal: true });
},
onResizeStop: () => {
this.resizeContainer.classList.remove("mx_AppsDrawer_resizing");
this.resizeContainer?.classList.remove("mx_AppsDrawer_resizing");
WidgetLayoutStore.instance.setResizerDistributions(
this.props.room,
Container.Top,

View File

@ -133,9 +133,9 @@ export default class Autocomplete extends React.PureComponent<IProps, IState> {
});
}
private processQuery(query: string, selection: ISelectionRange): Promise<void> {
private async processQuery(query: string, selection: ISelectionRange): Promise<void> {
return this.autocompleter
.getCompletions(query, selection, this.state.forceComplete, MAX_PROVIDER_MATCHES)
?.getCompletions(query, selection, this.state.forceComplete, MAX_PROVIDER_MATCHES)
.then((completions) => {
// Only ever process the completions for the most recent query being processed
if (query !== this.queryRequested) {

View File

@ -129,7 +129,7 @@ class EditMessageComposer extends React.Component<IEditMessageComposerProps, ISt
private readonly editorRef = createRef<BasicMessageComposer>();
private readonly dispatcherRef: string;
private readonly replyToEvent?: MatrixEvent;
private model: EditorModel;
private model!: EditorModel;
public constructor(props: IEditMessageComposerProps, context: React.ContextType<typeof RoomContext>) {
super(props);

View File

@ -46,8 +46,8 @@ interface IState {
}
export default class MemberTile extends React.Component<IProps, IState> {
private userLastModifiedTime: number;
private memberLastModifiedTime: number;
private userLastModifiedTime?: number;
private memberLastModifiedTime?: number;
public static defaultProps = {
showPresence: true,

View File

@ -443,7 +443,7 @@ const TAG_AESTHETICS: TagAestheticsMap = {
export default class RoomList extends React.PureComponent<IProps, IState> {
private dispatcherRef?: string;
private treeRef = createRef<HTMLDivElement>();
private favouriteMessageWatcher: string;
private favouriteMessageWatcher?: string;
public static contextType = MatrixClientContext;
public context!: React.ContextType<typeof MatrixClientContext>;
@ -476,7 +476,7 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
public componentWillUnmount(): void {
SpaceStore.instance.off(UPDATE_SUGGESTED_ROOMS, this.updateSuggestedRooms);
RoomListStore.instance.off(LISTS_UPDATE_EVENT, this.updateLists);
SettingsStore.unwatchSetting(this.favouriteMessageWatcher);
if (this.favouriteMessageWatcher) SettingsStore.unwatchSetting(this.favouriteMessageWatcher);
if (this.dispatcherRef) defaultDispatcher.unregister(this.dispatcherRef);
SdkContextClass.instance.roomViewStore.off(UPDATE_EVENT, this.onRoomViewStoreUpdate);
}

View File

@ -215,8 +215,10 @@ export default class RoomPreviewBar extends React.Component<IProps, IState> {
if (!myMember) {
return {};
}
const kickerMember = this.props.room?.currentState.getMember(myMember.events.member.getSender());
const memberName = kickerMember?.name ?? myMember.events.member?.getSender();
const kickerUserId = myMember.events.member?.getSender();
const kickerMember = kickerUserId ? this.props.room?.currentState.getMember(kickerUserId) : undefined;
const memberName = kickerMember?.name ?? kickerUserId;
const reason = myMember.events.member?.getContent().reason;
return { memberName, reason };
}
@ -559,7 +561,7 @@ export default class RoomPreviewBar extends React.Component<IProps, IState> {
"%(errcode)s was returned while trying to access the room or space. " +
"If you think you're seeing this message in error, please " +
"<issueLink>submit a bug report</issueLink>.",
{ errcode: this.props.error.errcode },
{ errcode: String(this.props.error?.errcode) },
{
issueLink: (label) => (
<a

View File

@ -110,7 +110,7 @@ export default class RoomSublist extends React.Component<IProps, IState> {
private headerButton = createRef<HTMLDivElement>();
private sublistRef = createRef<HTMLDivElement>();
private tilesRef = createRef<HTMLDivElement>();
private dispatcherRef: string;
private dispatcherRef?: string;
private layout: ListLayout;
private heightAtStart: number;
private notificationState: ListNotificationState;
@ -257,7 +257,7 @@ export default class RoomSublist extends React.Component<IProps, IState> {
}
public componentWillUnmount(): void {
defaultDispatcher.unregister(this.dispatcherRef);
if (this.dispatcherRef) defaultDispatcher.unregister(this.dispatcherRef);
RoomListStore.instance.off(LISTS_UPDATE_EVENT, this.onListsUpdated);
RoomListStore.instance.off(LISTS_LOADING_EVENT, this.onListsLoading);
this.tilesRef.current?.removeEventListener("scroll", this.onScrollPrevent);

View File

@ -23,7 +23,7 @@ import AppTile from "../elements/AppTile";
import { MatrixClientPeg } from "../../../MatrixClientPeg";
import dis from "../../../dispatcher/dispatcher";
import AccessibleButton from "../elements/AccessibleButton";
import WidgetUtils, { IWidgetEvent } from "../../../utils/WidgetUtils";
import WidgetUtils, { UserWidget } from "../../../utils/WidgetUtils";
import PersistedElement from "../elements/PersistedElement";
import { IntegrationManagers } from "../../../integrations/IntegrationManagers";
import ContextMenu, { ChevronFace } from "../../structures/ContextMenu";
@ -53,7 +53,7 @@ interface IProps {
interface IState {
imError: string | null;
stickerpickerWidget: IWidgetEvent | null;
stickerpickerWidget: UserWidget | null;
widgetId: string | null;
}
@ -62,7 +62,7 @@ export default class Stickerpicker extends React.PureComponent<IProps, IState> {
threadId: null,
};
public static currentWidget?: IWidgetEvent;
public static currentWidget?: UserWidget;
private dispatcherRef?: string;
@ -252,14 +252,14 @@ export default class Stickerpicker extends React.PureComponent<IProps, IState> {
// Render content from multiple stickerpack sources, each within their
// own iframe, within the stickerpicker UI element.
const stickerpickerWidget = this.state.stickerpickerWidget;
let stickersContent;
let stickersContent: JSX.Element | undefined;
// Use a separate ReactDOM tree to render the AppTile separately so that it persists and does
// not unmount when we (a) close the sticker picker (b) switch rooms. It's properties are still
// updated.
// Load stickerpack content
if (stickerpickerWidget && stickerpickerWidget.content && stickerpickerWidget.content.url) {
if (!!stickerpickerWidget?.content?.url) {
// Set default name
stickerpickerWidget.content.name = stickerpickerWidget.content.name || _t("Stickerpack");

View File

@ -69,9 +69,9 @@ interface IState {
}
export default class ChangePassword extends React.Component<IProps, IState> {
private [FIELD_OLD_PASSWORD]: Field | null;
private [FIELD_NEW_PASSWORD]: Field | null;
private [FIELD_NEW_PASSWORD_CONFIRM]: Field | null;
private [FIELD_OLD_PASSWORD]: Field | null = null;
private [FIELD_NEW_PASSWORD]: Field | null = null;
private [FIELD_NEW_PASSWORD_CONFIRM]: Field | null = null;
public static defaultProps: Partial<IProps> = {
onFinished() {},

View File

@ -43,7 +43,7 @@ interface IState {
}
export default class IntegrationManager extends React.Component<IProps, IState> {
private dispatcherRef: string;
private dispatcherRef?: string;
public static defaultProps: Partial<IProps> = {
connected: true,
@ -60,7 +60,7 @@ export default class IntegrationManager extends React.Component<IProps, IState>
}
public componentWillUnmount(): void {
dis.unregister(this.dispatcherRef);
if (this.dispatcherRef) dis.unregister(this.dispatcherRef);
document.removeEventListener("keydown", this.onKeyDown);
}

View File

@ -81,7 +81,7 @@ interface IState {
}
export default class SetIdServer extends React.Component<IProps, IState> {
private dispatcherRef: string;
private dispatcherRef?: string;
public constructor(props: IProps) {
super(props);
@ -108,7 +108,7 @@ export default class SetIdServer extends React.Component<IProps, IState> {
}
public componentWillUnmount(): void {
dis.unregister(this.dispatcherRef);
if (this.dispatcherRef) dis.unregister(this.dispatcherRef);
}
private onAction = (payload: ActionPayload): void => {

View File

@ -20,6 +20,7 @@ import { Optional } from "matrix-events-sdk";
import { MatrixClientPeg } from "../MatrixClientPeg";
import { IMediaEventContent, IPreparedMedia, prepEventContentAsMedia } from "./models/IMediaEventContent";
import { UserFriendlyError } from "../languageHandler";
// Populate this class with the details of your customisations when copying it.
@ -141,7 +142,11 @@ export class Media {
* @returns {Promise<Response>} Resolves to the server's response for chaining.
*/
public downloadSource(): Promise<Response> {
return fetch(this.srcHttp);
const src = this.srcHttp;
if (!src) {
throw new UserFriendlyError("Failed to download source media, no source url was found");
}
return fetch(src);
}
}

View File

@ -1130,6 +1130,7 @@
"You can use <code>/help</code> to list available commands. Did you mean to send this as a message?": "You can use <code>/help</code> to list available commands. Did you mean to send this as a message?",
"Hint: Begin your message with <code>//</code> to start it with a slash.": "Hint: Begin your message with <code>//</code> to start it with a slash.",
"Send as message": "Send as message",
"Failed to download source media, no source url was found": "Failed to download source media, no source url was found",
"Audio devices": "Audio devices",
"Mute microphone": "Mute microphone",
"Unmute microphone": "Unmute microphone",
@ -2975,6 +2976,7 @@
"Just a heads up, if you don't add an email and forget your password, you could <b>permanently lose access to your account</b>.": "Just a heads up, if you don't add an email and forget your password, you could <b>permanently lose access to your account</b>.",
"Email (optional)": "Email (optional)",
"Please fill why you're reporting.": "Please fill why you're reporting.",
"Unable to create room with moderation bot": "Unable to create room with moderation bot",
"Ignore user": "Ignore user",
"Check if you want to hide all current and future messages from this user.": "Check if you want to hide all current and future messages from this user.",
"What this user is writing is wrong.\nThis will be reported to the room moderators.": "What this user is writing is wrong.\nThis will be reported to the room moderators.",

View File

@ -150,8 +150,8 @@ export class RoomViewStore extends EventEmitter {
// another RVS via INITIAL_STATE as they share the same underlying object. Mostly relevant for tests.
private state = utils.deepCopy(INITIAL_STATE);
private dis: MatrixDispatcher;
private dispatchToken: string;
private dis?: MatrixDispatcher;
private dispatchToken?: string;
public constructor(dis: MatrixDispatcher, private readonly stores: SdkContextClass) {
super();
@ -217,7 +217,7 @@ export class RoomViewStore extends EventEmitter {
// Fired so we can reduce dependency on event emitters to this store, which is relatively
// central to the application and can easily cause import cycles.
this.dis.dispatch<ActiveRoomChangedPayload>({
this.dis?.dispatch<ActiveRoomChangedPayload>({
action: Action.ActiveRoomChanged,
oldRoomId: lastRoomId,
newRoomId: this.state.roomId,
@ -343,7 +343,7 @@ export class RoomViewStore extends EventEmitter {
// can happen when performing a search across all rooms. Persist the data from this event for both
// room and search timeline rendering types, search will get auto-closed by RoomView at this time.
if (payload.event && payload.event.getRoomId() !== this.state.roomId) {
this.dis.dispatch<ViewRoomPayload>({
this.dis?.dispatch<ViewRoomPayload>({
action: Action.ViewRoom,
room_id: payload.event.getRoomId(),
replyingToEvent: payload.event,
@ -414,7 +414,7 @@ export class RoomViewStore extends EventEmitter {
return;
}
// Re-fire the payload: we won't re-process it because the prev room ID == payload room ID now
this.dis.dispatch({
this.dis?.dispatch({
...payload,
});
return;
@ -455,7 +455,7 @@ export class RoomViewStore extends EventEmitter {
this.setState(newState);
if (payload.auto_join) {
this.dis.dispatch<JoinRoomPayload>({
this.dis?.dispatch<JoinRoomPayload>({
...payload,
action: Action.JoinRoom,
roomId: payload.room_id,
@ -493,7 +493,7 @@ export class RoomViewStore extends EventEmitter {
roomId = result.room_id;
} catch (err) {
logger.error("RVS failed to get room id for alias: ", err);
this.dis.dispatch<ViewRoomErrorPayload>({
this.dis?.dispatch<ViewRoomErrorPayload>({
action: Action.ViewRoomError,
room_id: null,
room_alias: payload.room_alias,
@ -504,7 +504,7 @@ export class RoomViewStore extends EventEmitter {
}
// Re-fire the payload with the newly found room_id
this.dis.dispatch({
this.dis?.dispatch({
...payload,
room_id: roomId,
});
@ -553,13 +553,13 @@ export class RoomViewStore extends EventEmitter {
// We do *not* clear the 'joining' flag because the Room object and/or our 'joined' member event may not
// have come down the sync stream yet, and that's the point at which we'd consider the user joined to the
// room.
this.dis.dispatch<JoinRoomReadyPayload>({
this.dis?.dispatch<JoinRoomReadyPayload>({
action: Action.JoinRoomReady,
roomId: roomId!,
metricsTrigger: payload.metricsTrigger,
});
} catch (err) {
this.dis.dispatch({
this.dis?.dispatch({
action: Action.JoinRoomError,
roomId,
err,
@ -648,7 +648,7 @@ export class RoomViewStore extends EventEmitter {
*/
public resetDispatcher(dis: MatrixDispatcher): void {
if (this.dispatchToken) {
this.dis.unregister(this.dispatchToken);
this.dis?.unregister(this.dispatchToken);
}
this.dis = dis;
if (dis) {

View File

@ -44,7 +44,7 @@ export enum Phase {
}
export class SetupEncryptionStore extends EventEmitter {
private started: boolean;
private started?: boolean;
public phase: Phase;
public verificationRequest: VerificationRequest | null = null;
public backupInfo: IKeyBackupInfo | null = null;
@ -52,7 +52,7 @@ export class SetupEncryptionStore extends EventEmitter {
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 hasDevicesToVerifyAgainst?: boolean;
public static sharedInstance(): SetupEncryptionStore {
if (!window.mxSetupEncryptionStore) window.mxSetupEncryptionStore = new SetupEncryptionStore();

View File

@ -32,7 +32,7 @@ export default class TypingStore {
userTimer: Timer;
serverTimer: Timer;
};
};
} = {};
public constructor(private readonly context: SdkContextClass) {
this.reset();
@ -68,7 +68,7 @@ export default class TypingStore {
if (threadId) return;
let currentTyping = this.typingStates[roomId];
if ((!isTyping && !currentTyping) || (currentTyping && currentTyping.isTyping === isTyping)) {
if ((!isTyping && !currentTyping) || currentTyping?.isTyping === isTyping) {
// No change in state, so don't do anything. We'll let the timer run its course.
return;
}

View File

@ -157,9 +157,9 @@ export class ElementWidget extends Widget {
export class StopGapWidget extends EventEmitter {
private client: MatrixClient;
private messaging: ClientWidgetApi | null;
private messaging: ClientWidgetApi | null = null;
private mockWidget: ElementWidget;
private scalarToken: string;
private scalarToken?: string;
private roomId?: string;
private kind: WidgetKind;
private readonly virtual: boolean;

View File

@ -39,7 +39,7 @@ export default class DMRoomMap {
private roomToUser: { [key: string]: string } | null = null;
private userToRooms: { [key: string]: string[] } | null = null;
private hasSentOutPatchDirectAccountDataPatch: boolean;
private mDirectEvent: { [key: string]: string[] };
private mDirectEvent!: { [key: string]: string[] };
public constructor(private readonly matrixClient: MatrixClient) {
// see onAccountData

View File

@ -47,7 +47,11 @@ export interface IWidgetEvent {
sender: string;
// eslint-disable-next-line camelcase
state_key: string;
content: Partial<IApp>;
content: IApp;
}
export interface UserWidget extends Omit<IWidgetEvent, "content"> {
content: IWidget & Partial<IApp>;
}
export default class WidgetUtils {
@ -254,6 +258,7 @@ export default class WidgetUtils {
const userId = client.getSafeUserId();
const content = {
id: widgetId,
type: widgetType.preferred,
url: widgetUrl,
name: widgetName,
@ -354,7 +359,7 @@ export default class WidgetUtils {
* Get user specific widgets (not linked to a specific room)
* @return {object} Event content object containing current / active user widgets
*/
public static getUserWidgets(): Record<string, IWidgetEvent> {
public static getUserWidgets(): Record<string, UserWidget> {
const client = MatrixClientPeg.get();
if (!client) {
throw new Error("User not logged in");
@ -370,7 +375,7 @@ export default class WidgetUtils {
* Get user specific widgets (not linked to a specific room) as an array
* @return {[object]} Array containing current / active user widgets
*/
public static getUserWidgetsArray(): IWidgetEvent[] {
public static getUserWidgetsArray(): UserWidget[] {
return Object.values(WidgetUtils.getUserWidgets());
}
@ -378,18 +383,18 @@ export default class WidgetUtils {
* Get active stickerpicker widgets (stickerpickers are user widgets by nature)
* @return {[object]} Array containing current / active stickerpicker widgets
*/
public static getStickerpickerWidgets(): IWidgetEvent[] {
public static getStickerpickerWidgets(): UserWidget[] {
const widgets = WidgetUtils.getUserWidgetsArray();
return widgets.filter((widget) => widget.content && widget.content.type === "m.stickerpicker");
return widgets.filter((widget) => widget.content?.type === "m.stickerpicker");
}
/**
* Get all integration manager widgets for this user.
* @returns {Object[]} An array of integration manager user widgets.
*/
public static getIntegrationManagerWidgets(): IWidgetEvent[] {
public static getIntegrationManagerWidgets(): UserWidget[] {
const widgets = WidgetUtils.getUserWidgetsArray();
return widgets.filter((w) => w.content && w.content.type === "m.integration_manager");
return widgets.filter((w) => w.content?.type === "m.integration_manager");
}
public static getRoomWidgetsOfType(room: Room, type: WidgetType): MatrixEvent[] {

View File

@ -126,7 +126,7 @@ export class VoiceBroadcastRecorder
return;
}
this.setCurrentChunkLength(this.voiceRecording.recorderSeconds - this.previousChunkEndTimePosition);
this.setCurrentChunkLength(this.voiceRecording.recorderSeconds! - this.previousChunkEndTimePosition);
this.handleData(dataArray);
};
@ -154,7 +154,7 @@ export class VoiceBroadcastRecorder
return null;
}
const currentRecorderTime = this.voiceRecording.recorderSeconds;
const currentRecorderTime = this.voiceRecording.recorderSeconds!;
const payload: ChunkRecordedPayload = {
buffer: concat(this.opusHead!, this.opusTags!, this.chunkBuffer),
length: this.getCurrentChunkLength(),

View File

@ -21,8 +21,8 @@ import { SettingLevel } from "../../../src/settings/SettingLevel";
function makeMatchMedia(values: any) {
class FakeMediaQueryList {
matches: false;
media: null;
onchange: null;
media?: null;
onchange?: null;
addListener() {}
removeListener() {}
addEventListener() {}