mirror of
https://github.com/element-hq/element-web
synced 2024-11-27 03:36:07 +03:00
Improve strictNullChecks support in right_panel (#10415)
This commit is contained in:
parent
853b3f822d
commit
ba36d2cc01
15 changed files with 63 additions and 52 deletions
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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}
|
||||
|
|
|
@ -34,7 +34,7 @@ export enum HeaderKind {
|
|||
|
||||
interface IState {
|
||||
headerKind: HeaderKind;
|
||||
phase: RightPanelPhases;
|
||||
phase: RightPanelPhases | null;
|
||||
threadNotificationColor: NotificationColor;
|
||||
globalNotificationColor: NotificationColor;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
@ -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();
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
|
@ -226,7 +228,7 @@ const AppsSection: React.FC<IAppsSectionProps> = ({ room }) => {
|
|||
managers.openNoManagerDialog();
|
||||
} else {
|
||||
// noinspection JSIgnoredPromiseFromCall
|
||||
managers.getPrimaryManager().open(room);
|
||||
managers.getPrimaryManager()?.open(room);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
@ -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(),
|
||||
|
@ -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,
|
||||
}}
|
||||
>
|
||||
|
|
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
|
|
@ -41,7 +41,7 @@ interface IProps {
|
|||
layout: string;
|
||||
request: VerificationRequest;
|
||||
member: RoomMember | User;
|
||||
phase: Phase;
|
||||
phase?: Phase;
|
||||
onClose: () => void;
|
||||
isRoomEncrypted: boolean;
|
||||
inDialog: boolean;
|
||||
|
@ -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 " +
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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 {
|
||||
|
@ -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 });
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
@ -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;
|
||||
|
|
|
@ -48,7 +48,7 @@ import { createVoiceMessageContent } from "../../../utils/createVoiceMessageCont
|
|||
|
||||
interface IProps {
|
||||
room: Room;
|
||||
permalinkCreator: RoomPermalinkCreator;
|
||||
permalinkCreator?: RoomPermalinkCreator;
|
||||
relation?: IEventRelation;
|
||||
replyToEvent?: MatrixEvent;
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
@ -236,7 +236,7 @@ interface AddReplyOpts {
|
|||
}
|
||||
|
||||
interface IncludeLegacyFeedbackOpts {
|
||||
permalinkCreator: RoomPermalinkCreator;
|
||||
permalinkCreator?: RoomPermalinkCreator;
|
||||
includeLegacyFallback: true;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue