Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Improve strictNullChecks support in right_panel #10415

Merged
merged 3 commits into from
Mar 22, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/components/structures/TimelinePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ interface IProps {
// representing. This may or may not have a room, depending on what it's
// a timeline representing. If it has a room, we maintain RRs etc for
// that room.
timelineSet: EventTimelineSet;
timelineSet?: EventTimelineSet;
// overlay events from a second timelineset on the main timeline
// added to support virtual rooms
// events from the overlay timeline set will be added by localTimestamp
Expand Down
11 changes: 7 additions & 4 deletions src/components/views/right_panel/EncryptionPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ const EncryptionPanel: React.FC<IProps> = (props: IProps) => {
const requestFromPromise = await verificationRequestPromise;
setRequesting(false);
setRequest(requestFromPromise);
setPhase(requestFromPromise.phase);
setPhase(requestFromPromise?.phase);
}
if (verificationRequestPromise) {
awaitPromise();
Expand Down Expand Up @@ -109,6 +109,9 @@ const EncryptionPanel: React.FC<IProps> = (props: IProps) => {
let verificationRequest_: VerificationRequest;
try {
const roomId = await ensureDMExists(cli, member.userId);
if (!roomId) {
throw new Error("Unable to create Room for verification");
}
verificationRequest_ = await cli.requestVerificationDM(member.userId, roomId);
} catch (e) {
console.error("Error starting verification", e);
Expand All @@ -133,15 +136,15 @@ const EncryptionPanel: React.FC<IProps> = (props: IProps) => {
if (!RightPanelStore.instance.isOpen) RightPanelStore.instance.togglePanel(null);
}, [member]);

const requested =
const requested: boolean =
(!request && isRequesting) ||
(request && (phase === PHASE_REQUESTED || phase === PHASE_UNSENT || phase === undefined));
(!!request && (phase === PHASE_REQUESTED || phase === PHASE_UNSENT || phase === undefined));
const isSelfVerification = request
? request.isSelfVerification
: member.userId === MatrixClientPeg.get().getUserId();

if (!request || requested) {
const initiatedByMe = (!request && isRequesting) || (request && request.initiatedByMe);
const initiatedByMe = (!request && isRequesting) || (!!request && request.initiatedByMe);
return (
<EncryptionInfo
isRoomEncrypted={isRoomEncrypted}
Expand Down
6 changes: 3 additions & 3 deletions src/components/views/right_panel/HeaderButtons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export enum HeaderKind {

interface IState {
headerKind: HeaderKind;
phase: RightPanelPhases;
phase: RightPanelPhases | null;
threadNotificationColor: NotificationColor;
globalNotificationColor: NotificationColor;
}
Expand All @@ -43,7 +43,7 @@ interface IProps {}

export default abstract class HeaderButtons<P = {}> extends React.Component<IProps & P, IState> {
private unmounted = false;
private dispatcherRef: string;
private dispatcherRef?: string = undefined;

public constructor(props: IProps & P, kind: HeaderKind) {
super(props);
Expand Down Expand Up @@ -83,7 +83,7 @@ export default abstract class HeaderButtons<P = {}> extends React.Component<IPro
public isPhase(phases: string | string[]): boolean {
if (!RightPanelStore.instance.isOpen) return false;
if (Array.isArray(phases)) {
return phases.includes(this.state.phase);
return !!this.state.phase && phases.includes(this.state.phase);
} else {
return phases === this.state.phase;
}
Expand Down
5 changes: 3 additions & 2 deletions src/components/views/right_panel/PinnedMessagesCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,10 @@ const PinnedMessagesCard: React.FC<IProps> = ({ room, onClose, permalinkCreator
}
await room.processPollEvents([event]);

if (event && PinningUtils.isPinnable(event)) {
const senderUserId = event.getSender();
if (senderUserId && PinningUtils.isPinnable(event)) {
// Inject sender information
event.sender = room.getMember(event.getSender());
event.sender = room.getMember(senderUserId);
// Also inject any edits we've found
if (edit) event.makeReplaced(edit);

Expand Down
4 changes: 2 additions & 2 deletions src/components/views/right_panel/RoomHeaderButtons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ export default class RoomHeaderButtons extends HeaderButtons<IProps> {
private onRoomSummaryClicked = (): void => {
// use roomPanelPhase rather than this.state.phase as it remembers the latest one if we close
const currentPhase = RightPanelStore.instance.currentCard.phase;
if (ROOM_INFO_PHASES.includes(currentPhase)) {
if (currentPhase && ROOM_INFO_PHASES.includes(currentPhase)) {
if (this.state.phase === currentPhase) {
this.setPhase(currentPhase);
} else {
Expand All @@ -257,7 +257,7 @@ export default class RoomHeaderButtons extends HeaderButtons<IProps> {
};

private onThreadsPanelClicked = (ev: ButtonEvent): void => {
if (RoomHeaderButtons.THREAD_PHASES.includes(this.state.phase)) {
if (this.state.phase && RoomHeaderButtons.THREAD_PHASES.includes(this.state.phase)) {
RightPanelStore.instance.togglePanel(this.props.room?.roomId ?? null);
} else {
showThreadPanel();
Expand Down
10 changes: 6 additions & 4 deletions src/components/views/right_panel/RoomSummaryCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -130,12 +130,14 @@ const AppRow: React.FC<IAppRowProps> = ({ app, room }) => {
const [menuDisplayed, handle, openMenu, closeMenu] = useContextMenu<HTMLDivElement>();
let contextMenu;
if (menuDisplayed) {
const rect = handle.current.getBoundingClientRect();
const rect = handle.current?.getBoundingClientRect();
const rightMargin = rect?.right ?? 0;
const topMargin = rect?.top ?? 0;
contextMenu = (
<WidgetContextMenu
chevronFace={ChevronFace.None}
right={UIStore.instance.windowWidth - rect.right}
bottom={UIStore.instance.windowHeight - rect.top}
right={UIStore.instance.windowWidth - rightMargin}
bottom={UIStore.instance.windowHeight - topMargin}
onFinished={closeMenu}
app={app}
/>
Expand Down Expand Up @@ -226,7 +228,7 @@ const AppsSection: React.FC<IAppsSectionProps> = ({ room }) => {
managers.openNoManagerDialog();
} else {
// noinspection JSIgnoredPromiseFromCall
managers.getPrimaryManager().open(room);
managers.getPrimaryManager()?.open(room);
}
};

Expand Down
10 changes: 5 additions & 5 deletions src/components/views/right_panel/TimelineCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ interface IState {
export default class TimelineCard extends React.Component<IProps, IState> {
public static contextType = RoomContext;

private dispatcherRef: string;
private layoutWatcherRef: string;
private dispatcherRef?: string;
private layoutWatcherRef?: string;
private timelinePanel = React.createRef<TimelinePanel>();
private card = React.createRef<HTMLDivElement>();
private readReceiptsSettingWatcher: string | undefined;
Expand Down Expand Up @@ -110,10 +110,10 @@ export default class TimelineCard extends React.Component<IProps, IState> {
SettingsStore.unwatchSetting(this.layoutWatcherRef);
}

dis.unregister(this.dispatcherRef);
if (this.dispatcherRef) dis.unregister(this.dispatcherRef);
}

private onRoomViewStoreUpdate = async (initial?: boolean): Promise<void> => {
private onRoomViewStoreUpdate = async (_initial?: boolean): Promise<void> => {
const newState: Pick<IState, any> = {
initialEventId: SdkContextClass.instance.roomViewStore.getInitialEventId(),
isInitialEventHighlighted: SdkContextClass.instance.roomViewStore.isInitialEventHighlighted(),
Expand Down Expand Up @@ -220,7 +220,7 @@ export default class TimelineCard extends React.Component<IProps, IState> {
value={{
...this.context,
timelineRenderingType: this.props.timelineRenderingType ?? this.context.timelineRenderingType,
liveTimeline: this.props.timelineSet.getLiveTimeline(),
liveTimeline: this.props.timelineSet?.getLiveTimeline(),
narrow: this.state.narrow,
}}
>
Expand Down
6 changes: 3 additions & 3 deletions src/components/views/right_panel/UserInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export interface IDevice extends DeviceInfo {
export const disambiguateDevices = (devices: IDevice[]): void => {
const names = Object.create(null);
for (let i = 0; i < devices.length; i++) {
const name = devices[i].getDisplayName();
const name = devices[i].getDisplayName() ?? "";
const indexList = names[name] || [];
indexList.push(i);
names[name] = indexList;
Expand Down Expand Up @@ -595,7 +595,7 @@ export const RoomKickButton = ({
member,
startUpdating,
stopUpdating,
}: Omit<IBaseRoomProps, "powerLevels">): JSX.Element => {
}: Omit<IBaseRoomProps, "powerLevels">): JSX.Element | null => {
const cli = useContext(MatrixClientContext);

// check if user can be kicked/disinvited
Expand Down Expand Up @@ -1611,7 +1611,7 @@ const UserInfo: React.FC<IProps> = ({ user, room, onClose, phase = RightPanelPha
const member = useMemo(() => (room ? room.getMember(user.userId) || user : user), [room, user]);

const isRoomEncrypted = useIsEncrypted(cli, room);
const devices = useDevices(user.userId);
const devices = useDevices(user.userId) ?? [];

let e2eStatus: E2EStatus | undefined;
if (isRoomEncrypted && devices) {
Expand Down
37 changes: 20 additions & 17 deletions src/components/views/right_panel/VerificationPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ interface IProps {
layout: string;
request: VerificationRequest;
member: RoomMember | User;
phase: Phase;
phase?: Phase;
onClose: () => void;
isRoomEncrypted: boolean;
inDialog: boolean;
Expand Down Expand Up @@ -69,9 +69,8 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
const showQR: boolean = request.otherPartySupportsMethod(SCAN_QR_CODE_METHOD);
const brand = SdkConfig.get().brand;

let noCommonMethodError: JSX.Element | undefined;
if (!showSAS && !showQR) {
noCommonMethodError = (
const noCommonMethodError: JSX.Element | null =
!showSAS && !showQR ? (
<p>
{_t(
"The device you are trying to verify doesn't support scanning a " +
Expand All @@ -80,14 +79,13 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
{ brand },
)}
</p>
);
}
) : null;

if (this.props.layout === "dialog") {
// HACK: This is a terrible idea.
let qrBlockDialog: JSX.Element | undefined;
let sasBlockDialog: JSX.Element | undefined;
if (showQR) {
if (showQR && request.qrCodeData) {
qrBlockDialog = (
<div className="mx_VerificationPanel_QRPhase_startOption">
<p>{_t("Scan this unique code")}</p>
Expand Down Expand Up @@ -135,7 +133,7 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
}

let qrBlock: JSX.Element | undefined;
if (showQR) {
if (showQR && request.qrCodeData) {
qrBlock = (
<div className="mx_UserInfo_container">
<h3>{_t("Verify by scanning")}</h3>
Expand Down Expand Up @@ -193,18 +191,23 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
private onReciprocateYesClick = (): void => {
if (!this.state.reciprocateQREvent) return;
this.setState({ reciprocateButtonClicked: true });
this.state.reciprocateQREvent.confirm();
this.state.reciprocateQREvent?.confirm();
};

private onReciprocateNoClick = (): void => {
if (!this.state.reciprocateQREvent) return;
this.setState({ reciprocateButtonClicked: true });
this.state.reciprocateQREvent.cancel();
this.state.reciprocateQREvent?.cancel();
};

private getDevice(): DeviceInfo | null {
const cli = MatrixClientPeg.get();
return cli.getStoredDevice(cli.getSafeUserId(), this.props.request?.channel.deviceId);
const deviceId = this.props.request && this.props.request.channel.deviceId;
const userId = MatrixClientPeg.get().getUserId();
if (deviceId && userId) {
return MatrixClientPeg.get().getStoredDevice(userId, deviceId);
} else {
return null;
}
}

private renderQRReciprocatePhase(): JSX.Element {
Expand Down Expand Up @@ -398,8 +401,8 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
const { request } = this.props;
const sasEvent = (request.verifier as SAS).sasEvent;
const reciprocateQREvent = (request.verifier as ReciprocateQRCode).reciprocateQREvent;
request.verifier.off(SasEvent.ShowSas, this.updateVerifierState);
request.verifier.off(QrCodeEvent.ShowReciprocateQr, this.updateVerifierState);
request.verifier?.off(SasEvent.ShowSas, this.updateVerifierState);
request.verifier?.off(QrCodeEvent.ShowReciprocateQr, this.updateVerifierState);
this.setState({ sasEvent, reciprocateQREvent });
};

Expand All @@ -408,12 +411,12 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
const hadVerifier = this.hasVerifier;
this.hasVerifier = !!request.verifier;
if (!hadVerifier && this.hasVerifier) {
request.verifier.on(SasEvent.ShowSas, this.updateVerifierState);
request.verifier.on(QrCodeEvent.ShowReciprocateQr, this.updateVerifierState);
request.verifier?.on(SasEvent.ShowSas, this.updateVerifierState);
request.verifier?.on(QrCodeEvent.ShowReciprocateQr, this.updateVerifierState);
try {
// on the requester side, this is also awaited in startSAS,
// but that's ok as verify should return the same promise.
await request.verifier.verify();
await request.verifier?.verify();
} catch (err) {
logger.error("error verify", err);
}
Expand Down
8 changes: 5 additions & 3 deletions src/components/views/right_panel/WidgetCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,14 @@ const WidgetCard: React.FC<IProps> = ({ room, widgetId, onClose }) => {

let contextMenu: JSX.Element | undefined;
if (menuDisplayed) {
const rect = handle.current!.getBoundingClientRect();
const rect = handle.current?.getBoundingClientRect();
const rightMargin = rect ? rect.right : 0;
const bottomMargin = rect ? rect.bottom : 0;
contextMenu = (
<WidgetContextMenu
chevronFace={ChevronFace.None}
right={UIStore.instance.windowWidth - rect.right - 12}
top={rect.bottom + 12}
right={UIStore.instance.windowWidth - rightMargin - 12}
top={bottomMargin + 12}
onFinished={closeMenu}
app={app}
/>
Expand Down
2 changes: 1 addition & 1 deletion src/components/views/rooms/MessageComposer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ function SendButton(props: ISendButtonProps): JSX.Element {
interface IProps extends MatrixClientProps {
room: Room;
resizeNotifier: ResizeNotifier;
permalinkCreator: RoomPermalinkCreator;
permalinkCreator?: RoomPermalinkCreator;
replyToEvent?: MatrixEvent;
relation?: IEventRelation;
e2eStatus?: E2EStatus;
Expand Down
4 changes: 2 additions & 2 deletions src/components/views/rooms/SendMessageComposer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export function createMessageContent(
model: EditorModel,
replyToEvent: MatrixEvent | undefined,
relation: IEventRelation | undefined,
permalinkCreator: RoomPermalinkCreator,
permalinkCreator?: RoomPermalinkCreator,
includeReplyLegacyFallback = true,
): IContent {
const isEmote = containsEmote(model);
Expand Down Expand Up @@ -133,7 +133,7 @@ export function isQuickReaction(model: EditorModel): boolean {
interface ISendMessageComposerProps extends MatrixClientProps {
room: Room;
placeholder?: string;
permalinkCreator: RoomPermalinkCreator;
permalinkCreator?: RoomPermalinkCreator;
relation?: IEventRelation;
replyToEvent?: MatrixEvent;
disabled?: boolean;
Expand Down
2 changes: 1 addition & 1 deletion src/components/views/rooms/VoiceRecordComposerTile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ import { createVoiceMessageContent } from "../../../utils/createVoiceMessageCont

interface IProps {
room: Room;
permalinkCreator: RoomPermalinkCreator;
permalinkCreator?: RoomPermalinkCreator;
relation?: IEventRelation;
replyToEvent?: MatrixEvent;
}
Expand Down
2 changes: 1 addition & 1 deletion src/stores/right-panel/RightPanelStorePhases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export enum RightPanelPhases {
ThreadPanel = "ThreadPanel",
}

export function backLabelForPhase(phase: RightPanelPhases): string | null {
export function backLabelForPhase(phase: RightPanelPhases | null): string | null {
switch (phase) {
case RightPanelPhases.ThreadPanel:
return _t("Threads");
Expand Down
6 changes: 3 additions & 3 deletions src/utils/Reply.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export function stripHTMLReply(html: string): string {
// Part of Replies fallback support
export function getNestedReplyText(
ev: MatrixEvent,
permalinkCreator: RoomPermalinkCreator,
permalinkCreator?: RoomPermalinkCreator,
): { body: string; html: string } | null {
if (!ev) return null;

Expand Down Expand Up @@ -99,7 +99,7 @@ export function getNestedReplyText(

// dev note: do not rely on `body` being safe for HTML usage below.

const evLink = permalinkCreator.forEvent(ev.getId()!);
const evLink = permalinkCreator?.forEvent(ev.getId()!);
const userLink = makeUserPermalink(ev.getSender()!);
const mxid = ev.getSender();

Expand Down Expand Up @@ -236,7 +236,7 @@ interface AddReplyOpts {
}

interface IncludeLegacyFeedbackOpts {
permalinkCreator: RoomPermalinkCreator;
permalinkCreator?: RoomPermalinkCreator;
includeLegacyFallback: true;
}

Expand Down