Conform more code to strictNullChecks (#10374)

* Apply `strictNullChecks` to `src/components/views/room_settings/*`

* Restore tsconfig.json

* Conform more code to `strictNullChecks`

* Iterate

* Update matrix-widget-api

* Conform more code to `strictNullChecks`
This commit is contained in:
Michael Telatynski 2023-03-16 11:07:29 +00:00 committed by GitHub
parent 9c816bb720
commit 1c9ea423c9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
44 changed files with 223 additions and 179 deletions

View file

@ -252,10 +252,10 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
}
}
if (isEmpty) {
this.formatBarRef.current.hide();
this.formatBarRef.current?.hide();
}
this.setState({
autoComplete: this.props.model.autoComplete,
autoComplete: this.props.model.autoComplete ?? undefined,
// if a change is happening then clear the showVisualBell
showVisualBell: diff ? false : this.state.showVisualBell,
});
@ -266,12 +266,16 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
// If the user is entering a command, only consider them typing if it is one which sends a message into the room
if (isTyping && this.props.model.parts[0].type === "command") {
const { cmd } = parseCommandString(this.props.model.parts[0].text);
const command = CommandMap.get(cmd);
if (!command || !command.isEnabled() || command.category !== CommandCategories.messages) {
const command = CommandMap.get(cmd!);
if (!command?.isEnabled() || command.category !== CommandCategories.messages) {
isTyping = false;
}
}
SdkContextClass.instance.typingStore.setSelfTyping(this.props.room.roomId, this.props.threadId, isTyping);
SdkContextClass.instance.typingStore.setSelfTyping(
this.props.room.roomId,
this.props.threadId ?? null,
isTyping,
);
if (this.props.onChange) {
this.props.onChange();
@ -280,14 +284,14 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
private showPlaceholder(): void {
// escape single quotes
const placeholder = this.props.placeholder.replace(/'/g, "\\'");
this.editorRef.current.style.setProperty("--placeholder", `'${placeholder}'`);
this.editorRef.current.classList.add("mx_BasicMessageComposer_inputEmpty");
const placeholder = this.props.placeholder?.replace(/'/g, "\\'");
this.editorRef.current?.style.setProperty("--placeholder", `'${placeholder}'`);
this.editorRef.current?.classList.add("mx_BasicMessageComposer_inputEmpty");
}
private hidePlaceholder(): void {
this.editorRef.current.classList.remove("mx_BasicMessageComposer_inputEmpty");
this.editorRef.current.style.removeProperty("--placeholder");
this.editorRef.current?.classList.remove("mx_BasicMessageComposer_inputEmpty");
this.editorRef.current?.style.removeProperty("--placeholder");
}
private onCompositionStart = (): void => {
@ -327,9 +331,9 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
}
private onCutCopy = (event: ClipboardEvent, type: string): void => {
const selection = document.getSelection();
const selection = document.getSelection()!;
const text = selection.toString();
if (text) {
if (text && this.editorRef.current) {
const { model } = this.props;
const range = getRangeForSelection(this.editorRef.current, model, selection);
const selectedParts = range.parts.map((p) => p.serialize());
@ -412,7 +416,7 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
const { model } = this.props;
this._isCaretAtEnd = position.isAtEnd(model);
this.lastCaret = position.asOffset(model);
this.lastSelection = cloneSelection(document.getSelection());
this.lastSelection = cloneSelection(document.getSelection()!);
}
private refreshLastCaretIfNeeded(): DocumentOffset | undefined {
@ -422,7 +426,7 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
if (!this.editorRef.current) {
return;
}
const selection = document.getSelection();
const selection = document.getSelection()!;
if (!this.lastSelection || !selectionEquals(this.lastSelection, selection)) {
this.lastSelection = cloneSelection(selection);
const { caret, text } = getCaretOffsetAndText(this.editorRef.current, selection);
@ -467,7 +471,7 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
const { isEmpty } = this.props.model;
this.refreshLastCaretIfNeeded();
const selection = document.getSelection();
const selection = document.getSelection()!;
if (this.hasTextSelected && selection.isCollapsed) {
this.hasTextSelected = false;
this.formatBarRef.current?.hide();
@ -485,7 +489,7 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
const model = this.props.model;
let handled = false;
if (this.state.surroundWith && document.getSelection().type !== "Caret") {
if (this.state.surroundWith && document.getSelection()!.type !== "Caret") {
// This surrounds the selected text with a character. This is
// intentionally left out of the keybinding manager as the keybinds
// here shouldn't be changeable
@ -538,7 +542,7 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
this.tabCompleteName();
handled = true;
} else if ([KeyBindingAction.Delete, KeyBindingAction.Backspace].includes(accessibilityAction!)) {
this.formatBarRef.current.hide();
this.formatBarRef.current?.hide();
}
if (handled) {
@ -654,7 +658,7 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
private onAutoCompleteConfirm = (completion: ICompletion): void => {
this.modifiedFlag = true;
this.props.model.autoComplete.onComponentConfirm(completion);
this.props.model.autoComplete?.onComponentConfirm(completion);
};
private onAutoCompleteSelectionChange = (completionIndex: number): void => {
@ -691,9 +695,9 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
public componentWillUnmount(): void {
document.removeEventListener("selectionchange", this.onSelectionChange);
this.editorRef.current.removeEventListener("input", this.onInput, true);
this.editorRef.current.removeEventListener("compositionstart", this.onCompositionStart, true);
this.editorRef.current.removeEventListener("compositionend", this.onCompositionEnd, true);
this.editorRef.current?.removeEventListener("input", this.onInput, true);
this.editorRef.current?.removeEventListener("compositionstart", this.onCompositionStart, true);
this.editorRef.current?.removeEventListener("compositionend", this.onCompositionEnd, true);
SettingsStore.unwatchSetting(this.useMarkdownHandle);
SettingsStore.unwatchSetting(this.emoticonSettingHandle);
SettingsStore.unwatchSetting(this.shouldShowPillAvatarSettingHandle);
@ -716,10 +720,10 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
this.updateEditorState(this.getInitialCaretPosition());
// attach input listener by hand so React doesn't proxy the events,
// as the proxied event doesn't support inputType, which we need.
this.editorRef.current.addEventListener("input", this.onInput, true);
this.editorRef.current.addEventListener("compositionstart", this.onCompositionStart, true);
this.editorRef.current.addEventListener("compositionend", this.onCompositionEnd, true);
this.editorRef.current.focus();
this.editorRef.current?.addEventListener("input", this.onInput, true);
this.editorRef.current?.addEventListener("compositionstart", this.onCompositionStart, true);
this.editorRef.current?.addEventListener("compositionend", this.onCompositionEnd, true);
this.editorRef.current?.focus();
}
private getInitialCaretPosition(): DocumentPosition {
@ -826,7 +830,7 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
}
public focus(): void {
this.editorRef.current.focus();
this.editorRef.current?.focus();
}
public insertMention(userId: string): void {

View file

@ -149,7 +149,7 @@ class EditMessageComposer extends React.Component<IEditMessageComposerProps, ISt
this.dispatcherRef = dis.register(this.onAction);
}
private getRoom(): Room {
private getRoom(): Room | null {
return this.props.mxClient.getRoom(this.props.editState.getEvent().getRoomId());
}
@ -237,10 +237,10 @@ class EditMessageComposer extends React.Component<IEditMessageComposerProps, ISt
}
private get events(): MatrixEvent[] {
const liveTimelineEvents = this.context.liveTimeline.getEvents();
const pendingEvents = this.getRoom().getPendingEvents();
const liveTimelineEvents = this.context.liveTimeline?.getEvents();
const pendingEvents = this.getRoom()?.getPendingEvents();
const isInThread = Boolean(this.props.editState.getEvent().getThread());
return liveTimelineEvents.concat(isInThread ? [] : pendingEvents);
return liveTimelineEvents?.concat(isInThread ? [] : pendingEvents) ?? [];
}
private cancelEdit = (): void => {
@ -304,10 +304,10 @@ class EditMessageComposer extends React.Component<IEditMessageComposerProps, ISt
});
// Replace emoticon at the end of the message
if (SettingsStore.getValue("MessageComposerInput.autoReplaceEmoji")) {
const caret = this.editorRef.current?.getCaret();
if (SettingsStore.getValue("MessageComposerInput.autoReplaceEmoji") && this.editorRef.current) {
const caret = this.editorRef.current.getCaret();
const position = this.model.positionForOffset(caret.offset, caret.atNodeEnd);
this.editorRef.current?.replaceEmoticon(position, REGEX_EMOTICON);
this.editorRef.current.replaceEmoticon(position, REGEX_EMOTICON);
}
const editContent = createEditContent(this.model, editedEvent);
const newContent = editContent["m.new_content"];
@ -382,7 +382,7 @@ class EditMessageComposer extends React.Component<IEditMessageComposerProps, ISt
// store caret and serialized parts in the
// editorstate so it can be restored when the remote echo event tile gets rendered
// in case we're currently editing a pending event
const sel = document.getSelection();
const sel = document.getSelection()!;
let caret: DocumentOffset | undefined;
if (sel.focusNode) {
caret = getCaretOffsetAndText(this.editorRef.current?.editorRef.current, sel).caret;

View file

@ -238,7 +238,7 @@ interface IState {
isQuoteExpanded?: boolean;
thread: Thread;
thread: Thread | null;
threadNotification?: NotificationCountType;
}
@ -438,12 +438,12 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
if (thread.id === this.props.mxEvent.getId()) {
this.updateThread(thread);
const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId());
room.off(ThreadEvent.New, this.onNewThread);
room?.off(ThreadEvent.New, this.onNewThread);
}
};
private get thread(): Thread | null {
let thread = this.props.mxEvent.getThread();
let thread: Thread | undefined = this.props.mxEvent.getThread();
/**
* Accessing the threads value through the room due to a race condition
* that will be solved when there are proper backend support for threads
@ -452,7 +452,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
*/
if (!thread) {
const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId());
thread = room?.findThreadForEvent(this.props.mxEvent);
thread = room?.findThreadForEvent(this.props.mxEvent) ?? undefined;
}
return thread ?? null;
}
@ -471,7 +471,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
}
private renderThreadInfo(): React.ReactNode {
if (this.state.thread?.id === this.props.mxEvent.getId()) {
if (this.state.thread && this.state.thread.id === this.props.mxEvent.getId()) {
return (
<ThreadSummary mxEvent={this.props.mxEvent} thread={this.state.thread} data-testid="thread-summary" />
);
@ -506,7 +506,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
evt.preventDefault();
evt.stopPropagation();
const { permalinkCreator, mxEvent } = this.props;
const matrixToUrl = permalinkCreator.forEvent(mxEvent.getId());
const matrixToUrl = permalinkCreator.forEvent(mxEvent.getId()!);
await copyPlaintext(matrixToUrl);
};
@ -1104,7 +1104,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
const useIRCLayout = this.props.layout === Layout.IRC;
const groupTimestamp = !useIRCLayout ? linkedTimestamp : null;
const ircTimestamp = useIRCLayout ? linkedTimestamp : null;
const bubbleTimestamp = this.props.layout === Layout.Bubble ? messageTimestamp : null;
const bubbleTimestamp = this.props.layout === Layout.Bubble ? messageTimestamp : undefined;
const groupPadlock = !useIRCLayout && !isBubbleMessage && this.renderE2EPadlock();
const ircPadlock = useIRCLayout && !isBubbleMessage && this.renderE2EPadlock();
@ -1493,7 +1493,7 @@ class E2ePadlock extends React.Component<IE2ePadlockProps, IE2ePadlockState> {
}
interface ISentReceiptProps {
messageState: string; // TODO: Types for message sending state
messageState: EventStatus | null;
}
function SentReceipt({ messageState }: ISentReceiptProps): JSX.Element {

View file

@ -164,7 +164,7 @@ const NewRoomIntro: React.FC = () => {
}
const creator = room.currentState.getStateEvents(EventType.RoomCreate, "")?.getSender();
const creatorName = room?.getMember(creator)?.rawDisplayName || creator;
const creatorName = (creator && room?.getMember(creator)?.rawDisplayName) || creator;
let createdText: string;
if (creator === cli.getUserId()) {
@ -178,7 +178,7 @@ const NewRoomIntro: React.FC = () => {
let parentSpace: Room | undefined;
if (
SpaceStore.instance.activeSpaceRoom?.canInvite(cli.getSafeUserId()) &&
SpaceStore.instance.isRoomInSpace(SpaceStore.instance.activeSpace, room.roomId)
SpaceStore.instance.isRoomInSpace(SpaceStore.instance.activeSpace!, room.roomId)
) {
parentSpace = SpaceStore.instance.activeSpaceRoom;
}

View file

@ -231,7 +231,7 @@ const RoomListHeader: React.FC<IProps> = ({ onVisibilityChange }) => {
contextMenu = (
<IconizedContextMenu
{...contextMenuBelow(plusMenuHandle.current.getBoundingClientRect())}
{...contextMenuBelow(plusMenuHandle.current!.getBoundingClientRect())}
onFinished={closePlusMenu}
compact
>
@ -357,7 +357,7 @@ const RoomListHeader: React.FC<IProps> = ({ onVisibilityChange }) => {
contextMenu = (
<IconizedContextMenu
{...contextMenuBelow(plusMenuHandle.current.getBoundingClientRect())}
{...contextMenuBelow(plusMenuHandle.current!.getBoundingClientRect())}
onFinished={closePlusMenu}
compact
>

View file

@ -227,12 +227,14 @@ export class SendMessageComposer extends React.Component<ISendMessageComposerPro
// selection must be collapsed and caret at start
if (this.editorRef.current?.isSelectionCollapsed() && this.editorRef.current?.isCaretAtStart()) {
const events = this.context.liveTimeline
.getEvents()
?.getEvents()
.concat(replyingToThread ? [] : this.props.room.getPendingEvents());
const editEvent = findEditableEvent({
events,
isForward: false,
});
const editEvent = events
? findEditableEvent({
events,
isForward: false,
})
: undefined;
if (editEvent) {
// We're selecting history, so prevent the key event from doing anything else
event.preventDefault();
@ -297,7 +299,7 @@ export class SendMessageComposer extends React.Component<ISendMessageComposerPro
if (events[i].getType() === EventType.RoomMessage) {
let shouldReact = true;
const lastMessage = events[i];
const userId = MatrixClientPeg.get().getUserId();
const userId = MatrixClientPeg.get().getSafeUserId();
const messageReactions = this.props.room.relations.getChildEventsForEvent(
lastMessage.getId()!,
RelationType.Annotation,
@ -307,10 +309,10 @@ export class SendMessageComposer extends React.Component<ISendMessageComposerPro
// if we have already sent this reaction, don't redact but don't re-send
if (messageReactions) {
const myReactionEvents =
messageReactions.getAnnotationsBySender()[userId] || new Set<MatrixEvent>();
messageReactions.getAnnotationsBySender()?.[userId] || new Set<MatrixEvent>();
const myReactionKeys = [...myReactionEvents]
.filter((event) => !event.isRedacted())
.map((event) => event.getRelation().key);
.map((event) => event.getRelation()?.key);
shouldReact = !myReactionKeys.includes(reaction);
}
if (shouldReact) {