Conform more code to strict null checking (#10167)
* Conform more code to strict null checking * Delint * Iterate PR based on feedback
This commit is contained in:
parent
f7bea2cae5
commit
4574c665ea
103 changed files with 517 additions and 495 deletions
|
@ -105,7 +105,7 @@ export interface IProps extends MenuProps {
|
|||
}
|
||||
|
||||
interface IState {
|
||||
contextMenuElem: HTMLDivElement;
|
||||
contextMenuElem?: HTMLDivElement;
|
||||
}
|
||||
|
||||
// Generic ContextMenu Portal wrapper
|
||||
|
@ -122,9 +122,7 @@ export default class ContextMenu extends React.PureComponent<IProps, IState> {
|
|||
public constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
contextMenuElem: null,
|
||||
};
|
||||
this.state = {};
|
||||
|
||||
// persist what had focus when we got initialized so we can return it after
|
||||
this.initialFocus = document.activeElement as HTMLElement;
|
||||
|
@ -181,7 +179,7 @@ export default class ContextMenu extends React.PureComponent<IProps, IState> {
|
|||
button: 0, // Left
|
||||
relatedTarget: null,
|
||||
});
|
||||
document.elementFromPoint(x, y).dispatchEvent(clickEvent);
|
||||
document.elementFromPoint(x, y)?.dispatchEvent(clickEvent);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -239,7 +237,7 @@ export default class ContextMenu extends React.PureComponent<IProps, IState> {
|
|||
// MessageActionBar), we should close any ContextMenu that is open.
|
||||
KeyBindingAction.ArrowLeft,
|
||||
KeyBindingAction.ArrowRight,
|
||||
].includes(action)
|
||||
].includes(action!)
|
||||
) {
|
||||
this.props.onFinished();
|
||||
}
|
||||
|
@ -312,12 +310,12 @@ export default class ContextMenu extends React.PureComponent<IProps, IState> {
|
|||
position.top = Math.min(position.top, maxTop);
|
||||
// Adjust the chevron if necessary
|
||||
if (chevronOffset.top !== undefined) {
|
||||
chevronOffset.top = propsChevronOffset + top - position.top;
|
||||
chevronOffset.top = propsChevronOffset! + top! - position.top;
|
||||
}
|
||||
} else if (position.bottom !== undefined) {
|
||||
position.bottom = Math.min(position.bottom, windowHeight - contextMenuRect.height - WINDOW_PADDING);
|
||||
if (chevronOffset.top !== undefined) {
|
||||
chevronOffset.top = propsChevronOffset + position.bottom - bottom;
|
||||
chevronOffset.top = propsChevronOffset! + position.bottom - bottom!;
|
||||
}
|
||||
}
|
||||
if (position.left !== undefined) {
|
||||
|
@ -327,12 +325,12 @@ export default class ContextMenu extends React.PureComponent<IProps, IState> {
|
|||
}
|
||||
position.left = Math.min(position.left, maxLeft);
|
||||
if (chevronOffset.left !== undefined) {
|
||||
chevronOffset.left = propsChevronOffset + left - position.left;
|
||||
chevronOffset.left = propsChevronOffset! + left! - position.left;
|
||||
}
|
||||
} else if (position.right !== undefined) {
|
||||
position.right = Math.min(position.right, windowWidth - contextMenuRect.width - WINDOW_PADDING);
|
||||
if (chevronOffset.left !== undefined) {
|
||||
chevronOffset.left = propsChevronOffset + position.right - right;
|
||||
chevronOffset.left = propsChevronOffset! + position.right - right!;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -389,7 +387,7 @@ export default class ContextMenu extends React.PureComponent<IProps, IState> {
|
|||
|
||||
const wrapperStyle: CSSProperties = {};
|
||||
if (!isNaN(Number(zIndex))) {
|
||||
menuStyle["zIndex"] = zIndex + 1;
|
||||
menuStyle["zIndex"] = zIndex! + 1;
|
||||
wrapperStyle["zIndex"] = zIndex;
|
||||
}
|
||||
|
||||
|
|
|
@ -97,7 +97,7 @@ class DMUserTile extends React.PureComponent<IDMUserTileProps> {
|
|||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
this.props.onRemove(this.props.member);
|
||||
this.props.onRemove!(this.props.member);
|
||||
};
|
||||
|
||||
public render(): React.ReactNode {
|
||||
|
@ -132,7 +132,7 @@ class DMUserTile extends React.PureComponent<IDMUserTileProps> {
|
|||
|
||||
interface IDMRoomTileProps {
|
||||
member: Member;
|
||||
lastActiveTs: number;
|
||||
lastActiveTs?: number;
|
||||
onToggle(member: Member): void;
|
||||
highlightWord: string;
|
||||
isSelected: boolean;
|
||||
|
@ -188,7 +188,7 @@ class DMRoomTile extends React.PureComponent<IDMRoomTileProps> {
|
|||
}
|
||||
|
||||
public render(): React.ReactNode {
|
||||
let timestamp = null;
|
||||
let timestamp: JSX.Element | undefined;
|
||||
if (this.props.lastActiveTs) {
|
||||
const humanTs = humanizeTime(this.props.lastActiveTs);
|
||||
timestamp = <span className="mx_InviteDialog_tile--room_time">{humanTs}</span>;
|
||||
|
@ -201,7 +201,7 @@ class DMRoomTile extends React.PureComponent<IDMRoomTileProps> {
|
|||
<BaseAvatar
|
||||
url={
|
||||
this.props.member.getMxcAvatarUrl()
|
||||
? mediaFromMxc(this.props.member.getMxcAvatarUrl()).getSquareThumbnailHttp(avatarSize)
|
||||
? mediaFromMxc(this.props.member.getMxcAvatarUrl()!).getSquareThumbnailHttp(avatarSize)
|
||||
: null
|
||||
}
|
||||
name={this.props.member.name}
|
||||
|
@ -211,7 +211,7 @@ class DMRoomTile extends React.PureComponent<IDMRoomTileProps> {
|
|||
/>
|
||||
);
|
||||
|
||||
let checkmark = null;
|
||||
let checkmark: JSX.Element | undefined;
|
||||
if (this.props.isSelected) {
|
||||
// To reduce flickering we put the 'selected' room tile above the real avatar
|
||||
checkmark = <div className="mx_InviteDialog_tile--room_selected" />;
|
||||
|
@ -301,7 +301,7 @@ interface IInviteDialogState {
|
|||
|
||||
// These two flags are used for the 'Go' button to communicate what is going on.
|
||||
busy: boolean;
|
||||
errorText: string;
|
||||
errorText?: string;
|
||||
}
|
||||
|
||||
export default class InviteDialog extends React.PureComponent<Props, IInviteDialogState> {
|
||||
|
@ -324,7 +324,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
|
|||
throw new Error("When using KIND_CALL_TRANSFER a call is required for an InviteDialog");
|
||||
}
|
||||
|
||||
const alreadyInvited = new Set([MatrixClientPeg.get().getUserId(), SdkConfig.get("welcome_user_id")]);
|
||||
const alreadyInvited = new Set([MatrixClientPeg.get().getUserId()!, SdkConfig.get("welcome_user_id")]);
|
||||
if (isRoomInvite(props)) {
|
||||
const room = MatrixClientPeg.get().getRoom(props.roomId);
|
||||
if (!room) throw new Error("Room ID given to InviteDialog does not look like a room");
|
||||
|
@ -351,7 +351,6 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
|
|||
|
||||
// These two flags are used for the 'Go' button to communicate what is going on.
|
||||
busy: false,
|
||||
errorText: null,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -386,7 +385,11 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
|
|||
}
|
||||
}
|
||||
|
||||
const recents = [];
|
||||
const recents: {
|
||||
userId: string;
|
||||
user: RoomMember;
|
||||
lastActive: number;
|
||||
}[] = [];
|
||||
for (const userId in rooms) {
|
||||
// Filter out user IDs that are already in the room / should be excluded
|
||||
if (excludedTargetIds.has(userId)) {
|
||||
|
@ -455,14 +458,16 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
|
|||
// Check to see if there's anything to convert first
|
||||
if (!this.state.filterText || !this.state.filterText.includes("@")) return this.state.targets || [];
|
||||
|
||||
let newMember: Member;
|
||||
let newMember: Member | undefined;
|
||||
if (this.state.filterText.startsWith("@")) {
|
||||
// Assume mxid
|
||||
newMember = new DirectoryMember({ user_id: this.state.filterText, display_name: null, avatar_url: null });
|
||||
newMember = new DirectoryMember({ user_id: this.state.filterText });
|
||||
} else if (SettingsStore.getValue(UIFeature.IdentityServer)) {
|
||||
// Assume email
|
||||
newMember = new ThreepidMember(this.state.filterText);
|
||||
}
|
||||
if (!newMember) return this.state.targets;
|
||||
|
||||
const newTargets = [...(this.state.targets || []), newMember];
|
||||
this.setState({ targets: newTargets, filterText: "" });
|
||||
return newTargets;
|
||||
|
@ -778,8 +783,8 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
|
|||
...this.state.serverResultsMixin,
|
||||
...this.state.threepidResultsMixin,
|
||||
];
|
||||
const toAdd = [];
|
||||
const failed = [];
|
||||
const toAdd: Member[] = [];
|
||||
const failed: string[] = [];
|
||||
const potentialAddresses = text
|
||||
.split(/[\s,]+/)
|
||||
.map((p) => p.trim())
|
||||
|
@ -803,13 +808,11 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
|
|||
|
||||
try {
|
||||
const profile = await MatrixClientPeg.get().getProfileInfo(address);
|
||||
const displayName = profile ? profile.displayname : null;
|
||||
const avatarUrl = profile ? profile.avatar_url : null;
|
||||
toAdd.push(
|
||||
new DirectoryMember({
|
||||
user_id: address,
|
||||
display_name: displayName,
|
||||
avatar_url: avatarUrl,
|
||||
display_name: profile?.displayname,
|
||||
avatar_url: profile?.avatar_url,
|
||||
}),
|
||||
);
|
||||
} catch (e) {
|
||||
|
@ -859,11 +862,11 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
|
|||
this.props.onFinished(false);
|
||||
};
|
||||
|
||||
private renderSection(kind: "recents" | "suggestions"): JSX.Element {
|
||||
private renderSection(kind: "recents" | "suggestions"): ReactNode {
|
||||
let sourceMembers = kind === "recents" ? this.state.recents : this.state.suggestions;
|
||||
let showNum = kind === "recents" ? this.state.numRecentsShown : this.state.numSuggestionsShown;
|
||||
const showMoreFn = kind === "recents" ? this.showMoreRecents.bind(this) : this.showMoreSuggestions.bind(this);
|
||||
const lastActive = (m: Result): number | null => (kind === "recents" ? m.lastActive : null);
|
||||
const lastActive = (m: Result): number | undefined => (kind === "recents" ? m.lastActive : undefined);
|
||||
let sectionName = kind === "recents" ? _t("Recent Conversations") : _t("Suggestions");
|
||||
|
||||
if (this.props.kind === KIND_INVITE) {
|
||||
|
@ -924,7 +927,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
|
|||
const toRender = sourceMembers.slice(0, showNum);
|
||||
const hasMore = toRender.length < sourceMembers.length;
|
||||
|
||||
let showMore = null;
|
||||
let showMore: JSX.Element | undefined;
|
||||
if (hasMore) {
|
||||
showMore = (
|
||||
<div className="mx_InviteDialog_section_showMore">
|
||||
|
@ -960,7 +963,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
|
|||
this.state.targets.length === 0 &&
|
||||
this.state.filterText.length === 0;
|
||||
const targets = this.state.targets.map((t) => (
|
||||
<DMUserTile member={t} onRemove={!this.state.busy && this.removeMember} key={t.userId} />
|
||||
<DMUserTile member={t} onRemove={this.state.busy ? undefined : this.removeMember} key={t.userId} />
|
||||
));
|
||||
const input = (
|
||||
<input
|
||||
|
@ -973,7 +976,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
|
|||
autoFocus={true}
|
||||
disabled={this.state.busy || (this.props.kind == KIND_CALL_TRANSFER && this.state.targets.length > 0)}
|
||||
autoComplete="off"
|
||||
placeholder={hasPlaceholder ? _t("Search") : null}
|
||||
placeholder={hasPlaceholder ? _t("Search") : undefined}
|
||||
data-testid="invite-dialog-input"
|
||||
/>
|
||||
);
|
||||
|
@ -985,7 +988,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
|
|||
);
|
||||
}
|
||||
|
||||
private renderIdentityServerWarning(): JSX.Element {
|
||||
private renderIdentityServerWarning(): ReactNode {
|
||||
if (
|
||||
!this.state.tryingIdentityServer ||
|
||||
this.state.canUseIdentityServer ||
|
||||
|
@ -1080,7 +1083,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
|
|||
selectText(e.currentTarget);
|
||||
}
|
||||
|
||||
private get screenName(): ScreenName {
|
||||
private get screenName(): ScreenName | undefined {
|
||||
switch (this.props.kind) {
|
||||
case KIND_DM:
|
||||
return "StartChat";
|
||||
|
@ -1088,7 +1091,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
|
|||
}
|
||||
|
||||
public render(): React.ReactNode {
|
||||
let spinner = null;
|
||||
let spinner: JSX.Element | undefined;
|
||||
if (this.state.busy) {
|
||||
spinner = <Spinner w={20} h={20} />;
|
||||
}
|
||||
|
@ -1108,7 +1111,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
|
|||
this.state.targets.length > 0 || (this.state.filterText && this.state.filterText.includes("@"));
|
||||
|
||||
const cli = MatrixClientPeg.get();
|
||||
const userId = cli.getUserId();
|
||||
const userId = cli.getUserId()!;
|
||||
if (this.props.kind === KIND_DM) {
|
||||
title = _t("Direct Messages");
|
||||
|
||||
|
@ -1150,11 +1153,11 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
|
|||
<p>{_t("If you can't see who you're looking for, send them your invite link below.")}</p>
|
||||
</div>
|
||||
);
|
||||
const link = makeUserPermalink(MatrixClientPeg.get().getUserId());
|
||||
const link = makeUserPermalink(MatrixClientPeg.get().getUserId()!);
|
||||
footer = (
|
||||
<div className="mx_InviteDialog_footer">
|
||||
<h3>{_t("Or send invite link")}</h3>
|
||||
<CopyableText getTextToCopy={() => makeUserPermalink(MatrixClientPeg.get().getUserId())}>
|
||||
<CopyableText getTextToCopy={() => makeUserPermalink(MatrixClientPeg.get().getUserId()!)}>
|
||||
<a href={link} onClick={this.onLinkClick}>
|
||||
{link}
|
||||
</a>
|
||||
|
@ -1296,7 +1299,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
|
|||
|
||||
let dialogContent;
|
||||
if (this.props.kind === KIND_CALL_TRANSFER) {
|
||||
const tabs = [];
|
||||
const tabs: Tab[] = [];
|
||||
tabs.push(
|
||||
new Tab(TabId.UserDirectory, _td("User Directory"), "mx_InviteDialog_userDirectoryIcon", usersSection),
|
||||
);
|
||||
|
|
|
@ -179,7 +179,7 @@ const toPublicRoomResult = (publicRoom: IPublicRoomsChunkRoom): IPublicRoomResul
|
|||
publicRoom.name?.toLowerCase(),
|
||||
sanitizeHtml(publicRoom.topic?.toLowerCase() ?? "", { allowedTags: [] }),
|
||||
...(publicRoom.aliases?.map((it) => it.toLowerCase()) || []),
|
||||
].filter(Boolean),
|
||||
].filter(Boolean) as string[],
|
||||
});
|
||||
|
||||
const toRoomResult = (room: Room): IRoomResult => {
|
||||
|
@ -310,7 +310,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
|
|||
}, [cli]);
|
||||
const msc3946ProcessDynamicPredecessor = useFeatureEnabled("feature_dynamic_room_predecessors");
|
||||
|
||||
const ownInviteLink = makeUserPermalink(cli.getUserId());
|
||||
const ownInviteLink = makeUserPermalink(cli.getUserId()!);
|
||||
const [inviteLinkCopied, setInviteLinkCopied] = useState<boolean>(false);
|
||||
const trimmedQuery = useMemo(() => query.trim(), [query]);
|
||||
|
||||
|
@ -465,7 +465,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
|
|||
useWebSearchMetrics(numResults, query.length, true);
|
||||
|
||||
const activeSpace = SpaceStore.instance.activeSpaceRoom;
|
||||
const [spaceResults, spaceResultsLoading] = useSpaceResults(activeSpace, query);
|
||||
const [spaceResults, spaceResultsLoading] = useSpaceResults(activeSpace ?? undefined, query);
|
||||
|
||||
const setQuery = (e: ChangeEvent<HTMLInputElement>): void => {
|
||||
const newQuery = e.currentTarget.value;
|
||||
|
@ -473,7 +473,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
|
|||
};
|
||||
useEffect(() => {
|
||||
setImmediate(() => {
|
||||
let ref: Ref;
|
||||
let ref: Ref | undefined;
|
||||
if (rovingContext.state.refs) {
|
||||
ref = rovingContext.state.refs[0];
|
||||
}
|
||||
|
@ -521,7 +521,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
|
|||
onFinished();
|
||||
};
|
||||
|
||||
let otherSearchesSection: JSX.Element;
|
||||
let otherSearchesSection: JSX.Element | undefined;
|
||||
if (trimmedQuery || filter !== Filter.PublicRooms) {
|
||||
otherSearchesSection = (
|
||||
<div
|
||||
|
@ -693,7 +693,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
|
|||
);
|
||||
};
|
||||
|
||||
let peopleSection: JSX.Element;
|
||||
let peopleSection: JSX.Element | undefined;
|
||||
if (results[Section.People].length) {
|
||||
peopleSection = (
|
||||
<div
|
||||
|
@ -707,7 +707,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
|
|||
);
|
||||
}
|
||||
|
||||
let suggestionsSection: JSX.Element;
|
||||
let suggestionsSection: JSX.Element | undefined;
|
||||
if (results[Section.Suggestions].length && filter === Filter.People) {
|
||||
suggestionsSection = (
|
||||
<div
|
||||
|
@ -721,7 +721,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
|
|||
);
|
||||
}
|
||||
|
||||
let roomsSection: JSX.Element;
|
||||
let roomsSection: JSX.Element | undefined;
|
||||
if (results[Section.Rooms].length) {
|
||||
roomsSection = (
|
||||
<div
|
||||
|
@ -735,7 +735,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
|
|||
);
|
||||
}
|
||||
|
||||
let spacesSection: JSX.Element;
|
||||
let spacesSection: JSX.Element | undefined;
|
||||
if (results[Section.Spaces].length) {
|
||||
spacesSection = (
|
||||
<div
|
||||
|
@ -749,7 +749,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
|
|||
);
|
||||
}
|
||||
|
||||
let publicRoomsSection: JSX.Element;
|
||||
let publicRoomsSection: JSX.Element | undefined;
|
||||
if (filter === Filter.PublicRooms) {
|
||||
publicRoomsSection = (
|
||||
<div
|
||||
|
@ -791,7 +791,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
|
|||
);
|
||||
}
|
||||
|
||||
let spaceRoomsSection: JSX.Element;
|
||||
let spaceRoomsSection: JSX.Element | undefined;
|
||||
if (spaceResults.length && activeSpace && filter === null) {
|
||||
spaceRoomsSection = (
|
||||
<div
|
||||
|
@ -836,7 +836,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
|
|||
);
|
||||
}
|
||||
|
||||
let joinRoomSection: JSX.Element;
|
||||
let joinRoomSection: JSX.Element | undefined;
|
||||
if (
|
||||
trimmedQuery.startsWith("#") &&
|
||||
trimmedQuery.includes(":") &&
|
||||
|
@ -868,7 +868,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
|
|||
);
|
||||
}
|
||||
|
||||
let hiddenResultsSection: JSX.Element;
|
||||
let hiddenResultsSection: JSX.Element | undefined;
|
||||
if (filter === Filter.People) {
|
||||
hiddenResultsSection = (
|
||||
<div className="mx_SpotlightDialog_section mx_SpotlightDialog_hiddenResults" role="group">
|
||||
|
@ -921,7 +921,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
|
|||
);
|
||||
}
|
||||
|
||||
let groupChatSection: JSX.Element;
|
||||
let groupChatSection: JSX.Element | undefined;
|
||||
if (filter === Filter.People) {
|
||||
groupChatSection = (
|
||||
<div
|
||||
|
@ -941,7 +941,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
|
|||
);
|
||||
}
|
||||
|
||||
let messageSearchSection: JSX.Element;
|
||||
let messageSearchSection: JSX.Element | undefined;
|
||||
if (filter === null) {
|
||||
messageSearchSection = (
|
||||
<div
|
||||
|
@ -977,7 +977,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
|
|||
</>
|
||||
);
|
||||
} else {
|
||||
let recentSearchesSection: JSX.Element;
|
||||
let recentSearchesSection: JSX.Element | undefined;
|
||||
if (recentSearches.length) {
|
||||
recentSearchesSection = (
|
||||
<div
|
||||
|
@ -1075,7 +1075,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
|
|||
break;
|
||||
}
|
||||
|
||||
let ref: RefObject<HTMLElement>;
|
||||
let ref: RefObject<HTMLElement> | undefined;
|
||||
const accessibilityAction = getKeyBindingsManager().getAccessibilityAction(ev);
|
||||
switch (accessibilityAction) {
|
||||
case KeyBindingAction.Escape:
|
||||
|
|
|
@ -24,11 +24,11 @@ import { chromeFileInputFix } from "../../../utils/BrowserWorkarounds";
|
|||
interface IProps {
|
||||
avatarUrl?: string;
|
||||
avatarDisabled?: boolean;
|
||||
name?: string;
|
||||
name: string;
|
||||
nameDisabled?: boolean;
|
||||
topic?: string;
|
||||
topicDisabled?: boolean;
|
||||
setAvatar(avatar: File): void;
|
||||
setAvatar(avatar?: File): void;
|
||||
setName(name: string): void;
|
||||
setTopic(topic: string): void;
|
||||
}
|
||||
|
@ -102,7 +102,7 @@ export const SpaceAvatar: React.FC<Pick<IProps, "avatarUrl" | "avatarDisabled" |
|
|||
setAvatar(file);
|
||||
const reader = new FileReader();
|
||||
reader.onload = (ev) => {
|
||||
setAvatarDataUrl(ev.target.result as string);
|
||||
setAvatarDataUrl(ev.target?.result as string);
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}}
|
||||
|
|
|
@ -53,7 +53,7 @@ const SpecificChildrenPicker: React.FC<ISpecificChildrenPickerProps> = ({
|
|||
|
||||
const matcher = new QueryMatcher<Room>(rooms, {
|
||||
keys: ["name"],
|
||||
funcs: [(r) => [r.getCanonicalAlias(), ...r.getAltAliases()].filter(Boolean)],
|
||||
funcs: [(r) => [r.getCanonicalAlias(), ...r.getAltAliases()].filter(Boolean) as string[]],
|
||||
shouldMatchWordsOnly: false,
|
||||
});
|
||||
|
||||
|
|
|
@ -245,14 +245,14 @@ export const SpaceCreateForm: React.FC<ISpaceCreateFormProps> = ({
|
|||
const SpaceCreateMenu: React.FC<{
|
||||
onFinished(): void;
|
||||
}> = ({ onFinished }) => {
|
||||
const [visibility, setVisibility] = useState<Visibility>(null);
|
||||
const [visibility, setVisibility] = useState<Visibility | null>(null);
|
||||
const [busy, setBusy] = useState<boolean>(false);
|
||||
|
||||
const [name, setName] = useState("");
|
||||
const spaceNameField = useRef<Field>();
|
||||
const [alias, setAlias] = useState("");
|
||||
const spaceAliasField = useRef<RoomAliasField>();
|
||||
const [avatar, setAvatar] = useState<File>(null);
|
||||
const [avatar, setAvatar] = useState<File | undefined>(undefined);
|
||||
const [topic, setTopic] = useState<string>("");
|
||||
|
||||
const onSpaceCreateClick = async (e: ButtonEvent): Promise<void> => {
|
||||
|
|
|
@ -215,7 +215,7 @@ const CreateSpaceButton: React.FC<Pick<IInnerSpacePanelProps, "isPanelCollapsed"
|
|||
}
|
||||
}, [isPanelCollapsed]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
let contextMenu = null;
|
||||
let contextMenu: JSX.Element | undefined;
|
||||
if (menuDisplayed) {
|
||||
contextMenu = <SpaceCreateMenu onFinished={closeMenu} />;
|
||||
}
|
||||
|
|
|
@ -121,11 +121,11 @@ export const SpaceButton = forwardRef<HTMLElement, IButtonProps>(
|
|||
);
|
||||
}
|
||||
|
||||
let contextMenu: JSX.Element;
|
||||
if (menuDisplayed && ContextMenuComponent) {
|
||||
let contextMenu: JSX.Element | undefined;
|
||||
if (menuDisplayed && handle.current && ContextMenuComponent) {
|
||||
contextMenu = (
|
||||
<ContextMenuComponent
|
||||
{...toRightOf(handle.current?.getBoundingClientRect(), 0)}
|
||||
{...toRightOf(handle.current.getBoundingClientRect(), 0)}
|
||||
space={space}
|
||||
onFinished={closeMenu}
|
||||
/>
|
||||
|
@ -242,7 +242,7 @@ export class SpaceItem extends React.PureComponent<IItemProps, IItemState> {
|
|||
}
|
||||
|
||||
private get isCollapsed(): boolean {
|
||||
return this.state.collapsed || this.props.isPanelCollapsed;
|
||||
return this.state.collapsed || !!this.props.isPanelCollapsed;
|
||||
}
|
||||
|
||||
private toggleCollapse = (evt: ButtonEvent): void => {
|
||||
|
|
|
@ -30,7 +30,7 @@ const onClickSendDm = (ev: ButtonEvent): void => {
|
|||
};
|
||||
|
||||
interface Props {
|
||||
useCase: UseCase;
|
||||
useCase: UseCase | null;
|
||||
}
|
||||
|
||||
export function UserOnboardingHeader({ useCase }: Props): JSX.Element {
|
||||
|
|
|
@ -38,7 +38,7 @@ interface Props {
|
|||
// We decided to only show the new user onboarding page to new users
|
||||
// For now, that means we set the cutoff at 2022-07-01 00:00 UTC
|
||||
const USER_ONBOARDING_CUTOFF_DATE = new Date(1_656_633_600);
|
||||
export function showUserOnboardingPage(useCase: UseCase): boolean {
|
||||
export function showUserOnboardingPage(useCase: UseCase | null): boolean {
|
||||
return useCase !== null || MatrixClientPeg.userRegisteredAfter(USER_ONBOARDING_CUTOFF_DATE);
|
||||
}
|
||||
|
||||
|
@ -55,13 +55,11 @@ export function UserOnboardingPage({ justRegistered = false }: Props): JSX.Eleme
|
|||
const [showList, setShowList] = useState<boolean>(false);
|
||||
useEffect(() => {
|
||||
if (initialSyncComplete) {
|
||||
let handler: number | null = window.setTimeout(() => {
|
||||
handler = null;
|
||||
const handler = window.setTimeout(() => {
|
||||
setShowList(true);
|
||||
}, ANIMATION_DURATION);
|
||||
return () => {
|
||||
clearTimeout(handler);
|
||||
handler = null;
|
||||
};
|
||||
} else {
|
||||
setShowList(false);
|
||||
|
|
|
@ -18,6 +18,7 @@ import * as React from "react";
|
|||
|
||||
import AccessibleButton, { ButtonEvent } from "../elements/AccessibleButton";
|
||||
import { _t } from "../../../languageHandler";
|
||||
import { XOR } from "../../../@types/common";
|
||||
|
||||
const BUTTONS = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "*", "0", "#"];
|
||||
const BUTTON_LETTERS = ["", "ABC", "DEF", "GHI", "JKL", "MNO", "PQRS", "TUV", "WXYZ", "", "+", ""];
|
||||
|
@ -31,7 +32,7 @@ interface IButtonProps {
|
|||
kind: DialPadButtonKind;
|
||||
digit?: string;
|
||||
digitSubtext?: string;
|
||||
onButtonPress: (digit: string, ev: ButtonEvent) => void;
|
||||
onButtonPress: (digit: string | undefined, ev: ButtonEvent) => void;
|
||||
}
|
||||
|
||||
class DialPadButton extends React.PureComponent<IButtonProps> {
|
||||
|
@ -60,16 +61,24 @@ class DialPadButton extends React.PureComponent<IButtonProps> {
|
|||
}
|
||||
}
|
||||
|
||||
interface IProps {
|
||||
interface IBaseProps {
|
||||
onDigitPress: (digit: string, ev: ButtonEvent) => void;
|
||||
hasDial: boolean;
|
||||
onDeletePress?: (ev: ButtonEvent) => void;
|
||||
onDialPress?: () => void;
|
||||
hasDial: boolean;
|
||||
}
|
||||
|
||||
export default class Dialpad extends React.PureComponent<IProps> {
|
||||
interface IProps extends IBaseProps {
|
||||
hasDial: false;
|
||||
}
|
||||
|
||||
interface IDialProps extends IBaseProps {
|
||||
hasDial: true;
|
||||
onDialPress: () => void;
|
||||
}
|
||||
|
||||
export default class Dialpad extends React.PureComponent<XOR<IProps, IDialProps>> {
|
||||
public render(): React.ReactNode {
|
||||
const buttonNodes = [];
|
||||
const buttonNodes: JSX.Element[] = [];
|
||||
|
||||
for (let i = 0; i < BUTTONS.length; i++) {
|
||||
const button = BUTTONS[i];
|
||||
|
|
|
@ -76,7 +76,7 @@ interface IState {
|
|||
sidebarShown: boolean;
|
||||
}
|
||||
|
||||
function getFullScreenElement(): Element | undefined {
|
||||
function getFullScreenElement(): Element | null {
|
||||
return (
|
||||
document.fullscreenElement ||
|
||||
// moz omitted because firefox supports this unprefixed now (webkit here for safari)
|
||||
|
@ -180,7 +180,7 @@ export default class LegacyCallView extends React.Component<IProps, IState> {
|
|||
}
|
||||
};
|
||||
|
||||
private updateCallListeners(oldCall: MatrixCall, newCall: MatrixCall | null): void {
|
||||
private updateCallListeners(oldCall: MatrixCall | null, newCall: MatrixCall | null): void {
|
||||
if (oldCall === newCall) return;
|
||||
|
||||
if (oldCall) {
|
||||
|
@ -245,7 +245,7 @@ export default class LegacyCallView extends React.Component<IProps, IState> {
|
|||
};
|
||||
}
|
||||
|
||||
let primary: CallFeed;
|
||||
let primary: CallFeed | undefined;
|
||||
|
||||
// Try to use a screensharing as primary, a remote one if possible
|
||||
const screensharingFeeds = feeds.filter((feed) => feed.purpose === SDPStreamMetadataPurpose.Screenshare);
|
||||
|
@ -289,7 +289,7 @@ export default class LegacyCallView extends React.Component<IProps, IState> {
|
|||
if (this.state.screensharing) {
|
||||
isScreensharing = await this.props.call.setScreensharingEnabled(false);
|
||||
} else {
|
||||
if (PlatformPeg.get().supportsDesktopCapturer()) {
|
||||
if (PlatformPeg.get()?.supportsDesktopCapturer()) {
|
||||
const { finished } = Modal.createDialog<[string]>(DesktopCapturerSourcePicker);
|
||||
const [source] = await finished;
|
||||
if (!source) return;
|
||||
|
@ -403,7 +403,7 @@ export default class LegacyCallView extends React.Component<IProps, IState> {
|
|||
);
|
||||
}
|
||||
|
||||
private renderToast(): JSX.Element {
|
||||
private renderToast(): JSX.Element | null {
|
||||
const { call } = this.props;
|
||||
const someoneIsScreensharing = call.getFeeds().some((feed) => {
|
||||
return feed.purpose === SDPStreamMetadataPurpose.Screenshare;
|
||||
|
@ -413,8 +413,8 @@ export default class LegacyCallView extends React.Component<IProps, IState> {
|
|||
|
||||
const isScreensharing = call.isScreensharing();
|
||||
const { primaryFeed, sidebarShown } = this.state;
|
||||
const sharerName = primaryFeed?.getMember().name;
|
||||
if (!sharerName) return;
|
||||
const sharerName = primaryFeed?.getMember()?.name;
|
||||
if (!sharerName) return null;
|
||||
|
||||
let text = isScreensharing ? _t("You are presenting") : _t("%(sharerName)s is presenting", { sharerName });
|
||||
if (!sidebarShown) {
|
||||
|
@ -495,7 +495,7 @@ export default class LegacyCallView extends React.Component<IProps, IState> {
|
|||
);
|
||||
} else if (isLocalOnHold) {
|
||||
onHoldText = _t("%(peerName)s held the call", {
|
||||
peerName: call.getOpponentMember().name,
|
||||
peerName: call.getOpponentMember()?.name,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -556,8 +556,8 @@ export default class LegacyCallView extends React.Component<IProps, IState> {
|
|||
const client = MatrixClientPeg.get();
|
||||
const callRoomId = LegacyCallHandler.instance.roomIdForCall(call);
|
||||
const secondaryCallRoomId = LegacyCallHandler.instance.roomIdForCall(secondaryCall);
|
||||
const callRoom = client.getRoom(callRoomId);
|
||||
const secCallRoom = secondaryCall ? client.getRoom(secondaryCallRoomId) : null;
|
||||
const callRoom = callRoomId ? client.getRoom(callRoomId) : null;
|
||||
const secCallRoom = secondaryCallRoomId ? client.getRoom(secondaryCallRoomId) : null;
|
||||
|
||||
const callViewClasses = classNames({
|
||||
mx_LegacyCallView: true,
|
||||
|
|
|
@ -104,9 +104,9 @@ const LegacyCallViewDropdownButton: React.FC<IDropdownButtonProps> = ({ state, d
|
|||
onHover={(hovering) => setHoveringDropdown(hovering)}
|
||||
state={state}
|
||||
/>
|
||||
{menuDisplayed && (
|
||||
{menuDisplayed && buttonRef.current && (
|
||||
<DeviceContextMenu
|
||||
{...alwaysAboveRightOf(buttonRef.current?.getBoundingClientRect())}
|
||||
{...alwaysAboveRightOf(buttonRef.current.getBoundingClientRect())}
|
||||
onFinished={closeMenu}
|
||||
deviceKinds={deviceKinds}
|
||||
/>
|
||||
|
@ -117,7 +117,7 @@ const LegacyCallViewDropdownButton: React.FC<IDropdownButtonProps> = ({ state, d
|
|||
|
||||
interface IProps {
|
||||
call: MatrixCall;
|
||||
pipMode: boolean;
|
||||
pipMode?: boolean;
|
||||
handlers: {
|
||||
onHangupClick: () => void;
|
||||
onScreenshareClick: () => void;
|
||||
|
@ -150,7 +150,7 @@ interface IState {
|
|||
export default class LegacyCallViewButtons extends React.Component<IProps, IState> {
|
||||
private dialpadButton = createRef<HTMLDivElement>();
|
||||
private contextMenuButton = createRef<HTMLDivElement>();
|
||||
private controlsHideTimer: number = null;
|
||||
private controlsHideTimer: number | null = null;
|
||||
|
||||
public constructor(props: IProps) {
|
||||
super(props);
|
||||
|
@ -223,7 +223,7 @@ export default class LegacyCallViewButtons extends React.Component<IProps, IStat
|
|||
});
|
||||
|
||||
let dialPad;
|
||||
if (this.state.showDialpad) {
|
||||
if (this.state.showDialpad && this.dialpadButton.current) {
|
||||
dialPad = (
|
||||
<DialpadContextMenu
|
||||
{...alwaysMenuProps(
|
||||
|
@ -231,7 +231,7 @@ export default class LegacyCallViewButtons extends React.Component<IProps, IStat
|
|||
ChevronFace.None,
|
||||
CONTEXT_MENU_VPADDING,
|
||||
)}
|
||||
// We mount the context menus as a as a child typically in order to include the
|
||||
// We mount the context menus as a child typically in order to include the
|
||||
// context menus when fullscreening the call content.
|
||||
// However, this does not work as well when the call is embedded in a
|
||||
// picture-in-picture frame. Thus, only mount as child when we are *not* in PiP.
|
||||
|
@ -243,7 +243,7 @@ export default class LegacyCallViewButtons extends React.Component<IProps, IStat
|
|||
}
|
||||
|
||||
let contextMenu;
|
||||
if (this.state.showMoreMenu) {
|
||||
if (this.state.showMoreMenu && this.contextMenuButton.current) {
|
||||
contextMenu = (
|
||||
<LegacyCallContextMenu
|
||||
{...alwaysMenuProps(
|
||||
|
|
|
@ -71,9 +71,9 @@ const SecondaryCallInfo: React.FC<ISecondaryCallInfoProps> = ({ callRoom }) => {
|
|||
};
|
||||
|
||||
interface LegacyCallViewHeaderProps {
|
||||
pipMode: boolean;
|
||||
callRooms?: Room[];
|
||||
onPipMouseDown: (event: React.MouseEvent<Element, MouseEvent>) => void;
|
||||
pipMode?: boolean;
|
||||
callRooms: [Room, Room | null];
|
||||
onPipMouseDown?: (event: React.MouseEvent<Element, MouseEvent>) => void;
|
||||
onExpand?: () => void;
|
||||
onPin?: () => void;
|
||||
onMaximize?: () => void;
|
||||
|
@ -81,7 +81,7 @@ interface LegacyCallViewHeaderProps {
|
|||
|
||||
const LegacyCallViewHeader: React.FC<LegacyCallViewHeaderProps> = ({
|
||||
pipMode = false,
|
||||
callRooms = [],
|
||||
callRooms,
|
||||
onPipMouseDown,
|
||||
onExpand,
|
||||
onPin,
|
||||
|
|
|
@ -95,7 +95,7 @@ export default class VideoFeed extends React.PureComponent<IProps, IState> {
|
|||
element.addEventListener("resize", this.onResize);
|
||||
};
|
||||
|
||||
private updateFeed(oldFeed: CallFeed, newFeed: CallFeed): void {
|
||||
private updateFeed(oldFeed: CallFeed | null, newFeed: CallFeed | null): void {
|
||||
if (oldFeed === newFeed) return;
|
||||
|
||||
if (oldFeed) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue