Conform more of the codebase to strictNullChecks
(#10350
* Conform more of the codebase to `strictNullChecks` * Iterate * Generics ftw * Iterate
This commit is contained in:
parent
d53e91802d
commit
127a3b667c
53 changed files with 279 additions and 263 deletions
|
@ -29,7 +29,7 @@ export type IProps<T extends keyof JSX.IntrinsicElements> = Omit<DynamicHtmlElem
|
|||
onWheel?: (event: WheelEvent) => void;
|
||||
style?: React.CSSProperties;
|
||||
tabIndex?: number;
|
||||
wrappedRef?: (ref: HTMLDivElement) => void;
|
||||
wrappedRef?: (ref: HTMLDivElement | null) => void;
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
|
|
|
@ -99,7 +99,7 @@ class FilePanel extends React.Component<IProps, IState> {
|
|||
|
||||
const timeline = this.state.timelineSet.getLiveTimeline();
|
||||
if (ev.getType() !== "m.room.message") return;
|
||||
if (["m.file", "m.image", "m.video", "m.audio"].indexOf(ev.getContent().msgtype) == -1) {
|
||||
if (!["m.file", "m.image", "m.video", "m.audio"].includes(ev.getContent().msgtype!)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -176,7 +176,7 @@ class FilePanel extends React.Component<IProps, IState> {
|
|||
// the event index to fulfill the pagination request. Asking the server
|
||||
// to paginate won't ever work since the server can't correctly filter
|
||||
// out events containing URLs
|
||||
if (client.isRoomEncrypted(roomId) && eventIndex !== null) {
|
||||
if (room && client.isRoomEncrypted(roomId) && eventIndex !== null) {
|
||||
return eventIndex.paginateTimelineWindow(room, timelineWindow, direction, limit);
|
||||
} else {
|
||||
return timelineWindow.paginate(direction, limit);
|
||||
|
|
|
@ -177,19 +177,20 @@ export function GenericDropdownMenu<T>({
|
|||
</>
|
||||
);
|
||||
}
|
||||
const contextMenu = menuDisplayed ? (
|
||||
<ContextMenu
|
||||
onFinished={closeMenu}
|
||||
chevronFace={ChevronFace.Top}
|
||||
wrapperClassName={classNames("mx_GenericDropdownMenu_wrapper", className)}
|
||||
{...aboveLeftOf(button.current.getBoundingClientRect())}
|
||||
>
|
||||
{contextMenuOptions}
|
||||
{AdditionalOptions && (
|
||||
<AdditionalOptions menuDisplayed={menuDisplayed} openMenu={openMenu} closeMenu={closeMenu} />
|
||||
)}
|
||||
</ContextMenu>
|
||||
) : null;
|
||||
const contextMenu =
|
||||
menuDisplayed && button.current ? (
|
||||
<ContextMenu
|
||||
onFinished={closeMenu}
|
||||
chevronFace={ChevronFace.Top}
|
||||
wrapperClassName={classNames("mx_GenericDropdownMenu_wrapper", className)}
|
||||
{...aboveLeftOf(button.current.getBoundingClientRect())}
|
||||
>
|
||||
{contextMenuOptions}
|
||||
{AdditionalOptions && (
|
||||
<AdditionalOptions menuDisplayed={menuDisplayed} openMenu={openMenu} closeMenu={closeMenu} />
|
||||
)}
|
||||
</ContextMenu>
|
||||
) : null;
|
||||
return (
|
||||
<>
|
||||
<ContextMenuButton
|
||||
|
|
|
@ -44,7 +44,7 @@ export default class IndicatorScrollbar<T extends keyof JSX.IntrinsicElements> e
|
|||
> {
|
||||
private autoHideScrollbar = createRef<AutoHideScrollbar<any>>();
|
||||
private scrollElement: HTMLDivElement;
|
||||
private likelyTrackpadUser: boolean = null;
|
||||
private likelyTrackpadUser: boolean | null = null;
|
||||
private checkAgainForTrackpad = 0; // ts in milliseconds to recheck this._likelyTrackpadUser
|
||||
|
||||
public constructor(props: IProps<T>) {
|
||||
|
|
|
@ -271,6 +271,7 @@ export default class LeftPanel extends React.Component<IProps, IState> {
|
|||
// add appropriate sticky classes to wrapper so it has
|
||||
// the necessary top/bottom padding to put the sticky header in
|
||||
const listWrapper = list.parentElement; // .mx_LeftPanel_roomListWrapper
|
||||
if (!listWrapper) return;
|
||||
if (lastTopHeader) {
|
||||
listWrapper.classList.add("mx_LeftPanel_roomListWrapper_stickyTop");
|
||||
} else {
|
||||
|
|
|
@ -85,23 +85,23 @@ export default class LegacyCallEventGrouper extends EventEmitter {
|
|||
);
|
||||
}
|
||||
|
||||
private get invite(): MatrixEvent {
|
||||
private get invite(): MatrixEvent | undefined {
|
||||
return [...this.events].find((event) => event.getType() === EventType.CallInvite);
|
||||
}
|
||||
|
||||
private get hangup(): MatrixEvent {
|
||||
private get hangup(): MatrixEvent | undefined {
|
||||
return [...this.events].find((event) => event.getType() === EventType.CallHangup);
|
||||
}
|
||||
|
||||
private get reject(): MatrixEvent {
|
||||
private get reject(): MatrixEvent | undefined {
|
||||
return [...this.events].find((event) => event.getType() === EventType.CallReject);
|
||||
}
|
||||
|
||||
private get selectAnswer(): MatrixEvent {
|
||||
private get selectAnswer(): MatrixEvent | undefined {
|
||||
return [...this.events].find((event) => event.getType() === EventType.CallSelectAnswer);
|
||||
}
|
||||
|
||||
public get isVoice(): boolean {
|
||||
public get isVoice(): boolean | undefined {
|
||||
const invite = this.invite;
|
||||
if (!invite) return;
|
||||
|
||||
|
@ -114,7 +114,7 @@ export default class LegacyCallEventGrouper extends EventEmitter {
|
|||
return this.call?.hangupReason ?? this.hangup?.getContent()?.reason ?? null;
|
||||
}
|
||||
|
||||
public get rejectParty(): string {
|
||||
public get rejectParty(): string | undefined {
|
||||
return this.reject?.getSender();
|
||||
}
|
||||
|
||||
|
|
|
@ -170,7 +170,7 @@ class LoggedInView extends React.Component<IProps, IState> {
|
|||
monitorSyncedPushRules(this._matrixClient.getAccountData("m.push_rules"), this._matrixClient);
|
||||
this._matrixClient.on(ClientEvent.Sync, this.onSync);
|
||||
// Call `onSync` with the current state as well
|
||||
this.onSync(this._matrixClient.getSyncState(), null, this._matrixClient.getSyncStateData());
|
||||
this.onSync(this._matrixClient.getSyncState(), null, this._matrixClient.getSyncStateData() ?? undefined);
|
||||
this._matrixClient.on(RoomStateEvent.Events, this.onRoomStateEvents);
|
||||
|
||||
this.layoutWatcherRef = SettingsStore.watchSetting("layout", null, this.onCompactLayoutChanged);
|
||||
|
@ -271,11 +271,11 @@ class LoggedInView extends React.Component<IProps, IState> {
|
|||
}
|
||||
|
||||
private loadResizerPreferences(): void {
|
||||
let lhsSize = parseInt(window.localStorage.getItem("mx_lhs_size"), 10);
|
||||
let lhsSize = parseInt(window.localStorage.getItem("mx_lhs_size")!, 10);
|
||||
if (isNaN(lhsSize)) {
|
||||
lhsSize = 350;
|
||||
}
|
||||
this.resizer.forHandleWithId("lp-resizer").resize(lhsSize);
|
||||
this.resizer.forHandleWithId("lp-resizer")?.resize(lhsSize);
|
||||
}
|
||||
|
||||
private onAccountData = (event: MatrixEvent): void => {
|
||||
|
@ -291,13 +291,13 @@ class LoggedInView extends React.Component<IProps, IState> {
|
|||
});
|
||||
};
|
||||
|
||||
private onSync = (syncState: SyncState, oldSyncState?: SyncState, data?: ISyncStateData): void => {
|
||||
private onSync = (syncState: SyncState | null, oldSyncState: SyncState | null, data?: ISyncStateData): void => {
|
||||
const oldErrCode = (this.state.syncErrorData?.error as MatrixError)?.errcode;
|
||||
const newErrCode = (data?.error as MatrixError)?.errcode;
|
||||
if (syncState === oldSyncState && oldErrCode === newErrCode) return;
|
||||
|
||||
this.setState({
|
||||
syncErrorData: syncState === SyncState.Error ? data : null,
|
||||
syncErrorData: syncState === SyncState.Error ? data : undefined,
|
||||
});
|
||||
|
||||
if (oldSyncState === SyncState.Prepared && syncState === SyncState.Syncing) {
|
||||
|
@ -355,7 +355,7 @@ class LoggedInView extends React.Component<IProps, IState> {
|
|||
const pinnedEventIds = pinStateEvent.getContent().pinned.slice(0, MAX_PINNED_NOTICES_PER_ROOM);
|
||||
for (const eventId of pinnedEventIds) {
|
||||
const timeline = await this._matrixClient.getEventTimeline(room.getUnfilteredTimelineSet(), eventId);
|
||||
const event = timeline.getEvents().find((ev) => ev.getId() === eventId);
|
||||
const event = timeline?.getEvents().find((ev) => ev.getId() === eventId);
|
||||
if (event) events.push(event);
|
||||
}
|
||||
}
|
||||
|
@ -390,7 +390,7 @@ class LoggedInView extends React.Component<IProps, IState> {
|
|||
if (inputableElement?.focus) {
|
||||
inputableElement.focus();
|
||||
} else {
|
||||
const inThread = !!document.activeElement.closest(".mx_ThreadView");
|
||||
const inThread = !!document.activeElement?.closest(".mx_ThreadView");
|
||||
// refocusing during a paste event will make the paste end up in the newly focused element,
|
||||
// so dispatch synchronously before paste happens
|
||||
dis.dispatch(
|
||||
|
@ -533,11 +533,11 @@ class LoggedInView extends React.Component<IProps, IState> {
|
|||
});
|
||||
break;
|
||||
case KeyBindingAction.PreviousVisitedRoomOrSpace:
|
||||
PlatformPeg.get().navigateForwardBack(true);
|
||||
PlatformPeg.get()?.navigateForwardBack(true);
|
||||
handled = true;
|
||||
break;
|
||||
case KeyBindingAction.NextVisitedRoomOrSpace:
|
||||
PlatformPeg.get().navigateForwardBack(false);
|
||||
PlatformPeg.get()?.navigateForwardBack(false);
|
||||
handled = true;
|
||||
break;
|
||||
}
|
||||
|
@ -555,7 +555,7 @@ class LoggedInView extends React.Component<IProps, IState> {
|
|||
);
|
||||
SettingsStore.setValue(
|
||||
"showHiddenEventsInTimeline",
|
||||
undefined,
|
||||
null,
|
||||
SettingLevel.DEVICE,
|
||||
!hiddenEventVisibility,
|
||||
);
|
||||
|
@ -567,7 +567,7 @@ class LoggedInView extends React.Component<IProps, IState> {
|
|||
|
||||
if (
|
||||
!handled &&
|
||||
PlatformPeg.get().overrideBrowserShortcuts() &&
|
||||
PlatformPeg.get()?.overrideBrowserShortcuts() &&
|
||||
ev.code.startsWith("Digit") &&
|
||||
ev.code !== "Digit0" && // this is the shortcut for reset zoom, don't override it
|
||||
isOnlyCtrlOrCmdKeyEvent(ev)
|
||||
|
@ -599,7 +599,7 @@ class LoggedInView extends React.Component<IProps, IState> {
|
|||
// If the user is entering a printable character outside of an input field
|
||||
// redirect it to the composer for them.
|
||||
if (!isClickShortcut && isPrintable && !getInputableElement(ev.target as HTMLElement)) {
|
||||
const inThread = !!document.activeElement.closest(".mx_ThreadView");
|
||||
const inThread = !!document.activeElement?.closest(".mx_ThreadView");
|
||||
// synchronous dispatch so we focus before key generates input
|
||||
dis.dispatch(
|
||||
{
|
||||
|
|
|
@ -187,9 +187,9 @@ interface IState {
|
|||
// The ID of the room we're viewing. This is either populated directly
|
||||
// in the case where we view a room by ID or by RoomView when it resolves
|
||||
// what ID an alias points at.
|
||||
currentRoomId?: string;
|
||||
currentRoomId: string | null;
|
||||
// If we're trying to just view a user ID (i.e. /user URL), this is it
|
||||
currentUserId?: string;
|
||||
currentUserId: string | null;
|
||||
// this is persisted as mx_lhs_size, loaded in LoggedInView
|
||||
collapseLhs: boolean;
|
||||
// Parameters used in the registration dance with the IS
|
||||
|
@ -202,7 +202,7 @@ interface IState {
|
|||
// When showing Modal dialogs we need to set aria-hidden on the root app element
|
||||
// and disable it when there are no dialogs
|
||||
hideToSRUsers: boolean;
|
||||
syncError?: Error;
|
||||
syncError: Error | null;
|
||||
resizeNotifier: ResizeNotifier;
|
||||
serverConfig?: ValidatedServerConfig;
|
||||
ready: boolean;
|
||||
|
@ -248,6 +248,8 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
this.state = {
|
||||
view: Views.LOADING,
|
||||
collapseLhs: false,
|
||||
currentRoomId: null,
|
||||
currentUserId: null,
|
||||
|
||||
hideToSRUsers: false,
|
||||
|
||||
|
@ -469,9 +471,9 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
);
|
||||
}, 1000);
|
||||
|
||||
private getFallbackHsUrl(): string {
|
||||
private getFallbackHsUrl(): string | null {
|
||||
if (this.props.serverConfig?.isDefault) {
|
||||
return this.props.config.fallback_hs_url;
|
||||
return this.props.config.fallback_hs_url ?? null;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
@ -480,7 +482,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
private getServerProperties(): { serverConfig: ValidatedServerConfig } {
|
||||
let props = this.state.serverConfig;
|
||||
if (!props) props = this.props.serverConfig; // for unit tests
|
||||
if (!props) props = SdkConfig.get("validated_server_config");
|
||||
if (!props) props = SdkConfig.get("validated_server_config")!;
|
||||
return { serverConfig: props };
|
||||
}
|
||||
|
||||
|
@ -709,7 +711,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
Modal.createDialog(
|
||||
UserSettingsDialog,
|
||||
{ initialTabId: tabPayload.initialTabId as UserTab },
|
||||
/*className=*/ null,
|
||||
/*className=*/ undefined,
|
||||
/*isPriority=*/ false,
|
||||
/*isStatic=*/ true,
|
||||
);
|
||||
|
@ -978,7 +980,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
this.setState(
|
||||
{
|
||||
view: Views.LOGGED_IN,
|
||||
currentRoomId: roomInfo.room_id || null,
|
||||
currentRoomId: roomInfo.room_id ?? null,
|
||||
page_type: PageType.RoomView,
|
||||
threepidInvite: roomInfo.threepid_invite,
|
||||
roomOobData: roomInfo.oob_data,
|
||||
|
@ -1063,7 +1065,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
|
||||
const [shouldCreate, opts] = await modal.finished;
|
||||
if (shouldCreate) {
|
||||
createRoom(opts);
|
||||
createRoom(opts!);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1122,7 +1124,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
// Show a warning if there are additional complications.
|
||||
const warnings: JSX.Element[] = [];
|
||||
|
||||
const memberCount = roomToLeave.currentState.getJoinedMemberCount();
|
||||
const memberCount = roomToLeave?.currentState.getJoinedMemberCount();
|
||||
if (memberCount === 1) {
|
||||
warnings.push(
|
||||
<span className="warning" key="only_member_warning">
|
||||
|
@ -1137,7 +1139,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
return warnings;
|
||||
}
|
||||
|
||||
const joinRules = roomToLeave.currentState.getStateEvents("m.room.join_rules", "");
|
||||
const joinRules = roomToLeave?.currentState.getStateEvents("m.room.join_rules", "");
|
||||
if (joinRules) {
|
||||
const rule = joinRules.getContent().join_rule;
|
||||
if (rule !== "public") {
|
||||
|
@ -1165,9 +1167,11 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
<span>
|
||||
{isSpace
|
||||
? _t("Are you sure you want to leave the space '%(spaceName)s'?", {
|
||||
spaceName: roomToLeave.name,
|
||||
spaceName: roomToLeave?.name ?? _t("Unnamed Space"),
|
||||
})
|
||||
: _t("Are you sure you want to leave the room '%(roomName)s'?", { roomName: roomToLeave.name })}
|
||||
: _t("Are you sure you want to leave the room '%(roomName)s'?", {
|
||||
roomName: roomToLeave?.name ?? _t("Unnamed Room"),
|
||||
})}
|
||||
{warnings}
|
||||
</span>
|
||||
),
|
||||
|
@ -1311,9 +1315,9 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
this.setStateForNewView({ view: Views.LOGGED_IN });
|
||||
// If a specific screen is set to be shown after login, show that above
|
||||
// all else, as it probably means the user clicked on something already.
|
||||
if (this.screenAfterLogin && this.screenAfterLogin.screen) {
|
||||
if (this.screenAfterLogin?.screen) {
|
||||
this.showScreen(this.screenAfterLogin.screen, this.screenAfterLogin.params);
|
||||
this.screenAfterLogin = null;
|
||||
this.screenAfterLogin = undefined;
|
||||
} else if (MatrixClientPeg.currentUserIsJustRegistered()) {
|
||||
MatrixClientPeg.setJustRegisteredUserId(null);
|
||||
|
||||
|
@ -1403,7 +1407,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
// result in view_home_page, _user_settings or _room_directory
|
||||
if (this.screenAfterLogin && this.screenAfterLogin.screen) {
|
||||
this.showScreen(this.screenAfterLogin.screen, this.screenAfterLogin.params);
|
||||
this.screenAfterLogin = null;
|
||||
this.screenAfterLogin = undefined;
|
||||
} else if (localStorage && localStorage.getItem("mx_last_room_id")) {
|
||||
// Before defaulting to directory, show the last viewed room
|
||||
this.viewLastRoom();
|
||||
|
@ -1419,7 +1423,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
private viewLastRoom(): void {
|
||||
dis.dispatch<ViewRoomPayload>({
|
||||
action: Action.ViewRoom,
|
||||
room_id: localStorage.getItem("mx_last_room_id"),
|
||||
room_id: localStorage.getItem("mx_last_room_id") ?? undefined,
|
||||
metricsTrigger: undefined, // other
|
||||
});
|
||||
}
|
||||
|
@ -1486,12 +1490,12 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
return this.loggedInView.current.canResetTimelineInRoom(roomId);
|
||||
});
|
||||
|
||||
cli.on(ClientEvent.Sync, (state: SyncState, prevState?: SyncState, data?: ISyncStateData) => {
|
||||
cli.on(ClientEvent.Sync, (state: SyncState, prevState: SyncState | null, data?: ISyncStateData) => {
|
||||
if (state === SyncState.Error || state === SyncState.Reconnecting) {
|
||||
if (data.error instanceof InvalidStoreError) {
|
||||
if (data?.error instanceof InvalidStoreError) {
|
||||
Lifecycle.handleInvalidStoreError(data.error);
|
||||
}
|
||||
this.setState({ syncError: data.error });
|
||||
this.setState({ syncError: data?.error ?? null });
|
||||
} else if (this.state.syncError) {
|
||||
this.setState({ syncError: null });
|
||||
}
|
||||
|
@ -1559,12 +1563,12 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
cancelButton: _t("Dismiss"),
|
||||
onFinished: (confirmed) => {
|
||||
if (confirmed) {
|
||||
const wnd = window.open(consentUri, "_blank");
|
||||
const wnd = window.open(consentUri, "_blank")!;
|
||||
wnd.opener = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
null,
|
||||
undefined,
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
@ -1655,7 +1659,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
{
|
||||
verifier: request.verifier,
|
||||
},
|
||||
null,
|
||||
undefined,
|
||||
/* priority = */ false,
|
||||
/* static = */ true,
|
||||
);
|
||||
|
@ -1774,7 +1778,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
}
|
||||
|
||||
const type = screen === "start_sso" ? "sso" : "cas";
|
||||
PlatformPeg.get().startSingleSignOn(cli, type, this.getFragmentAfterLogin());
|
||||
PlatformPeg.get()?.startSingleSignOn(cli, type, this.getFragmentAfterLogin());
|
||||
} else if (screen.indexOf("room/") === 0) {
|
||||
// Rooms can have the following formats:
|
||||
// #room_alias:domain or !opaque_id:domain
|
||||
|
@ -1786,7 +1790,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
eventOffset = domainOffset + room.substring(domainOffset).indexOf("/");
|
||||
}
|
||||
const roomString = room.substring(0, eventOffset);
|
||||
let eventId = room.substring(eventOffset + 1); // empty string if no event id given
|
||||
let eventId: string | undefined = room.substring(eventOffset + 1); // empty string if no event id given
|
||||
|
||||
// Previously we pulled the eventID from the segments in such a way
|
||||
// where if there was no eventId then we'd get undefined. However, we
|
||||
|
@ -1797,9 +1801,9 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
|
||||
// TODO: Handle encoded room/event IDs: https://github.com/vector-im/element-web/issues/9149
|
||||
|
||||
let threepidInvite: IThreepidInvite;
|
||||
let threepidInvite: IThreepidInvite | undefined;
|
||||
// if we landed here from a 3PID invite, persist it
|
||||
if (params.signurl && params.email) {
|
||||
if (params?.signurl && params?.email) {
|
||||
threepidInvite = ThreepidInviteStore.instance.storeInvite(
|
||||
roomString,
|
||||
params as IThreepidInviteWireFormat,
|
||||
|
@ -1816,8 +1820,8 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
// to other levels. If there's just one ?via= then params.via is a
|
||||
// single string. If someone does something like ?via=one.com&via=two.com
|
||||
// then params.via is an array of strings.
|
||||
let via = [];
|
||||
if (params.via) {
|
||||
let via: string[] = [];
|
||||
if (params?.via) {
|
||||
if (typeof params.via === "string") via = [params.via];
|
||||
else via = params.via;
|
||||
}
|
||||
|
@ -1855,7 +1859,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
dis.dispatch({
|
||||
action: "view_user_info",
|
||||
userId: userId,
|
||||
subAction: params.action,
|
||||
subAction: params?.action,
|
||||
});
|
||||
} else {
|
||||
logger.info("Ignoring showScreen for '%s'", screen);
|
||||
|
@ -1949,8 +1953,8 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
const numUnreadRooms = notificationState.numUnreadStates; // we know that states === rooms here
|
||||
|
||||
if (PlatformPeg.get()) {
|
||||
PlatformPeg.get().setErrorStatus(state === SyncState.Error);
|
||||
PlatformPeg.get().setNotificationCount(numUnreadRooms);
|
||||
PlatformPeg.get()!.setErrorStatus(state === SyncState.Error);
|
||||
PlatformPeg.get()!.setNotificationCount(numUnreadRooms);
|
||||
}
|
||||
|
||||
this.subTitleStatus = "";
|
||||
|
@ -1971,7 +1975,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
};
|
||||
|
||||
private makeRegistrationUrl = (params: QueryDict): string => {
|
||||
if (this.props.startingFragmentQueryParams.referrer) {
|
||||
if (this.props.startingFragmentQueryParams?.referrer) {
|
||||
params.referrer = this.props.startingFragmentQueryParams.referrer;
|
||||
}
|
||||
return this.props.makeRegistrationUrl(params);
|
||||
|
|
|
@ -163,7 +163,7 @@ interface IProps {
|
|||
stickyBottom?: boolean;
|
||||
|
||||
// className for the panel
|
||||
className: string;
|
||||
className?: string;
|
||||
|
||||
// show twelve hour timestamps
|
||||
isTwelveHour?: boolean;
|
||||
|
@ -177,7 +177,7 @@ interface IProps {
|
|||
// which layout to use
|
||||
layout?: Layout;
|
||||
|
||||
resizeNotifier: ResizeNotifier;
|
||||
resizeNotifier?: ResizeNotifier;
|
||||
permalinkCreator?: RoomPermalinkCreator;
|
||||
editState?: EditorStateTransfer;
|
||||
|
||||
|
@ -345,12 +345,12 @@ export default class MessagePanel extends React.Component<IProps, IState> {
|
|||
};
|
||||
|
||||
/* get the DOM node representing the given event */
|
||||
public getNodeForEventId(eventId: string): HTMLElement {
|
||||
public getNodeForEventId(eventId: string): HTMLElement | undefined {
|
||||
if (!this.eventTiles) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return this.eventTiles[eventId]?.ref?.current;
|
||||
return this.eventTiles[eventId]?.ref?.current ?? undefined;
|
||||
}
|
||||
|
||||
public getTileForEventId(eventId?: string): UnwrappedEventTile | undefined {
|
||||
|
@ -362,7 +362,7 @@ export default class MessagePanel extends React.Component<IProps, IState> {
|
|||
|
||||
/* return true if the content is fully scrolled down right now; else false.
|
||||
*/
|
||||
public isAtBottom(): boolean {
|
||||
public isAtBottom(): boolean | undefined {
|
||||
return this.scrollPanel.current?.isAtBottom();
|
||||
}
|
||||
|
||||
|
@ -371,7 +371,7 @@ export default class MessagePanel extends React.Component<IProps, IState> {
|
|||
*
|
||||
* returns null if we are not mounted.
|
||||
*/
|
||||
public getScrollState(): IScrollState {
|
||||
public getScrollState(): IScrollState | null {
|
||||
return this.scrollPanel.current?.getScrollState() ?? null;
|
||||
}
|
||||
|
||||
|
@ -381,7 +381,7 @@ export default class MessagePanel extends React.Component<IProps, IState> {
|
|||
// -1: read marker is above the window
|
||||
// 0: read marker is within the window
|
||||
// +1: read marker is below the window
|
||||
public getReadMarkerPosition(): number {
|
||||
public getReadMarkerPosition(): number | null {
|
||||
const readMarker = this.readMarkerNode.current;
|
||||
const messageWrapper = this.scrollPanel.current;
|
||||
|
||||
|
@ -633,9 +633,8 @@ export default class MessagePanel extends React.Component<IProps, IState> {
|
|||
break;
|
||||
}
|
||||
|
||||
const ret = [];
|
||||
|
||||
let prevEvent = null; // the last event we showed
|
||||
const ret: ReactNode[] = [];
|
||||
let prevEvent: MatrixEvent | null = null; // the last event we showed
|
||||
|
||||
// Note: the EventTile might still render a "sent/sending receipt" independent of
|
||||
// this information. When not providing read receipt information, the tile is likely
|
||||
|
@ -645,7 +644,7 @@ export default class MessagePanel extends React.Component<IProps, IState> {
|
|||
this.readReceiptsByEvent = this.getReadReceiptsByShownEvent();
|
||||
}
|
||||
|
||||
let grouper: BaseGrouper = null;
|
||||
let grouper: BaseGrouper | null = null;
|
||||
|
||||
for (let i = 0; i < events.length; i++) {
|
||||
const { event: mxEv, shouldShow } = events[i];
|
||||
|
@ -695,14 +694,14 @@ export default class MessagePanel extends React.Component<IProps, IState> {
|
|||
}
|
||||
|
||||
public getTilesForEvent(
|
||||
prevEvent: MatrixEvent,
|
||||
prevEvent: MatrixEvent | null,
|
||||
mxEv: MatrixEvent,
|
||||
last = false,
|
||||
isGrouped = false,
|
||||
nextEvent?: MatrixEvent,
|
||||
nextEventWithTile?: MatrixEvent,
|
||||
): ReactNode[] {
|
||||
const ret = [];
|
||||
const ret: ReactNode[] = [];
|
||||
|
||||
const isEditing = this.props.editState?.getEvent().getId() === mxEv.getId();
|
||||
// local echoes have a fake date, which could even be yesterday. Treat them as 'today' for the date separators.
|
||||
|
@ -806,7 +805,7 @@ export default class MessagePanel extends React.Component<IProps, IState> {
|
|||
return ret;
|
||||
}
|
||||
|
||||
public wantsDateSeparator(prevEvent: MatrixEvent, nextEventDate: Date): boolean {
|
||||
public wantsDateSeparator(prevEvent: MatrixEvent | null, nextEventDate: Date): boolean {
|
||||
if (this.context.timelineRenderingType === TimelineRenderingType.ThreadsList) {
|
||||
return false;
|
||||
}
|
||||
|
@ -820,7 +819,7 @@ export default class MessagePanel extends React.Component<IProps, IState> {
|
|||
|
||||
// Get a list of read receipts that should be shown next to this event
|
||||
// Receipts are objects which have a 'userId', 'roomMember' and 'ts'.
|
||||
private getReadReceiptsForEvent(event: MatrixEvent): IReadReceiptProps[] {
|
||||
private getReadReceiptsForEvent(event: MatrixEvent): IReadReceiptProps[] | null {
|
||||
const myUserId = MatrixClientPeg.get().credentials.userId;
|
||||
|
||||
// get list of read receipts, sorted most recent first
|
||||
|
@ -939,7 +938,7 @@ export default class MessagePanel extends React.Component<IProps, IState> {
|
|||
private onTypingShown = (): void => {
|
||||
const scrollPanel = this.scrollPanel.current;
|
||||
// this will make the timeline grow, so checkScroll
|
||||
scrollPanel.checkScroll();
|
||||
scrollPanel?.checkScroll();
|
||||
if (scrollPanel && scrollPanel.getScrollState().stuckAtBottom) {
|
||||
scrollPanel.preventShrinking();
|
||||
}
|
||||
|
@ -1018,7 +1017,7 @@ export default class MessagePanel extends React.Component<IProps, IState> {
|
|||
);
|
||||
}
|
||||
|
||||
let ircResizer = null;
|
||||
let ircResizer: JSX.Element | undefined;
|
||||
if (this.props.layout == Layout.IRC) {
|
||||
ircResizer = (
|
||||
<IRCTimelineProfileResizer
|
||||
|
@ -1076,7 +1075,7 @@ abstract class BaseGrouper {
|
|||
public constructor(
|
||||
public readonly panel: MessagePanel,
|
||||
public readonly event: MatrixEvent,
|
||||
public readonly prevEvent: MatrixEvent,
|
||||
public readonly prevEvent: MatrixEvent | null,
|
||||
public readonly lastShownEvent: MatrixEvent,
|
||||
public readonly nextEvent?: MatrixEvent,
|
||||
public readonly nextEventTile?: MatrixEvent,
|
||||
|
@ -1261,7 +1260,7 @@ class MainGrouper extends BaseGrouper {
|
|||
public constructor(
|
||||
public readonly panel: MessagePanel,
|
||||
public readonly event: MatrixEvent,
|
||||
public readonly prevEvent: MatrixEvent,
|
||||
public readonly prevEvent: MatrixEvent | null,
|
||||
public readonly lastShownEvent: MatrixEvent,
|
||||
nextEvent: MatrixEvent,
|
||||
nextEventTile: MatrixEvent,
|
||||
|
@ -1341,7 +1340,7 @@ class MainGrouper extends BaseGrouper {
|
|||
}
|
||||
|
||||
let highlightInSummary = false;
|
||||
let eventTiles = this.events
|
||||
let eventTiles: ReactNode[] | null = this.events
|
||||
.map((e, i) => {
|
||||
if (e.getId() === panel.props.highlightedEventId) {
|
||||
highlightInSummary = true;
|
||||
|
|
|
@ -149,7 +149,7 @@ interface IProps {
|
|||
*/
|
||||
|
||||
export interface IScrollState {
|
||||
stuckAtBottom: boolean;
|
||||
stuckAtBottom?: boolean;
|
||||
trackedNode?: HTMLElement;
|
||||
trackedScrollToken?: string;
|
||||
bottomOffset?: number;
|
||||
|
@ -173,7 +173,7 @@ export default class ScrollPanel extends React.Component<IProps> {
|
|||
onScroll: function () {},
|
||||
};
|
||||
|
||||
private readonly pendingFillRequests: Record<"b" | "f", boolean> = {
|
||||
private readonly pendingFillRequests: Record<"b" | "f", boolean | null> = {
|
||||
b: null,
|
||||
f: null,
|
||||
};
|
||||
|
@ -190,7 +190,7 @@ export default class ScrollPanel extends React.Component<IProps> {
|
|||
private pendingFillDueToPropsUpdate: boolean;
|
||||
private scrollState: IScrollState;
|
||||
private preventShrinkingState: IPreventShrinkingState;
|
||||
private unfillDebouncer: number;
|
||||
private unfillDebouncer: number | null;
|
||||
private bottomGrowth: number;
|
||||
private minListHeight: number;
|
||||
private heightUpdateInProgress: boolean;
|
||||
|
|
|
@ -24,6 +24,7 @@ import { _t } from "../../languageHandler";
|
|||
import AutoHideScrollbar from "./AutoHideScrollbar";
|
||||
import AccessibleButton from "../views/elements/AccessibleButton";
|
||||
import { PosthogScreenTracker, ScreenName } from "../../PosthogTrackers";
|
||||
import { NonEmptyArray } from "../../@types/common";
|
||||
|
||||
/**
|
||||
* Represents a tab for the TabbedView.
|
||||
|
@ -40,7 +41,7 @@ export class Tab {
|
|||
public constructor(
|
||||
public readonly id: string,
|
||||
public readonly label: string,
|
||||
public readonly icon: string,
|
||||
public readonly icon: string | null,
|
||||
public readonly body: React.ReactNode,
|
||||
public readonly screenName?: ScreenName,
|
||||
) {}
|
||||
|
@ -52,7 +53,7 @@ export enum TabLocation {
|
|||
}
|
||||
|
||||
interface IProps {
|
||||
tabs: Tab[];
|
||||
tabs: NonEmptyArray<Tab>;
|
||||
initialTabId?: string;
|
||||
tabLocation: TabLocation;
|
||||
onChange?: (tabId: string) => void;
|
||||
|
@ -69,7 +70,7 @@ export default class TabbedView extends React.Component<IProps, IState> {
|
|||
|
||||
const initialTabIdIsValid = props.tabs.find((tab) => tab.id === props.initialTabId);
|
||||
this.state = {
|
||||
activeTabId: initialTabIdIsValid ? props.initialTabId : props.tabs[0]?.id,
|
||||
activeTabId: initialTabIdIsValid ? props.initialTabId! : props.tabs[0].id,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -101,7 +102,7 @@ export default class TabbedView extends React.Component<IProps, IState> {
|
|||
|
||||
if (this.state.activeTabId === tab.id) classes += "mx_TabbedView_tabLabel_active";
|
||||
|
||||
let tabIcon = null;
|
||||
let tabIcon: JSX.Element | undefined;
|
||||
if (tab.icon) {
|
||||
tabIcon = <span className={`mx_TabbedView_maskedIcon ${tab.icon}`} />;
|
||||
}
|
||||
|
@ -141,9 +142,11 @@ export default class TabbedView extends React.Component<IProps, IState> {
|
|||
mx_TabbedView_tabsOnTop: this.props.tabLocation == TabLocation.TOP,
|
||||
});
|
||||
|
||||
const screenName = tab?.screenName ?? this.props.screenName;
|
||||
|
||||
return (
|
||||
<div className={tabbedViewClasses}>
|
||||
<PosthogScreenTracker screenName={tab?.screenName ?? this.props.screenName} />
|
||||
{screenName && <PosthogScreenTracker screenName={screenName} />}
|
||||
<div className="mx_TabbedView_tabLabels">{labels}</div>
|
||||
{panel}
|
||||
</div>
|
||||
|
|
|
@ -200,7 +200,7 @@ interface IState {
|
|||
forwardPaginating: boolean;
|
||||
|
||||
// cache of matrixClient.getSyncState() (but from the 'sync' event)
|
||||
clientSyncState: SyncState;
|
||||
clientSyncState: SyncState | null;
|
||||
|
||||
// should the event tiles have twelve hour times
|
||||
isTwelveHour: boolean;
|
||||
|
@ -268,7 +268,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
|
|||
// but for now we just do it per room for simplicity.
|
||||
let initialReadMarker: string | null = null;
|
||||
if (this.props.manageReadMarkers) {
|
||||
const readmarker = this.props.timelineSet.room.getAccountData("m.fully_read");
|
||||
const readmarker = this.props.timelineSet.room?.getAccountData("m.fully_read");
|
||||
if (readmarker) {
|
||||
initialReadMarker = readmarker.getContent().event_id;
|
||||
} else {
|
||||
|
@ -414,7 +414,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
|
|||
// Get the list of actually rendered events seen in the DOM.
|
||||
// This is useful to know for sure what's being shown on screen.
|
||||
// And we can suss out any corrupted React `key` problems.
|
||||
let renderedEventIds: string[];
|
||||
let renderedEventIds: string[] | undefined;
|
||||
try {
|
||||
const messagePanel = this.messagePanel.current;
|
||||
if (messagePanel) {
|
||||
|
@ -422,7 +422,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
|
|||
if (messagePanelNode) {
|
||||
const actuallyRenderedEvents = messagePanelNode.querySelectorAll("[data-event-id]");
|
||||
renderedEventIds = [...actuallyRenderedEvents].map((renderedEvent) => {
|
||||
return renderedEvent.getAttribute("data-event-id");
|
||||
return renderedEvent.getAttribute("data-event-id")!;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -432,8 +432,8 @@ class TimelinePanel extends React.Component<IProps, IState> {
|
|||
|
||||
// Get the list of events and threads for the room as seen by the
|
||||
// matrix-js-sdk.
|
||||
let serializedEventIdsFromTimelineSets: { [key: string]: string[] }[];
|
||||
let serializedEventIdsFromThreadsTimelineSets: { [key: string]: string[] }[];
|
||||
let serializedEventIdsFromTimelineSets: { [key: string]: string[] }[] | undefined;
|
||||
let serializedEventIdsFromThreadsTimelineSets: { [key: string]: string[] }[] | undefined;
|
||||
const serializedThreadsMap: { [key: string]: any } = {};
|
||||
if (room) {
|
||||
const timelineSets = room.getTimelineSets();
|
||||
|
@ -469,15 +469,15 @@ class TimelinePanel extends React.Component<IProps, IState> {
|
|||
}
|
||||
}
|
||||
|
||||
let timelineWindowEventIds: string[];
|
||||
let timelineWindowEventIds: string[] | undefined;
|
||||
try {
|
||||
timelineWindowEventIds = this.timelineWindow.getEvents().map((ev) => ev.getId());
|
||||
timelineWindowEventIds = this.timelineWindow?.getEvents().map((ev) => ev.getId()!);
|
||||
} catch (err) {
|
||||
logger.error(`onDumpDebugLogs: Failed to get event IDs from the timelineWindow`, err);
|
||||
}
|
||||
let pendingEventIds: string[];
|
||||
let pendingEventIds: string[] | undefined;
|
||||
try {
|
||||
pendingEventIds = this.props.timelineSet.getPendingEvents().map((ev) => ev.getId());
|
||||
pendingEventIds = this.props.timelineSet.getPendingEvents().map((ev) => ev.getId()!);
|
||||
} catch (err) {
|
||||
logger.error(`onDumpDebugLogs: Failed to get pending event IDs`, err);
|
||||
}
|
||||
|
@ -491,10 +491,10 @@ class TimelinePanel extends React.Component<IProps, IState> {
|
|||
`\tserializedEventIdsFromThreadsTimelineSets=` +
|
||||
`${JSON.stringify(serializedEventIdsFromThreadsTimelineSets)}\n` +
|
||||
`\tserializedThreadsMap=${JSON.stringify(serializedThreadsMap)}\n` +
|
||||
`\ttimelineWindowEventIds(${timelineWindowEventIds.length})=${JSON.stringify(
|
||||
`\ttimelineWindowEventIds(${timelineWindowEventIds?.length})=${JSON.stringify(
|
||||
timelineWindowEventIds,
|
||||
)}\n` +
|
||||
`\tpendingEventIds(${pendingEventIds.length})=${JSON.stringify(pendingEventIds)}`,
|
||||
`\tpendingEventIds(${pendingEventIds?.length})=${JSON.stringify(pendingEventIds)}`,
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -560,7 +560,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
|
|||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
if (!this.timelineWindow.canPaginate(dir)) {
|
||||
if (!this.timelineWindow?.canPaginate(dir)) {
|
||||
debuglog("can't", dir, "paginate any further");
|
||||
this.setState<null>({ [canPaginateKey]: false });
|
||||
return Promise.resolve(false);
|
||||
|
@ -576,7 +576,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
|
|||
|
||||
return this.onPaginationRequest(this.timelineWindow, dir, PAGINATE_SIZE).then((r) => {
|
||||
if (this.unmounted) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
debuglog("paginate complete backwards:" + backwards + "; success:" + r);
|
||||
|
@ -595,7 +595,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
|
|||
// paginate in the other where we previously could not.
|
||||
const otherDirection = backwards ? EventTimeline.FORWARDS : EventTimeline.BACKWARDS;
|
||||
const canPaginateOtherWayKey = backwards ? "canForwardPaginate" : "canBackPaginate";
|
||||
if (!this.state[canPaginateOtherWayKey] && this.timelineWindow.canPaginate(otherDirection)) {
|
||||
if (!this.state[canPaginateOtherWayKey] && this.timelineWindow?.canPaginate(otherDirection)) {
|
||||
debuglog("can now", otherDirection, "paginate again");
|
||||
newState[canPaginateOtherWayKey] = true;
|
||||
}
|
||||
|
@ -666,7 +666,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
|
|||
|
||||
private onRoomTimeline = (
|
||||
ev: MatrixEvent,
|
||||
room: Room | null,
|
||||
room: Room | undefined,
|
||||
toStartOfTimeline: boolean,
|
||||
removed: boolean,
|
||||
data: IRoomTimelineData,
|
||||
|
@ -1008,7 +1008,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
|
|||
if (
|
||||
currentRREventId &&
|
||||
currentRREventIndex === null &&
|
||||
this.timelineWindow.canPaginate(EventTimeline.FORWARDS)
|
||||
this.timelineWindow?.canPaginate(EventTimeline.FORWARDS)
|
||||
) {
|
||||
shouldSendRR = false;
|
||||
}
|
||||
|
@ -1149,7 +1149,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
|
|||
const events = this.timelineWindow.getEvents();
|
||||
|
||||
// first find where the current RM is
|
||||
let i;
|
||||
let i: number;
|
||||
for (i = 0; i < events.length; i++) {
|
||||
if (events[i].getId() == this.state.readMarkerEventId) {
|
||||
break;
|
||||
|
@ -1182,7 +1182,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
|
|||
//
|
||||
// Otherwise, reload the timeline rather than trying to paginate
|
||||
// through all of space-time.
|
||||
if (this.timelineWindow.canPaginate(EventTimeline.FORWARDS)) {
|
||||
if (this.timelineWindow?.canPaginate(EventTimeline.FORWARDS)) {
|
||||
this.loadTimeline();
|
||||
} else {
|
||||
this.messagePanel.current?.scrollToBottom();
|
||||
|
@ -1231,7 +1231,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
|
|||
|
||||
// Look up the timestamp if we can find it
|
||||
const tl = this.props.timelineSet.getTimelineForEvent(rmId ?? "");
|
||||
let rmTs: number;
|
||||
let rmTs: number | undefined;
|
||||
if (tl) {
|
||||
const event = tl.getEvents().find((e) => {
|
||||
return e.getId() == rmId;
|
||||
|
@ -1264,7 +1264,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
|
|||
*
|
||||
* returns null if we are not mounted.
|
||||
*/
|
||||
public getScrollState = (): IScrollState => {
|
||||
public getScrollState = (): IScrollState | null => {
|
||||
if (!this.messagePanel.current) {
|
||||
return null;
|
||||
}
|
||||
|
@ -1277,7 +1277,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
|
|||
// -1: read marker is above the window
|
||||
// 0: read marker is visible
|
||||
// +1: read marker is below the window
|
||||
public getReadMarkerPosition = (): number => {
|
||||
public getReadMarkerPosition = (): number | null => {
|
||||
if (!this.props.manageReadMarkers) return null;
|
||||
if (!this.messagePanel.current) return null;
|
||||
|
||||
|
@ -1449,7 +1449,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
|
|||
this.setState({ timelineLoading: false });
|
||||
logger.error(`Error loading timeline panel at ${this.props.timelineSet.room?.roomId}/${eventId}`, error);
|
||||
|
||||
let onFinished: () => void;
|
||||
let onFinished: (() => void) | undefined;
|
||||
|
||||
// if we were given an event ID, then when the user closes the
|
||||
// dialog, let's jump to the end of the timeline. If we weren't,
|
||||
|
@ -1745,7 +1745,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
|
|||
const wrapperRect = messagePanelNode.getBoundingClientRect();
|
||||
const myUserId = MatrixClientPeg.get().credentials.userId;
|
||||
|
||||
const isNodeInView = (node: HTMLElement): boolean => {
|
||||
const isNodeInView = (node?: HTMLElement): boolean => {
|
||||
if (node) {
|
||||
const boundingRect = node.getBoundingClientRect();
|
||||
if (
|
||||
|
@ -1828,7 +1828,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
|
|||
return null;
|
||||
}
|
||||
|
||||
const myUserId = client.credentials.userId;
|
||||
const myUserId = client.getSafeUserId();
|
||||
const receiptStore: ReadReceipt<any, any> = this.props.timelineSet.thread ?? this.props.timelineSet.room;
|
||||
return receiptStore?.getEventReadUpTo(myUserId, ignoreSynthesized);
|
||||
}
|
||||
|
@ -1943,7 +1943,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
|
|||
canBackPaginate={this.state.canBackPaginate && this.state.firstVisibleEventIndex === 0}
|
||||
showUrlPreview={this.props.showUrlPreview}
|
||||
showReadReceipts={this.props.showReadReceipts}
|
||||
ourUserId={MatrixClientPeg.get().credentials.userId}
|
||||
ourUserId={MatrixClientPeg.get().getSafeUserId()}
|
||||
stickyBottom={stickyBottom}
|
||||
onScroll={this.onMessageListScroll}
|
||||
onFillRequest={this.onMessageListFillRequest}
|
||||
|
|
|
@ -145,7 +145,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
|
|||
} else {
|
||||
const theme = SettingsStore.getValue("theme");
|
||||
if (theme.startsWith("custom-")) {
|
||||
return getCustomTheme(theme.substring("custom-".length)).is_dark;
|
||||
return !!getCustomTheme(theme.substring("custom-".length)).is_dark;
|
||||
}
|
||||
return theme === "dark";
|
||||
}
|
||||
|
@ -236,7 +236,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
|
|||
SettingsStore.setValue("theme", null, SettingLevel.DEVICE, newTheme); // set at same level as Appearance tab
|
||||
};
|
||||
|
||||
private onSettingsOpen = (ev: ButtonEvent, tabId: string): void => {
|
||||
private onSettingsOpen = (ev: ButtonEvent, tabId?: string): void => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
|
@ -319,7 +319,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
|
|||
);
|
||||
}
|
||||
|
||||
let homeButton = null;
|
||||
let homeButton: JSX.Element | undefined;
|
||||
if (this.hasHomePage) {
|
||||
homeButton = (
|
||||
<IconizedContextMenuOption
|
||||
|
@ -330,7 +330,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
|
|||
);
|
||||
}
|
||||
|
||||
let feedbackButton;
|
||||
let feedbackButton: JSX.Element | undefined;
|
||||
if (SettingsStore.getValue(UIFeature.Feedback)) {
|
||||
feedbackButton = (
|
||||
<IconizedContextMenuOption
|
||||
|
@ -357,7 +357,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
|
|||
<IconizedContextMenuOption
|
||||
iconClassName="mx_UserMenu_iconSettings"
|
||||
label={_t("All settings")}
|
||||
onClick={(e) => this.onSettingsOpen(e, null)}
|
||||
onClick={(e) => this.onSettingsOpen(e)}
|
||||
/>
|
||||
{feedbackButton}
|
||||
<IconizedContextMenuOption
|
||||
|
@ -376,7 +376,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
|
|||
<IconizedContextMenuOption
|
||||
iconClassName="mx_UserMenu_iconSettings"
|
||||
label={_t("Settings")}
|
||||
onClick={(e) => this.onSettingsOpen(e, null)}
|
||||
onClick={(e) => this.onSettingsOpen(e)}
|
||||
/>
|
||||
{feedbackButton}
|
||||
</IconizedContextMenuOptionList>
|
||||
|
@ -395,9 +395,12 @@ export default class UserMenu extends React.Component<IProps, IState> {
|
|||
{OwnProfileStore.instance.displayName}
|
||||
</span>
|
||||
<span className="mx_UserMenu_contextMenu_userId">
|
||||
{UserIdentifierCustomisations.getDisplayUserIdentifier(MatrixClientPeg.get().getUserId(), {
|
||||
withDisplayName: true,
|
||||
})}
|
||||
{UserIdentifierCustomisations.getDisplayUserIdentifier(
|
||||
MatrixClientPeg.get().getSafeUserId(),
|
||||
{
|
||||
withDisplayName: true,
|
||||
},
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
@ -426,7 +429,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
|
|||
const displayName = OwnProfileStore.instance.displayName || userId;
|
||||
const avatarUrl = OwnProfileStore.instance.getHttpAvatarUrl(avatarSize);
|
||||
|
||||
let name: JSX.Element;
|
||||
let name: JSX.Element | undefined;
|
||||
if (!this.props.isPanelCollapsed) {
|
||||
name = <div className="mx_UserMenu_name">{displayName}</div>;
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ export default class ViewSource extends React.Component<IProps, IState> {
|
|||
};
|
||||
if (isEncrypted) {
|
||||
const copyDecryptedFunc = (): string => {
|
||||
return stringify(decryptedEventSource);
|
||||
return stringify(decryptedEventSource || {});
|
||||
};
|
||||
return (
|
||||
<>
|
||||
|
@ -117,7 +117,7 @@ export default class ViewSource extends React.Component<IProps, IState> {
|
|||
return (
|
||||
<MatrixClientContext.Consumer>
|
||||
{(cli) => (
|
||||
<DevtoolsContext.Provider value={{ room: cli.getRoom(roomId) }}>
|
||||
<DevtoolsContext.Provider value={{ room: cli.getRoom(roomId)! }}>
|
||||
<StateEventEditor onBack={this.onBack} mxEvent={mxEvent} />
|
||||
</DevtoolsContext.Provider>
|
||||
)}
|
||||
|
@ -128,7 +128,7 @@ export default class ViewSource extends React.Component<IProps, IState> {
|
|||
return (
|
||||
<MatrixClientContext.Consumer>
|
||||
{(cli) => (
|
||||
<DevtoolsContext.Provider value={{ room: cli.getRoom(roomId) }}>
|
||||
<DevtoolsContext.Provider value={{ room: cli.getRoom(roomId)! }}>
|
||||
<TimelineEventEditor onBack={this.onBack} mxEvent={mxEvent} />
|
||||
</DevtoolsContext.Provider>
|
||||
)}
|
||||
|
|
|
@ -395,7 +395,7 @@ export default class ForgotPassword extends React.Component<Props, State> {
|
|||
button: _t("Continue"),
|
||||
});
|
||||
const [confirmed] = await finished;
|
||||
return confirmed;
|
||||
return !!confirmed;
|
||||
}
|
||||
|
||||
public renderCheckEmail(): JSX.Element {
|
||||
|
|
|
@ -368,7 +368,7 @@ export default class LoginComponent extends React.PureComponent<IProps, IState>
|
|||
isDefaultServer = true;
|
||||
}
|
||||
|
||||
const fallbackHsUrl = isDefaultServer ? this.props.fallbackHsUrl : null;
|
||||
const fallbackHsUrl = isDefaultServer ? this.props.fallbackHsUrl! : null;
|
||||
|
||||
const loginLogic = new Login(hsUrl, isUrl, fallbackHsUrl, {
|
||||
defaultDeviceDisplayName: this.props.defaultDeviceDisplayName,
|
||||
|
@ -514,7 +514,7 @@ export default class LoginComponent extends React.PureComponent<IProps, IState>
|
|||
// this is the ideal order we want to show the flows in
|
||||
const order = ["m.login.password", "m.login.sso"];
|
||||
|
||||
const flows = filterBoolean(order.map((type) => this.state.flows.find((flow) => flow.type === type)));
|
||||
const flows = filterBoolean(order.map((type) => this.state.flows?.find((flow) => flow.type === type)));
|
||||
return (
|
||||
<React.Fragment>
|
||||
{flows.map((flow) => {
|
||||
|
@ -546,7 +546,7 @@ export default class LoginComponent extends React.PureComponent<IProps, IState>
|
|||
};
|
||||
|
||||
private renderSsoStep = (loginType: "cas" | "sso"): JSX.Element => {
|
||||
const flow = this.state.flows.find((flow) => flow.type === "m.login." + loginType) as ISSOFlow;
|
||||
const flow = this.state.flows?.find((flow) => flow.type === "m.login." + loginType) as ISSOFlow;
|
||||
|
||||
return (
|
||||
<SSOButtons
|
||||
|
@ -554,7 +554,7 @@ export default class LoginComponent extends React.PureComponent<IProps, IState>
|
|||
flow={flow}
|
||||
loginType={loginType}
|
||||
fragmentAfterLogin={this.props.fragmentAfterLogin}
|
||||
primary={!this.state.flows.find((flow) => flow.type === "m.login.password")}
|
||||
primary={!this.state.flows?.find((flow) => flow.type === "m.login.password")}
|
||||
action={SSOAction.LOGIN}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -88,7 +88,7 @@ export default class SetupEncryptionBody extends React.Component<IProps, IState>
|
|||
|
||||
private onVerifyClick = (): void => {
|
||||
const cli = MatrixClientPeg.get();
|
||||
const userId = cli.getUserId();
|
||||
const userId = cli.getSafeUserId();
|
||||
const requestPromise = cli.requestVerification(userId);
|
||||
|
||||
// We need to call onFinished now to close this dialog, and
|
||||
|
@ -212,7 +212,7 @@ export default class SetupEncryptionBody extends React.Component<IProps, IState>
|
|||
{useRecoveryKeyButton}
|
||||
</div>
|
||||
<div className="mx_SetupEncryptionBody_reset">
|
||||
{_t("Forgotten or lost all recovery methods? <a>Reset all</a>", null, {
|
||||
{_t("Forgotten or lost all recovery methods? <a>Reset all</a>", undefined, {
|
||||
a: (sub) => (
|
||||
<AccessibleButton
|
||||
kind="link_inline"
|
||||
|
@ -228,7 +228,7 @@ export default class SetupEncryptionBody extends React.Component<IProps, IState>
|
|||
);
|
||||
}
|
||||
} else if (phase === Phase.Done) {
|
||||
let message;
|
||||
let message: JSX.Element;
|
||||
if (this.state.backupInfo) {
|
||||
message = (
|
||||
<p>
|
||||
|
|
|
@ -23,7 +23,7 @@ import { _t } from "../../../languageHandler";
|
|||
import dis from "../../../dispatcher/dispatcher";
|
||||
import * as Lifecycle from "../../../Lifecycle";
|
||||
import Modal from "../../../Modal";
|
||||
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
||||
import { IMatrixClientCreds, MatrixClientPeg } from "../../../MatrixClientPeg";
|
||||
import { sendLoginRequest } from "../../../Login";
|
||||
import AuthPage from "../../views/auth/AuthPage";
|
||||
import { SSO_HOMESERVER_URL_KEY, SSO_ID_SERVER_URL_KEY } from "../../../BasePlatform";
|
||||
|
@ -159,7 +159,7 @@ export default class SoftLogout extends React.Component<IProps, IState> {
|
|||
device_id: MatrixClientPeg.get().getDeviceId(),
|
||||
};
|
||||
|
||||
let credentials = null;
|
||||
let credentials: IMatrixClientCreds;
|
||||
try {
|
||||
credentials = await sendLoginRequest(hsUrl, isUrl, loginType, loginParams);
|
||||
} catch (e) {
|
||||
|
@ -192,7 +192,7 @@ export default class SoftLogout extends React.Component<IProps, IState> {
|
|||
device_id: MatrixClientPeg.get().getDeviceId(),
|
||||
};
|
||||
|
||||
let credentials = null;
|
||||
let credentials: IMatrixClientCreds;
|
||||
try {
|
||||
credentials = await sendLoginRequest(hsUrl, isUrl, loginType, loginParams);
|
||||
} catch (e) {
|
||||
|
@ -212,7 +212,7 @@ export default class SoftLogout extends React.Component<IProps, IState> {
|
|||
}
|
||||
|
||||
private renderPasswordForm(introText: Optional<string>): JSX.Element {
|
||||
let error: JSX.Element = null;
|
||||
let error: JSX.Element | undefined;
|
||||
if (this.state.errorText) {
|
||||
error = <span className="mx_Login_error">{this.state.errorText}</span>;
|
||||
}
|
||||
|
@ -267,7 +267,7 @@ export default class SoftLogout extends React.Component<IProps, IState> {
|
|||
return <Spinner />;
|
||||
}
|
||||
|
||||
let introText = null; // null is translated to something area specific in this function
|
||||
let introText: string | null = null; // null is translated to something area specific in this function
|
||||
if (this.state.keyBackupNeeded) {
|
||||
introText = _t(
|
||||
"Regain access to your account and recover encryption keys stored in this session. " +
|
||||
|
|
|
@ -27,7 +27,7 @@ interface Props {
|
|||
export function AuthHeaderDisplay({ title, icon, serverPicker, children }: PropsWithChildren<Props>): JSX.Element {
|
||||
const context = useContext(AuthHeaderContext);
|
||||
if (!context) {
|
||||
return null;
|
||||
return <></>;
|
||||
}
|
||||
const current = context.state.length ? context.state[0] : null;
|
||||
return (
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue