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
|
@ -52,3 +52,5 @@ export type KeysStartingWith<Input extends object, Str extends string> = {
|
|||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
[P in keyof Input]: P extends `${Str}${infer _X}` ? P : never; // we don't use _X
|
||||
}[keyof Input];
|
||||
|
||||
export type NonEmptyArray<T> = [T, ...T[]];
|
||||
|
|
|
@ -26,7 +26,11 @@ import { SSOAuthEntry } from "./components/views/auth/InteractiveAuthEntryCompon
|
|||
import InteractiveAuthDialog from "./components/views/dialogs/InteractiveAuthDialog";
|
||||
|
||||
function getIdServerDomain(): string {
|
||||
return MatrixClientPeg.get().idBaseUrl.split("://")[1];
|
||||
const idBaseUrl = MatrixClientPeg.get().idBaseUrl;
|
||||
if (!idBaseUrl) {
|
||||
throw new Error("Identity server not set");
|
||||
}
|
||||
return idBaseUrl.split("://")[1];
|
||||
}
|
||||
|
||||
export type Binding = {
|
||||
|
@ -190,6 +194,9 @@ export default class AddThreepid {
|
|||
if (this.bind) {
|
||||
const authClient = new IdentityAuthClient();
|
||||
const identityAccessToken = await authClient.getAccessToken();
|
||||
if (!identityAccessToken) {
|
||||
throw new Error("No identity access token found");
|
||||
}
|
||||
await MatrixClientPeg.get().bindThreePid({
|
||||
sid: this.sessionId,
|
||||
client_secret: this.clientSecret,
|
||||
|
@ -279,7 +286,9 @@ export default class AddThreepid {
|
|||
* with a "message" property which contains a human-readable message detailing why
|
||||
* the request failed.
|
||||
*/
|
||||
public async haveMsisdnToken(msisdnToken: string): Promise<any[] | undefined> {
|
||||
public async haveMsisdnToken(
|
||||
msisdnToken: string,
|
||||
): Promise<[success?: boolean, result?: IAuthData | Error | null] | undefined> {
|
||||
const authClient = new IdentityAuthClient();
|
||||
const supportsSeparateAddAndBind = await MatrixClientPeg.get().doesServerSupportSeparateAddAndBind();
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ export const ContextMenuButton: React.FC<IProps> = ({
|
|||
<AccessibleButton
|
||||
{...props}
|
||||
onClick={onClick}
|
||||
onContextMenu={onContextMenu || onClick}
|
||||
onContextMenu={onContextMenu ?? onClick ?? undefined}
|
||||
title={label}
|
||||
aria-label={label}
|
||||
aria-haspopup={true}
|
||||
|
|
|
@ -37,7 +37,7 @@ export const ContextMenuTooltipButton: React.FC<IProps> = ({
|
|||
<AccessibleTooltipButton
|
||||
{...props}
|
||||
onClick={onClick}
|
||||
onContextMenu={onContextMenu || onClick}
|
||||
onContextMenu={onContextMenu ?? onClick ?? undefined}
|
||||
aria-haspopup={true}
|
||||
aria-expanded={isExpanded}
|
||||
forceHide={isExpanded}
|
||||
|
|
|
@ -26,7 +26,7 @@ import { _t } from "../languageHandler";
|
|||
import { AsyncActionPayload } from "../dispatcher/payloads";
|
||||
import RoomListStore from "../stores/room-list/RoomListStore";
|
||||
import { SortAlgorithm } from "../stores/room-list/algorithms/models";
|
||||
import { DefaultTagID } from "../stores/room-list/models";
|
||||
import { DefaultTagID, TagID } from "../stores/room-list/models";
|
||||
import ErrorDialog from "../components/views/dialogs/ErrorDialog";
|
||||
|
||||
export default class RoomListActions {
|
||||
|
@ -49,10 +49,10 @@ export default class RoomListActions {
|
|||
public static tagRoom(
|
||||
matrixClient: MatrixClient,
|
||||
room: Room,
|
||||
oldTag: string,
|
||||
newTag: string,
|
||||
oldIndex: number | null,
|
||||
newIndex: number | null,
|
||||
oldTag: TagID | null,
|
||||
newTag: TagID | null,
|
||||
oldIndex?: number,
|
||||
newIndex?: number,
|
||||
): AsyncActionPayload {
|
||||
let metaData: Parameters<MatrixClient["setRoomTag"]>[2] | null = null;
|
||||
|
||||
|
|
|
@ -106,7 +106,7 @@ export class PlaybackQueue {
|
|||
// Remove the now-useless clock for some space savings
|
||||
this.clockStates.delete(mxEvent.getId()!);
|
||||
|
||||
if (wasLastPlaying) {
|
||||
if (wasLastPlaying && this.currentPlaybackId) {
|
||||
this.recentFullPlays.add(this.currentPlaybackId);
|
||||
const orderClone = arrayFastClone(this.playbackIdOrder);
|
||||
const last = orderClone.pop();
|
||||
|
@ -188,8 +188,8 @@ export class PlaybackQueue {
|
|||
if (order.length === 0 || order[order.length - 1] !== this.currentPlaybackId) {
|
||||
const lastInstance = this.playbacks.get(this.currentPlaybackId);
|
||||
if (
|
||||
lastInstance.currentState === PlaybackState.Playing ||
|
||||
lastInstance.currentState === PlaybackState.Paused
|
||||
lastInstance &&
|
||||
[PlaybackState.Playing, PlaybackState.Paused].includes(lastInstance.currentState)
|
||||
) {
|
||||
order.push(this.currentPlaybackId);
|
||||
}
|
||||
|
|
|
@ -35,6 +35,9 @@ export async function getThreepidsWithBindStatus(
|
|||
try {
|
||||
const authClient = new IdentityAuthClient();
|
||||
const identityAccessToken = await authClient.getAccessToken({ check: false });
|
||||
if (!identityAccessToken) {
|
||||
throw new Error("No identity access token found");
|
||||
}
|
||||
|
||||
// Restructure for lookup query
|
||||
const query = threepids.map(({ medium, address }): [string, string] => [medium, address]);
|
||||
|
|
|
@ -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,7 +177,8 @@ export function GenericDropdownMenu<T>({
|
|||
</>
|
||||
);
|
||||
}
|
||||
const contextMenu = menuDisplayed ? (
|
||||
const contextMenu =
|
||||
menuDisplayed && button.current ? (
|
||||
<ContextMenu
|
||||
onFinished={closeMenu}
|
||||
chevronFace={ChevronFace.Top}
|
||||
|
|
|
@ -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(), {
|
||||
{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 (
|
||||
|
|
|
@ -61,7 +61,7 @@ const RoomContextMenu: React.FC<IProps> = ({ room, onFinished, ...props }) => {
|
|||
RoomListStore.instance.getTagsForRoom(room),
|
||||
);
|
||||
|
||||
let leaveOption: JSX.Element;
|
||||
let leaveOption: JSX.Element | undefined;
|
||||
if (roomTags.includes(DefaultTagID.Archived)) {
|
||||
const onForgetRoomClick = (ev: ButtonEvent): void => {
|
||||
ev.preventDefault();
|
||||
|
@ -112,7 +112,7 @@ const RoomContextMenu: React.FC<IProps> = ({ room, onFinished, ...props }) => {
|
|||
const isVideoRoom =
|
||||
videoRoomsEnabled && (room.isElementVideoRoom() || (elementCallVideoRoomsEnabled && room.isCallRoom()));
|
||||
|
||||
let inviteOption: JSX.Element;
|
||||
let inviteOption: JSX.Element | undefined;
|
||||
if (room.canInvite(cli.getUserId()!) && !isDm) {
|
||||
const onInviteClick = (ev: ButtonEvent): void => {
|
||||
ev.preventDefault();
|
||||
|
@ -136,9 +136,9 @@ const RoomContextMenu: React.FC<IProps> = ({ room, onFinished, ...props }) => {
|
|||
);
|
||||
}
|
||||
|
||||
let favouriteOption: JSX.Element;
|
||||
let lowPriorityOption: JSX.Element;
|
||||
let notificationOption: JSX.Element;
|
||||
let favouriteOption: JSX.Element | undefined;
|
||||
let lowPriorityOption: JSX.Element | undefined;
|
||||
let notificationOption: JSX.Element | undefined;
|
||||
if (room.getMyMembership() === "join") {
|
||||
const isFavorite = roomTags.includes(DefaultTagID.Favourite);
|
||||
favouriteOption = (
|
||||
|
@ -208,8 +208,8 @@ const RoomContextMenu: React.FC<IProps> = ({ room, onFinished, ...props }) => {
|
|||
);
|
||||
}
|
||||
|
||||
let peopleOption: JSX.Element;
|
||||
let copyLinkOption: JSX.Element;
|
||||
let peopleOption: JSX.Element | undefined;
|
||||
let copyLinkOption: JSX.Element | undefined;
|
||||
if (!isDm) {
|
||||
peopleOption = (
|
||||
<IconizedContextMenuOption
|
||||
|
@ -247,7 +247,7 @@ const RoomContextMenu: React.FC<IProps> = ({ room, onFinished, ...props }) => {
|
|||
);
|
||||
}
|
||||
|
||||
let filesOption: JSX.Element;
|
||||
let filesOption: JSX.Element | undefined;
|
||||
if (!isVideoRoom) {
|
||||
filesOption = (
|
||||
<IconizedContextMenuOption
|
||||
|
@ -266,9 +266,9 @@ const RoomContextMenu: React.FC<IProps> = ({ room, onFinished, ...props }) => {
|
|||
}
|
||||
|
||||
const pinningEnabled = useFeatureEnabled("feature_pinning");
|
||||
const pinCount = usePinnedEvents(pinningEnabled && room)?.length;
|
||||
const pinCount = usePinnedEvents(pinningEnabled ? room : undefined)?.length;
|
||||
|
||||
let pinsOption: JSX.Element;
|
||||
let pinsOption: JSX.Element | undefined;
|
||||
if (pinningEnabled && !isVideoRoom) {
|
||||
pinsOption = (
|
||||
<IconizedContextMenuOption
|
||||
|
@ -288,7 +288,7 @@ const RoomContextMenu: React.FC<IProps> = ({ room, onFinished, ...props }) => {
|
|||
);
|
||||
}
|
||||
|
||||
let widgetsOption: JSX.Element;
|
||||
let widgetsOption: JSX.Element | undefined;
|
||||
if (!isVideoRoom) {
|
||||
widgetsOption = (
|
||||
<IconizedContextMenuOption
|
||||
|
@ -306,7 +306,7 @@ const RoomContextMenu: React.FC<IProps> = ({ room, onFinished, ...props }) => {
|
|||
);
|
||||
}
|
||||
|
||||
let exportChatOption: JSX.Element;
|
||||
let exportChatOption: JSX.Element | undefined;
|
||||
if (!isVideoRoom) {
|
||||
exportChatOption = (
|
||||
<IconizedContextMenuOption
|
||||
|
|
|
@ -40,6 +40,7 @@ import DecoratedRoomAvatar from "../avatars/DecoratedRoomAvatar";
|
|||
import QueryMatcher from "../../../autocomplete/QueryMatcher";
|
||||
import LazyRenderList from "../elements/LazyRenderList";
|
||||
import { useSettingValue } from "../../../hooks/useSettings";
|
||||
import { filterBoolean } from "../../../utils/arrays";
|
||||
|
||||
// These values match CSS
|
||||
const ROW_HEIGHT = 32 + 12;
|
||||
|
@ -56,7 +57,7 @@ interface IProps {
|
|||
export const Entry: React.FC<{
|
||||
room: Room;
|
||||
checked: boolean;
|
||||
onChange(value: boolean): void;
|
||||
onChange?(value: boolean): void;
|
||||
}> = ({ room, checked, onChange }) => {
|
||||
return (
|
||||
<label className="mx_AddExistingToSpace_entry">
|
||||
|
@ -67,7 +68,7 @@ export const Entry: React.FC<{
|
|||
)}
|
||||
<span className="mx_AddExistingToSpace_entry_name">{room.name}</span>
|
||||
<StyledCheckbox
|
||||
onChange={onChange ? (e) => onChange(e.currentTarget.checked) : null}
|
||||
onChange={onChange ? (e) => onChange(e.currentTarget.checked) : undefined}
|
||||
checked={checked}
|
||||
disabled={!onChange}
|
||||
/>
|
||||
|
@ -150,8 +151,8 @@ export const AddExistingToSpace: React.FC<IAddExistingToSpaceProps> = ({
|
|||
});
|
||||
|
||||
const [selectedToAdd, setSelectedToAdd] = useState(new Set<Room>());
|
||||
const [progress, setProgress] = useState<number>(null);
|
||||
const [error, setError] = useState<Error>(null);
|
||||
const [progress, setProgress] = useState<number | null>(null);
|
||||
const [error, setError] = useState<Error | null>(null);
|
||||
const [query, setQuery] = useState("");
|
||||
const lcQuery = query.toLowerCase().trim();
|
||||
|
||||
|
@ -164,7 +165,7 @@ export const AddExistingToSpace: React.FC<IAddExistingToSpaceProps> = ({
|
|||
if (lcQuery) {
|
||||
const matcher = new QueryMatcher<Room>(visibleRooms, {
|
||||
keys: ["name"],
|
||||
funcs: [(r) => [r.getCanonicalAlias(), ...r.getAltAliases()].filter(Boolean)],
|
||||
funcs: [(r) => filterBoolean([r.getCanonicalAlias(), ...r.getAltAliases()])],
|
||||
shouldMatchWordsOnly: false,
|
||||
});
|
||||
|
||||
|
@ -172,7 +173,7 @@ export const AddExistingToSpace: React.FC<IAddExistingToSpaceProps> = ({
|
|||
}
|
||||
|
||||
const joinRule = space.getJoinRule();
|
||||
return sortRooms(rooms).reduce(
|
||||
return sortRooms(rooms).reduce<[spaces: Room[], rooms: Room[], dms: Room[]]>(
|
||||
(arr, room) => {
|
||||
if (room.isSpaceRoom()) {
|
||||
if (room !== space && !existingSubspacesSet.has(room)) {
|
||||
|
@ -289,7 +290,7 @@ export const AddExistingToSpace: React.FC<IAddExistingToSpaceProps> = ({
|
|||
}
|
||||
setSelectedToAdd(new Set(selectedToAdd));
|
||||
}
|
||||
: null;
|
||||
: undefined;
|
||||
|
||||
// only count spaces when alone as they're shown on a separate modal all on their own
|
||||
const numSpaces = spacesRenderer && !dmsRenderer && !roomsRenderer ? spaces.length : 0;
|
||||
|
@ -373,7 +374,7 @@ const defaultRendererFactory =
|
|||
? (checked: boolean) => {
|
||||
onChange(checked, room);
|
||||
}
|
||||
: null
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
@ -397,7 +398,7 @@ export const SubspaceSelector: React.FC<ISubspaceSelectorProps> = ({ title, spac
|
|||
return [
|
||||
space,
|
||||
...SpaceStore.instance.getChildSpaces(space.roomId).filter((space) => {
|
||||
return space.currentState.maySendStateEvent(EventType.SpaceChild, space.client.credentials.userId);
|
||||
return space.currentState.maySendStateEvent(EventType.SpaceChild, space.client.getSafeUserId());
|
||||
}),
|
||||
];
|
||||
}, [space]);
|
||||
|
|
|
@ -153,12 +153,11 @@ export default class BaseDialog extends React.Component<IProps> {
|
|||
|
||||
return (
|
||||
<MatrixClientContext.Provider value={this.matrixClient}>
|
||||
<PosthogScreenTracker screenName={this.props.screenName} />
|
||||
{this.props.screenName && <PosthogScreenTracker screenName={this.props.screenName} />}
|
||||
<FocusLock
|
||||
returnFocus={true}
|
||||
lockProps={lockProps}
|
||||
className={classNames({
|
||||
[this.props.className]: true,
|
||||
className={classNames(this.props.className, {
|
||||
mx_Dialog_fixedWidth: this.props.fixedWidth,
|
||||
})}
|
||||
>
|
||||
|
|
|
@ -74,6 +74,7 @@ import { InviteKind } from "./InviteDialogTypes";
|
|||
import Modal from "../../../Modal";
|
||||
import dis from "../../../dispatcher/dispatcher";
|
||||
import { privateShouldBeEncrypted } from "../../../utils/rooms";
|
||||
import { NonEmptyArray } from "../../../@types/common";
|
||||
|
||||
// we have a number of types defined from the Matrix spec which can't reasonably be altered here.
|
||||
/* eslint-disable camelcase */
|
||||
|
@ -1421,10 +1422,9 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
|
|||
|
||||
let dialogContent;
|
||||
if (this.props.kind === InviteKind.CallTransfer) {
|
||||
const tabs: Tab[] = [];
|
||||
tabs.push(
|
||||
const tabs: NonEmptyArray<Tab> = [
|
||||
new Tab(TabId.UserDirectory, _td("User Directory"), "mx_InviteDialog_userDirectoryIcon", usersSection),
|
||||
);
|
||||
];
|
||||
|
||||
const backspaceButton = <DialPadBackspaceButton onBackspacePress={this.onDeletePress} />;
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ import BaseDialog from "./BaseDialog";
|
|||
import { Action } from "../../../dispatcher/actions";
|
||||
import { VoipRoomSettingsTab } from "../settings/tabs/room/VoipRoomSettingsTab";
|
||||
import { ActionPayload } from "../../../dispatcher/payloads";
|
||||
import { NonEmptyArray } from "../../../@types/common";
|
||||
|
||||
export const ROOM_GENERAL_TAB = "ROOM_GENERAL_TAB";
|
||||
export const ROOM_VOIP_TAB = "ROOM_VOIP_TAB";
|
||||
|
@ -85,11 +86,11 @@ export default class RoomSettingsDialog extends React.Component<IProps, IState>
|
|||
|
||||
private onRoomName = (): void => {
|
||||
this.setState({
|
||||
roomName: MatrixClientPeg.get().getRoom(this.props.roomId).name,
|
||||
roomName: MatrixClientPeg.get().getRoom(this.props.roomId)?.name ?? "",
|
||||
});
|
||||
};
|
||||
|
||||
private getTabs(): Tab[] {
|
||||
private getTabs(): NonEmptyArray<Tab> {
|
||||
const tabs: Tab[] = [];
|
||||
|
||||
tabs.push(
|
||||
|
@ -178,7 +179,7 @@ export default class RoomSettingsDialog extends React.Component<IProps, IState>
|
|||
);
|
||||
}
|
||||
|
||||
return tabs;
|
||||
return tabs as NonEmptyArray<Tab>;
|
||||
}
|
||||
|
||||
public render(): React.ReactNode {
|
||||
|
|
|
@ -26,6 +26,7 @@ import SettingsStore from "../../../settings/SettingsStore";
|
|||
import { SettingLevel } from "../../../settings/SettingLevel";
|
||||
import RoomName from "../elements/RoomName";
|
||||
import { SpacePreferenceTab } from "../../../dispatcher/payloads/OpenSpacePreferencesPayload";
|
||||
import { NonEmptyArray } from "../../../@types/common";
|
||||
|
||||
interface IProps {
|
||||
space: Room;
|
||||
|
@ -69,7 +70,7 @@ const SpacePreferencesAppearanceTab: React.FC<Pick<IProps, "space">> = ({ space
|
|||
};
|
||||
|
||||
const SpacePreferencesDialog: React.FC<IProps> = ({ space, initialTabId, onFinished }) => {
|
||||
const tabs = [
|
||||
const tabs: NonEmptyArray<Tab> = [
|
||||
new Tab(
|
||||
SpacePreferenceTab.Appearance,
|
||||
_td("Appearance"),
|
||||
|
|
|
@ -30,6 +30,7 @@ import { UIFeature } from "../../../settings/UIFeature";
|
|||
import AdvancedRoomSettingsTab from "../settings/tabs/room/AdvancedRoomSettingsTab";
|
||||
import RolesRoomSettingsTab from "../settings/tabs/room/RolesRoomSettingsTab";
|
||||
import { Action } from "../../../dispatcher/actions";
|
||||
import { NonEmptyArray } from "../../../@types/common";
|
||||
|
||||
export enum SpaceSettingsTab {
|
||||
General = "SPACE_GENERAL_TAB",
|
||||
|
@ -79,7 +80,7 @@ const SpaceSettingsDialog: React.FC<IProps> = ({ matrixClient: cli, space, onFin
|
|||
<AdvancedRoomSettingsTab roomId={space.roomId} closeSettingsFn={onFinished} />,
|
||||
)
|
||||
: null,
|
||||
].filter(Boolean);
|
||||
].filter(Boolean) as NonEmptyArray<Tab>;
|
||||
}, [cli, space, onFinished]);
|
||||
|
||||
return (
|
||||
|
|
|
@ -36,6 +36,7 @@ import SidebarUserSettingsTab from "../settings/tabs/user/SidebarUserSettingsTab
|
|||
import KeyboardUserSettingsTab from "../settings/tabs/user/KeyboardUserSettingsTab";
|
||||
import SessionManagerTab from "../settings/tabs/user/SessionManagerTab";
|
||||
import { UserTab } from "./UserTab";
|
||||
import { NonEmptyArray } from "../../../@types/common";
|
||||
|
||||
interface IProps {
|
||||
initialTabId?: UserTab;
|
||||
|
@ -80,7 +81,7 @@ export default class UserSettingsDialog extends React.Component<IProps, IState>
|
|||
this.setState({ newSessionManagerEnabled: newValue });
|
||||
};
|
||||
|
||||
private getTabs(): Tab[] {
|
||||
private getTabs(): NonEmptyArray<Tab> {
|
||||
const tabs: Tab[] = [];
|
||||
|
||||
tabs.push(
|
||||
|
@ -207,7 +208,7 @@ export default class UserSettingsDialog extends React.Component<IProps, IState>
|
|||
),
|
||||
);
|
||||
|
||||
return tabs;
|
||||
return tabs as NonEmptyArray<Tab>;
|
||||
}
|
||||
|
||||
public render(): React.ReactNode {
|
||||
|
|
|
@ -23,6 +23,7 @@ import DialogButtons from "./DialogButtons";
|
|||
import AccessibleButton from "./AccessibleButton";
|
||||
import TabbedView, { Tab, TabLocation } from "../../structures/TabbedView";
|
||||
import PlatformPeg from "../../../PlatformPeg";
|
||||
import { NonEmptyArray } from "../../../@types/common";
|
||||
|
||||
export function getDesktopCapturerSources(): Promise<Array<DesktopCapturerSource>> {
|
||||
const options: GetSourcesOptions = {
|
||||
|
@ -80,7 +81,7 @@ export interface PickerIState {
|
|||
selectedSource: DesktopCapturerSource | null;
|
||||
}
|
||||
export interface PickerIProps {
|
||||
onFinished(sourceId: string): void;
|
||||
onFinished(sourceId?: string): void;
|
||||
}
|
||||
|
||||
export default class DesktopCapturerSourcePicker extends React.Component<PickerIProps, PickerIState> {
|
||||
|
@ -129,7 +130,7 @@ export default class DesktopCapturerSourcePicker extends React.Component<PickerI
|
|||
};
|
||||
|
||||
private onCloseClick = (): void => {
|
||||
this.props.onFinished(null);
|
||||
this.props.onFinished();
|
||||
};
|
||||
|
||||
private getTab(type: "screen" | "window", label: string): Tab {
|
||||
|
@ -150,7 +151,7 @@ export default class DesktopCapturerSourcePicker extends React.Component<PickerI
|
|||
}
|
||||
|
||||
public render(): React.ReactNode {
|
||||
const tabs = [
|
||||
const tabs: NonEmptyArray<Tab> = [
|
||||
this.getTab("screen", _t("Share entire screen")),
|
||||
this.getTab("window", _t("Application window")),
|
||||
];
|
||||
|
|
|
@ -25,7 +25,7 @@ import { objectHasDiff } from "../../../utils/objects";
|
|||
|
||||
const CUSTOM_VALUE = "SELECT_VALUE_CUSTOM";
|
||||
|
||||
interface IProps {
|
||||
interface Props<K extends undefined | string> {
|
||||
value: number;
|
||||
// The maximum value that can be set with the power selector
|
||||
maxValue: number;
|
||||
|
@ -35,13 +35,14 @@ interface IProps {
|
|||
|
||||
// should the user be able to change the value? false by default.
|
||||
disabled?: boolean;
|
||||
onChange?: (value: number, powerLevelKey: string) => void;
|
||||
|
||||
// Optional key to pass as the second argument to `onChange`
|
||||
powerLevelKey?: string;
|
||||
|
||||
// The name to annotate the selector with
|
||||
label?: string;
|
||||
|
||||
onChange(value: number, powerLevelKey: K extends undefined ? void : K): void;
|
||||
|
||||
// Optional key to pass as the second argument to `onChange`
|
||||
powerLevelKey: K extends undefined ? void : K;
|
||||
}
|
||||
|
||||
interface IState {
|
||||
|
@ -54,13 +55,13 @@ interface IState {
|
|||
custom?: boolean;
|
||||
}
|
||||
|
||||
export default class PowerSelector extends React.Component<IProps, IState> {
|
||||
public static defaultProps: Partial<IProps> = {
|
||||
export default class PowerSelector<K extends undefined | string> extends React.Component<Props<K>, IState> {
|
||||
public static defaultProps: Partial<Props<any>> = {
|
||||
maxValue: Infinity,
|
||||
usersDefault: 0,
|
||||
};
|
||||
|
||||
public constructor(props: IProps) {
|
||||
public constructor(props: Props<K>) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
|
@ -77,7 +78,7 @@ export default class PowerSelector extends React.Component<IProps, IState> {
|
|||
this.initStateFromProps();
|
||||
}
|
||||
|
||||
public componentDidUpdate(prevProps: Readonly<IProps>): void {
|
||||
public componentDidUpdate(prevProps: Readonly<Props<K>>): void {
|
||||
if (objectHasDiff(this.props, prevProps)) {
|
||||
this.initStateFromProps();
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ const defaultOptions: QRCodeToDataURLOptions = {
|
|||
};
|
||||
|
||||
const QRCode: React.FC<IProps> = ({ data, className, ...options }) => {
|
||||
const [dataUri, setUri] = React.useState<string>(null);
|
||||
const [dataUri, setUri] = React.useState<string | null>(null);
|
||||
React.useEffect(() => {
|
||||
let cancelled = false;
|
||||
toDataURL(data, { ...defaultOptions, ...options }).then((uri) => {
|
||||
|
|
|
@ -63,7 +63,7 @@ interface IState {
|
|||
// The loaded events to be rendered as linear-replies
|
||||
events: MatrixEvent[];
|
||||
// The latest loaded event which has not yet been shown
|
||||
loadedEv: MatrixEvent;
|
||||
loadedEv: MatrixEvent | null;
|
||||
// Whether the component is still loading more events
|
||||
loading: boolean;
|
||||
// Whether as error was encountered fetching a replied to event.
|
||||
|
@ -145,7 +145,7 @@ export default class ReplyChain extends React.Component<IProps, IState> {
|
|||
}
|
||||
}
|
||||
|
||||
private async getNextEvent(ev: MatrixEvent): Promise<MatrixEvent> {
|
||||
private async getNextEvent(ev: MatrixEvent): Promise<MatrixEvent | null> {
|
||||
try {
|
||||
const inReplyToEventId = getParentEventId(ev);
|
||||
return await this.getEvent(inReplyToEventId);
|
||||
|
@ -154,7 +154,7 @@ export default class ReplyChain extends React.Component<IProps, IState> {
|
|||
}
|
||||
}
|
||||
|
||||
private async getEvent(eventId: string): Promise<MatrixEvent> {
|
||||
private async getEvent(eventId: string): Promise<MatrixEvent | null> {
|
||||
if (!eventId) return null;
|
||||
const event = this.room.findEventById(eventId);
|
||||
if (event) return event;
|
||||
|
@ -168,7 +168,7 @@ export default class ReplyChain extends React.Component<IProps, IState> {
|
|||
// Return null as it is falsy and thus should be treated as an error (as the event cannot be resolved).
|
||||
return null;
|
||||
}
|
||||
return this.room.findEventById(eventId);
|
||||
return this.room.findEventById(eventId) ?? null;
|
||||
}
|
||||
|
||||
public canCollapse = (): boolean => {
|
||||
|
@ -182,7 +182,7 @@ export default class ReplyChain extends React.Component<IProps, IState> {
|
|||
private onQuoteClick = async (event: ButtonEvent): Promise<void> => {
|
||||
const events = [this.state.loadedEv, ...this.state.events];
|
||||
|
||||
let loadedEv = null;
|
||||
let loadedEv: MatrixEvent | null = null;
|
||||
if (events.length > 0) {
|
||||
loadedEv = await this.getNextEvent(events[0]);
|
||||
}
|
||||
|
@ -200,7 +200,7 @@ export default class ReplyChain extends React.Component<IProps, IState> {
|
|||
}
|
||||
|
||||
public render(): React.ReactNode {
|
||||
let header = null;
|
||||
let header: JSX.Element | undefined;
|
||||
if (this.state.err) {
|
||||
header = (
|
||||
<blockquote className="mx_ReplyChain mx_ReplyChain_error">
|
||||
|
|
|
@ -81,7 +81,7 @@ export default class Slider extends React.Component<IProps> {
|
|||
/>
|
||||
));
|
||||
|
||||
let selection = null;
|
||||
let selection: JSX.Element | undefined;
|
||||
|
||||
if (!this.props.disabled) {
|
||||
const offset = this.offset(this.props.values, this.props.value);
|
||||
|
|
|
@ -21,7 +21,7 @@ import { _t } from "../../../languageHandler";
|
|||
|
||||
interface IProps {
|
||||
// The number of elements to show before truncating. If negative, no truncation is done.
|
||||
truncateAt?: number;
|
||||
truncateAt: number;
|
||||
// The className to apply to the wrapping div
|
||||
className?: string;
|
||||
// A function that returns the children to be rendered into the element.
|
||||
|
@ -34,7 +34,7 @@ interface IProps {
|
|||
getChildCount?: () => number;
|
||||
// A function which will be invoked when an overflow element is required.
|
||||
// This will be inserted after the children.
|
||||
createOverflowElement?: (overflowCount: number, totalCount: number) => React.ReactNode;
|
||||
createOverflowElement: (overflowCount: number, totalCount: number) => React.ReactNode;
|
||||
children?: ReactNode;
|
||||
}
|
||||
|
||||
|
@ -71,8 +71,8 @@ export default class TruncatedList extends React.Component<IProps> {
|
|||
}
|
||||
}
|
||||
|
||||
public render(): React.ReactNode {
|
||||
let overflowNode = null;
|
||||
public render(): ReactNode {
|
||||
let overflowNode: ReactNode | undefined;
|
||||
|
||||
const totalChildren = this.getChildCount();
|
||||
let upperBound = totalChildren;
|
||||
|
|
|
@ -37,6 +37,7 @@ import RoomContext, { TimelineRenderingType } from "../../../contexts/RoomContex
|
|||
import { ReadPinsEventId } from "./types";
|
||||
import Heading from "../typography/Heading";
|
||||
import { RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks";
|
||||
import { filterBoolean } from "../../../utils/arrays";
|
||||
|
||||
interface IProps {
|
||||
room: Room;
|
||||
|
@ -44,7 +45,7 @@ interface IProps {
|
|||
onClose(): void;
|
||||
}
|
||||
|
||||
export const usePinnedEvents = (room: Room): string[] => {
|
||||
export const usePinnedEvents = (room?: Room): string[] => {
|
||||
const [pinnedEvents, setPinnedEvents] = useState<string[]>([]);
|
||||
|
||||
const update = useCallback(
|
||||
|
@ -173,8 +174,7 @@ const PinnedMessagesCard: React.FC<IProps> = ({ room, onClose, permalinkCreator
|
|||
};
|
||||
|
||||
// show them in reverse, with latest pinned at the top
|
||||
content = pinnedEvents
|
||||
.filter(Boolean)
|
||||
content = filterBoolean(pinnedEvents)
|
||||
.reverse()
|
||||
.map((ev) => (
|
||||
<PinnedEventTile
|
||||
|
|
|
@ -325,7 +325,7 @@ const RoomSummaryCard: React.FC<IProps> = ({ room, permalinkCreator, onClose })
|
|||
|
||||
const memberCount = useRoomMemberCount(room);
|
||||
const pinningEnabled = useFeatureEnabled("feature_pinning");
|
||||
const pinCount = usePinnedEvents(pinningEnabled && room)?.length;
|
||||
const pinCount = usePinnedEvents(pinningEnabled ? room : undefined)?.length;
|
||||
|
||||
const isPollHistoryEnabled = useFeatureEnabled("feature_poll_history");
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ const crossSigningRoomTitles: { [key in E2EState]?: string } = {
|
|||
|
||||
interface IProps {
|
||||
isUser?: boolean;
|
||||
status?: E2EState | E2EStatus;
|
||||
status: E2EState | E2EStatus;
|
||||
className?: string;
|
||||
size?: number;
|
||||
onClick?: () => void;
|
||||
|
@ -76,7 +76,7 @@ const E2EIcon: React.FC<IProps> = ({
|
|||
className,
|
||||
);
|
||||
|
||||
let e2eTitle;
|
||||
let e2eTitle: string | undefined;
|
||||
if (isUser) {
|
||||
e2eTitle = crossSigningUserTitles[status];
|
||||
} else {
|
||||
|
|
|
@ -43,7 +43,7 @@ const PRESENCE_CLASS: Record<PresenceState, string> = {
|
|||
unavailable: "mx_EntityTile_unavailable",
|
||||
};
|
||||
|
||||
function presenceClassForMember(presenceState: PresenceState, lastActiveAgo: number, showPresence: boolean): string {
|
||||
function presenceClassForMember(presenceState?: PresenceState, lastActiveAgo?: number, showPresence?: boolean): string {
|
||||
if (showPresence === false) {
|
||||
return "mx_EntityTile_online_beenactive";
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ interface IProps {
|
|||
presenceLastTs?: number;
|
||||
presenceCurrentlyActive?: boolean;
|
||||
showInviteButton?: boolean;
|
||||
onClick?(): void;
|
||||
onClick(): void;
|
||||
suppressOnHover?: boolean;
|
||||
showPresence?: boolean;
|
||||
subtextLabel?: string;
|
||||
|
@ -108,7 +108,7 @@ export default class EntityTile extends React.PureComponent<IProps, IState> {
|
|||
public render(): React.ReactNode {
|
||||
const mainClassNames: Record<string, boolean> = {
|
||||
mx_EntityTile: true,
|
||||
mx_EntityTile_noHover: this.props.suppressOnHover,
|
||||
mx_EntityTile_noHover: !!this.props.suppressOnHover,
|
||||
};
|
||||
if (this.props.className) mainClassNames[this.props.className] = true;
|
||||
|
||||
|
@ -127,7 +127,7 @@ export default class EntityTile extends React.PureComponent<IProps, IState> {
|
|||
? Date.now() - (this.props.presenceLastTs - this.props.presenceLastActiveAgo)
|
||||
: -1;
|
||||
|
||||
let presenceLabel = null;
|
||||
let presenceLabel: JSX.Element | undefined;
|
||||
if (this.props.showPresence) {
|
||||
presenceLabel = (
|
||||
<PresenceLabel
|
||||
|
|
|
@ -39,7 +39,7 @@ export default class PresenceLabel extends React.Component<IProps> {
|
|||
|
||||
// Return duration as a string using appropriate time units
|
||||
// XXX: This would be better handled using a culture-aware library, but we don't use one yet.
|
||||
private getDuration(time: number): string {
|
||||
private getDuration(time: number): string | undefined {
|
||||
if (!time) return;
|
||||
const t = Math.round(time / 1000);
|
||||
const s = t % 60;
|
||||
|
@ -61,11 +61,11 @@ export default class PresenceLabel extends React.Component<IProps> {
|
|||
return _t("%(duration)sd", { duration: d });
|
||||
}
|
||||
|
||||
private getPrettyPresence(presence: string, activeAgo: number, currentlyActive: boolean): string {
|
||||
private getPrettyPresence(presence?: string, activeAgo?: number, currentlyActive?: boolean): string {
|
||||
// for busy presence, we ignore the 'currentlyActive' flag: they're busy whether
|
||||
// they're active or not. It can be set while the user is active in which case
|
||||
// the 'active ago' ends up being 0.
|
||||
if (BUSY_PRESENCE_NAME.matches(presence)) return _t("Busy");
|
||||
if (presence && BUSY_PRESENCE_NAME.matches(presence)) return _t("Busy");
|
||||
|
||||
if (!currentlyActive && activeAgo !== undefined && activeAgo > 0) {
|
||||
const duration = this.getDuration(activeAgo);
|
||||
|
|
|
@ -129,9 +129,9 @@ interface IProps {
|
|||
|
||||
interface IState {
|
||||
verifying: boolean;
|
||||
verifyError: string;
|
||||
verifyError: string | null;
|
||||
verifyMsisdn: string;
|
||||
addTask: AddThreepid;
|
||||
addTask: AddThreepid | null;
|
||||
continueDisabled: boolean;
|
||||
phoneCountry: string;
|
||||
newPhoneNumber: string;
|
||||
|
@ -205,7 +205,7 @@ export default class PhoneNumbers extends React.Component<IProps, IState> {
|
|||
const token = this.state.newPhoneNumberCode;
|
||||
const address = this.state.verifyMsisdn;
|
||||
this.state.addTask
|
||||
.haveMsisdnToken(token)
|
||||
?.haveMsisdnToken(token)
|
||||
.then(([finished]) => {
|
||||
let newPhoneNumber = this.state.newPhoneNumber;
|
||||
if (finished) {
|
||||
|
|
|
@ -21,6 +21,7 @@ import { RoomState, RoomStateEvent } from "matrix-js-sdk/src/models/room-state";
|
|||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { throttle, get } from "lodash";
|
||||
import { compare } from "matrix-js-sdk/src/utils";
|
||||
import { IContent } from "matrix-js-sdk/src/models/event";
|
||||
|
||||
import { _t, _td } from "../../../../../languageHandler";
|
||||
import { MatrixClientPeg } from "../../../../../MatrixClientPeg";
|
||||
|
@ -171,8 +172,8 @@ export default class RolesRoomSettingsTab extends React.Component<IProps> {
|
|||
private onPowerLevelsChanged = (value: number, powerLevelKey: string): void => {
|
||||
const client = MatrixClientPeg.get();
|
||||
const room = client.getRoom(this.props.roomId);
|
||||
const plEvent = room.currentState.getStateEvents(EventType.RoomPowerLevels, "");
|
||||
let plContent = plEvent ? plEvent.getContent() || {} : {};
|
||||
const plEvent = room?.currentState.getStateEvents(EventType.RoomPowerLevels, "");
|
||||
let plContent = plEvent?.getContent() ?? {};
|
||||
|
||||
// Clone the power levels just in case
|
||||
plContent = Object.assign({}, plContent);
|
||||
|
@ -185,7 +186,7 @@ export default class RolesRoomSettingsTab extends React.Component<IProps> {
|
|||
plContent["events"][powerLevelKey.slice(eventsLevelPrefix.length)] = value;
|
||||
} else {
|
||||
const keyPath = powerLevelKey.split(".");
|
||||
let parentObj;
|
||||
let parentObj: IContent | undefined;
|
||||
let currentObj = plContent;
|
||||
for (const key of keyPath) {
|
||||
if (!currentObj[key]) {
|
||||
|
@ -213,8 +214,8 @@ export default class RolesRoomSettingsTab extends React.Component<IProps> {
|
|||
private onUserPowerLevelChanged = (value: number, powerLevelKey: string): void => {
|
||||
const client = MatrixClientPeg.get();
|
||||
const room = client.getRoom(this.props.roomId);
|
||||
const plEvent = room.currentState.getStateEvents(EventType.RoomPowerLevels, "");
|
||||
let plContent = plEvent ? plEvent.getContent() || {} : {};
|
||||
const plEvent = room?.currentState.getStateEvents(EventType.RoomPowerLevels, "");
|
||||
let plContent = plEvent?.getContent() ?? {};
|
||||
|
||||
// Clone the power levels just in case
|
||||
plContent = Object.assign({}, plContent);
|
||||
|
|
|
@ -113,7 +113,7 @@ export default class WidgetStore extends AsyncStoreWithClient<IState> {
|
|||
});
|
||||
}
|
||||
|
||||
private loadRoomWidgets(room: Room): void {
|
||||
private loadRoomWidgets(room: Room | null): void {
|
||||
if (!room) return;
|
||||
const roomInfo = this.roomMap.get(room.roomId) || <IRoomWidgets>{};
|
||||
roomInfo.widgets = [];
|
||||
|
|
|
@ -18,7 +18,7 @@ import { Room } from "matrix-js-sdk/src/matrix";
|
|||
|
||||
import { LocalRoom, LOCAL_ROOM_ID_PREFIX } from "../../models/LocalRoom";
|
||||
|
||||
export function isLocalRoom(roomOrID: Room | string): boolean {
|
||||
export function isLocalRoom(roomOrID?: Room | string): boolean {
|
||||
if (typeof roomOrID === "string") {
|
||||
return roomOrID.startsWith(LOCAL_ROOM_ID_PREFIX);
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import React from "react";
|
|||
import { act, fireEvent, render } from "@testing-library/react";
|
||||
|
||||
import TabbedView, { Tab, TabLocation } from "../../../src/components/structures/TabbedView";
|
||||
import { NonEmptyArray } from "../../../src/@types/common";
|
||||
|
||||
describe("<TabbedView />", () => {
|
||||
const generalTab = new Tab("GENERAL", "General", "general", <div>general</div>);
|
||||
|
@ -25,7 +26,7 @@ describe("<TabbedView />", () => {
|
|||
const securityTab = new Tab("SECURITY", "Security", "security", <div>security</div>);
|
||||
const defaultProps = {
|
||||
tabLocation: TabLocation.LEFT,
|
||||
tabs: [generalTab, labsTab, securityTab],
|
||||
tabs: [generalTab, labsTab, securityTab] as NonEmptyArray<Tab>,
|
||||
};
|
||||
const getComponent = (props = {}): React.ReactElement => <TabbedView {...defaultProps} {...props} />;
|
||||
|
||||
|
@ -58,11 +59,6 @@ describe("<TabbedView />", () => {
|
|||
expect(getActiveTabBody(container)?.textContent).toEqual("security");
|
||||
});
|
||||
|
||||
it("renders without error when there are no tabs", () => {
|
||||
const { container } = render(getComponent({ tabs: [] }));
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("sets active tab on tab click", () => {
|
||||
const { container, getByTestId } = render(getComponent());
|
||||
|
||||
|
|
|
@ -69,15 +69,3 @@ exports[`<TabbedView /> renders tabs 1`] = `
|
|||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`<TabbedView /> renders without error when there are no tabs 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="mx_TabbedView mx_TabbedView_tabsOnLeft"
|
||||
>
|
||||
<div
|
||||
class="mx_TabbedView_tabLabels"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
@ -38,7 +38,7 @@ exports[`<PollHistoryDialog /> renders a list of active polls when there are pol
|
|||
/>
|
||||
<div
|
||||
aria-labelledby="mx_BaseDialog_title"
|
||||
class="undefined mx_Dialog_fixedWidth"
|
||||
class="mx_Dialog_fixedWidth"
|
||||
data-focus-lock-disabled="false"
|
||||
role="dialog"
|
||||
>
|
||||
|
|
|
@ -190,8 +190,8 @@ describe("BreadcrumbsStore", () => {
|
|||
/**
|
||||
* Create as many fake rooms in an array as you ask for.
|
||||
*/
|
||||
function fakeRooms(howMany: number): Array<Room> {
|
||||
const ret = [];
|
||||
function fakeRooms(howMany: number): Room[] {
|
||||
const ret: Room[] = [];
|
||||
for (let i = 0; i < howMany; i++) {
|
||||
ret.push(fakeRoom());
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue