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:
parent
9c816bb720
commit
1c9ea423c9
44 changed files with 223 additions and 179 deletions
|
@ -25,7 +25,7 @@ import { IS_MAC } from "./Keyboard";
|
|||
* The combo is evaluated strictly, i.e. the KeyboardEvent must match exactly what is specified in the KeyCombo.
|
||||
*/
|
||||
export type KeyCombo = {
|
||||
key?: string;
|
||||
key: string;
|
||||
|
||||
/** On PC: ctrl is pressed; on Mac: meta is pressed */
|
||||
ctrlOrCmdKey?: boolean;
|
||||
|
|
|
@ -306,7 +306,7 @@ class MatrixClientPegClass implements IMatrixClientPeg {
|
|||
homeserverUrl: this.matrixClient.baseUrl,
|
||||
identityServerUrl: this.matrixClient.idBaseUrl,
|
||||
userId: this.matrixClient.credentials.userId,
|
||||
deviceId: this.matrixClient.getDeviceId(),
|
||||
deviceId: this.matrixClient.getDeviceId() ?? undefined,
|
||||
accessToken: this.matrixClient.getAccessToken(),
|
||||
guest: this.matrixClient.isGuest(),
|
||||
};
|
||||
|
|
|
@ -576,7 +576,7 @@ async function combinedPagination(searchResult: ISeshatSearchResults): Promise<I
|
|||
const newSlice = result.results.slice(Math.max(result.results.length - newResultCount, 0));
|
||||
restoreEncryptionInfo(newSlice);
|
||||
|
||||
searchResult.pendingRequest = null;
|
||||
searchResult.pendingRequest = undefined;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -41,7 +41,8 @@ interface IProps {
|
|||
defaultName?: string;
|
||||
parentSpace?: Room;
|
||||
defaultEncrypted?: boolean;
|
||||
onFinished(proceed?: boolean, opts?: IOpts): void;
|
||||
onFinished(proceed?: false): void;
|
||||
onFinished(proceed: true, opts: IOpts): void;
|
||||
}
|
||||
|
||||
interface IState {
|
||||
|
|
|
@ -46,7 +46,8 @@ interface IProps {
|
|||
widgetDefinition: IModalWidgetOpenRequestData;
|
||||
widgetRoomId?: string;
|
||||
sourceWidgetId: string;
|
||||
onFinished(success?: boolean, data?: IModalWidgetReturnData): void;
|
||||
onFinished(success: true, data: IModalWidgetReturnData): void;
|
||||
onFinished(success?: false, data?: void): void;
|
||||
}
|
||||
|
||||
interface IState {
|
||||
|
|
|
@ -133,13 +133,13 @@ export default class ImageView extends React.Component<IProps, IState> {
|
|||
// We want to recalculate zoom whenever the window's size changes
|
||||
window.addEventListener("resize", this.recalculateZoom);
|
||||
// After the image loads for the first time we want to calculate the zoom
|
||||
this.image.current.addEventListener("load", this.imageLoaded);
|
||||
this.image.current?.addEventListener("load", this.imageLoaded);
|
||||
}
|
||||
|
||||
public componentWillUnmount(): void {
|
||||
this.focusLock.current.removeEventListener("wheel", this.onWheel);
|
||||
window.removeEventListener("resize", this.recalculateZoom);
|
||||
this.image.current.removeEventListener("load", this.imageLoaded);
|
||||
this.image.current?.removeEventListener("load", this.imageLoaded);
|
||||
}
|
||||
|
||||
private imageLoaded = (): void => {
|
||||
|
@ -171,6 +171,7 @@ export default class ImageView extends React.Component<IProps, IState> {
|
|||
private setZoomAndRotation = (inputRotation?: number): void => {
|
||||
const image = this.image.current;
|
||||
const imageWrapper = this.imageWrapper.current;
|
||||
if (!image || !imageWrapper) return;
|
||||
|
||||
const rotation = inputRotation ?? this.state.rotation;
|
||||
|
||||
|
@ -236,8 +237,8 @@ export default class ImageView extends React.Component<IProps, IState> {
|
|||
// Zoom relative to the given point on the image.
|
||||
// First we need to figure out the offset of the anchor point
|
||||
// relative to the center of the image, accounting for rotation.
|
||||
let offsetX;
|
||||
let offsetY;
|
||||
let offsetX: number | undefined;
|
||||
let offsetY: number | undefined;
|
||||
// The modulo operator can return negative values for some
|
||||
// rotations, so we have to do some extra work to normalize it
|
||||
switch (((this.state.rotation % 360) + 360) % 360) {
|
||||
|
@ -310,7 +311,7 @@ export default class ImageView extends React.Component<IProps, IState> {
|
|||
private onDownloadClick = (): void => {
|
||||
const a = document.createElement("a");
|
||||
a.href = this.props.src;
|
||||
a.download = this.props.name;
|
||||
if (this.props.name) a.download = this.props.name;
|
||||
a.target = "_blank";
|
||||
a.rel = "noreferrer noopener";
|
||||
a.click();
|
||||
|
@ -334,9 +335,9 @@ export default class ImageView extends React.Component<IProps, IState> {
|
|||
ev.preventDefault();
|
||||
dis.dispatch<ViewRoomPayload>({
|
||||
action: Action.ViewRoom,
|
||||
event_id: this.props.mxEvent.getId(),
|
||||
event_id: this.props.mxEvent?.getId(),
|
||||
highlighted: true,
|
||||
room_id: this.props.mxEvent.getRoomId(),
|
||||
room_id: this.props.mxEvent?.getRoomId(),
|
||||
metricsTrigger: undefined, // room doesn't change
|
||||
});
|
||||
this.props.onFinished();
|
||||
|
@ -440,11 +441,11 @@ export default class ImageView extends React.Component<IProps, IState> {
|
|||
|
||||
let info: JSX.Element | undefined;
|
||||
if (showEventMeta) {
|
||||
const mxEvent = this.props.mxEvent;
|
||||
const mxEvent = this.props.mxEvent!;
|
||||
const showTwelveHour = SettingsStore.getValue("showTwelveHourTimestamps");
|
||||
let permalink = "#";
|
||||
if (this.props.permalinkCreator) {
|
||||
permalink = this.props.permalinkCreator.forEvent(this.props.mxEvent.getId());
|
||||
permalink = this.props.permalinkCreator.forEvent(mxEvent.getId());
|
||||
}
|
||||
|
||||
const senderName = mxEvent.sender?.name ?? mxEvent.getSender();
|
||||
|
@ -453,7 +454,7 @@ export default class ImageView extends React.Component<IProps, IState> {
|
|||
<a
|
||||
href={permalink}
|
||||
onClick={this.onPermalinkClicked}
|
||||
aria-label={formatFullDate(new Date(this.props.mxEvent.getTs()), showTwelveHour, false)}
|
||||
aria-label={formatFullDate(new Date(mxEvent.getTs()), showTwelveHour, false)}
|
||||
>
|
||||
<MessageTimestamp
|
||||
showFullDate={true}
|
||||
|
|
|
@ -66,17 +66,17 @@ interface IProps<T> {
|
|||
// should typically be less than `overflowItems` unless applying
|
||||
// margins in the parent component when using multiple LazyRenderList in one viewport.
|
||||
// use 0 to only rerender when items will come into view.
|
||||
overflowMargin?: number;
|
||||
overflowMargin: number;
|
||||
// the amount of items to add at the top and bottom to render,
|
||||
// so not every scroll of causes a rerender.
|
||||
overflowItems?: number;
|
||||
overflowItems: number;
|
||||
|
||||
element?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
interface IState {
|
||||
renderRange: ItemRange | null;
|
||||
renderRange: ItemRange;
|
||||
}
|
||||
|
||||
export default class LazyRenderList<T = any> extends React.Component<IProps<T>, IState> {
|
||||
|
@ -88,9 +88,7 @@ export default class LazyRenderList<T = any> extends React.Component<IProps<T>,
|
|||
public constructor(props: IProps<T>) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
renderRange: null,
|
||||
};
|
||||
this.state = LazyRenderList.getDerivedStateFromProps(props, {} as IState) as IState;
|
||||
}
|
||||
|
||||
public static getDerivedStateFromProps(props: IProps<unknown>, state: IState): Partial<IState> | null {
|
||||
|
|
|
@ -35,7 +35,7 @@ const ALGORITHM = "m.megolm.v1.aes-sha2";
|
|||
|
||||
const EncryptionEvent = forwardRef<HTMLDivElement, IProps>(({ mxEvent, timestamp }, ref) => {
|
||||
const cli = useContext(MatrixClientContext);
|
||||
const roomId = mxEvent.getRoomId();
|
||||
const roomId = mxEvent.getRoomId()!;
|
||||
const isRoomEncrypted = MatrixClientPeg.get().isRoomEncrypted(roomId);
|
||||
|
||||
const prevContent = mxEvent.getPrevContent() as IRoomEncryption;
|
||||
|
@ -51,13 +51,13 @@ const EncryptionEvent = forwardRef<HTMLDivElement, IProps>(({ mxEvent, timestamp
|
|||
if (prevContent.algorithm === ALGORITHM) {
|
||||
subtitle = _t("Some encryption parameters have been changed.");
|
||||
} else if (dmPartner) {
|
||||
const displayName = room.getMember(dmPartner)?.rawDisplayName || dmPartner;
|
||||
const displayName = room?.getMember(dmPartner)?.rawDisplayName || dmPartner;
|
||||
subtitle = _t(
|
||||
"Messages here are end-to-end encrypted. " +
|
||||
"Verify %(displayName)s in their profile - tap on their avatar.",
|
||||
{ displayName },
|
||||
);
|
||||
} else if (isLocalRoom(room)) {
|
||||
} else if (room && isLocalRoom(room)) {
|
||||
subtitle = _t("Messages in this chat will be end-to-end encrypted.");
|
||||
} else {
|
||||
subtitle = _t(
|
||||
|
|
|
@ -183,7 +183,7 @@ const MBeaconBody: React.FC<IBodyProps> = React.forwardRef(({ mxEvent, getRelati
|
|||
<SmartMarker
|
||||
map={map}
|
||||
id={`${mapId}-marker`}
|
||||
geoUri={latestLocationState.uri}
|
||||
geoUri={latestLocationState?.uri}
|
||||
roomMember={markerRoomMember ?? undefined}
|
||||
useMemberColor
|
||||
/>
|
||||
|
|
|
@ -47,10 +47,10 @@ interface IProps {
|
|||
room: Room;
|
||||
onClose: () => void;
|
||||
resizeNotifier: ResizeNotifier;
|
||||
permalinkCreator?: RoomPermalinkCreator;
|
||||
permalinkCreator: RoomPermalinkCreator;
|
||||
e2eStatus?: E2EStatus;
|
||||
classNames?: string;
|
||||
timelineSet?: EventTimelineSet;
|
||||
timelineSet: EventTimelineSet;
|
||||
timelineRenderingType?: TimelineRenderingType;
|
||||
showComposer?: boolean;
|
||||
composerRelation?: IEventRelation;
|
||||
|
@ -77,7 +77,7 @@ export default class TimelineCard extends React.Component<IProps, IState> {
|
|||
private layoutWatcherRef: string;
|
||||
private timelinePanel = React.createRef<TimelinePanel>();
|
||||
private card = React.createRef<HTMLDivElement>();
|
||||
private readReceiptsSettingWatcher: string;
|
||||
private readReceiptsSettingWatcher: string | undefined;
|
||||
|
||||
public constructor(props: IProps) {
|
||||
super(props);
|
||||
|
@ -87,7 +87,6 @@ export default class TimelineCard extends React.Component<IProps, IState> {
|
|||
atEndOfLiveTimeline: true,
|
||||
narrow: false,
|
||||
};
|
||||
this.readReceiptsSettingWatcher = null;
|
||||
}
|
||||
|
||||
public componentDidMount(): void {
|
||||
|
@ -129,7 +128,7 @@ export default class TimelineCard extends React.Component<IProps, IState> {
|
|||
case Action.EditEvent:
|
||||
this.setState(
|
||||
{
|
||||
editState: payload.event ? new EditorStateTransfer(payload.event) : null,
|
||||
editState: payload.event ? new EditorStateTransfer(payload.event) : undefined,
|
||||
},
|
||||
() => {
|
||||
if (payload.event) {
|
||||
|
@ -199,7 +198,7 @@ export default class TimelineCard extends React.Component<IProps, IState> {
|
|||
};
|
||||
|
||||
public render(): React.ReactNode {
|
||||
const highlightedEventId = this.state.isInitialEventHighlighted ? this.state.initialEventId : null;
|
||||
const highlightedEventId = this.state.isInitialEventHighlighted ? this.state.initialEventId : undefined;
|
||||
|
||||
let jumpToBottom;
|
||||
if (!this.state.atEndOfLiveTimeline) {
|
||||
|
@ -232,7 +231,7 @@ export default class TimelineCard extends React.Component<IProps, IState> {
|
|||
header={this.renderTimelineCardHeader()}
|
||||
ref={this.card}
|
||||
>
|
||||
<Measured sensor={this.card.current} onMeasurement={this.onMeasurement} />
|
||||
{this.card.current && <Measured sensor={this.card.current} onMeasurement={this.onMeasurement} />}
|
||||
<div className="mx_TimelineCard_timeline">
|
||||
{jumpToBottom}
|
||||
<TimelinePanel
|
||||
|
|
|
@ -69,8 +69,9 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
|
|||
const showQR: boolean = request.otherPartySupportsMethod(SCAN_QR_CODE_METHOD);
|
||||
const brand = SdkConfig.get().brand;
|
||||
|
||||
const noCommonMethodError: JSX.Element =
|
||||
!showSAS && !showQR ? (
|
||||
let noCommonMethodError: JSX.Element | undefined;
|
||||
if (!showSAS && !showQR) {
|
||||
noCommonMethodError = (
|
||||
<p>
|
||||
{_t(
|
||||
"The device you are trying to verify doesn't support scanning a " +
|
||||
|
@ -79,12 +80,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;
|
||||
let sasBlockDialog: JSX.Element;
|
||||
let qrBlockDialog: JSX.Element | undefined;
|
||||
let sasBlockDialog: JSX.Element | undefined;
|
||||
if (showQR) {
|
||||
qrBlockDialog = (
|
||||
<div className="mx_VerificationPanel_QRPhase_startOption">
|
||||
|
@ -132,7 +134,7 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
|
|||
);
|
||||
}
|
||||
|
||||
let qrBlock: JSX.Element;
|
||||
let qrBlock: JSX.Element | undefined;
|
||||
if (showQR) {
|
||||
qrBlock = (
|
||||
<div className="mx_UserInfo_container">
|
||||
|
@ -150,7 +152,7 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
|
|||
);
|
||||
}
|
||||
|
||||
let sasBlock: JSX.Element;
|
||||
let sasBlock: JSX.Element | undefined;
|
||||
if (showSAS) {
|
||||
const disabled = this.state.emojiButtonClicked;
|
||||
const sasLabel = showQR
|
||||
|
@ -189,18 +191,20 @@ 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();
|
||||
};
|
||||
|
||||
private onReciprocateNoClick = (): void => {
|
||||
if (!this.state.reciprocateQREvent) return;
|
||||
this.setState({ reciprocateButtonClicked: true });
|
||||
this.state.reciprocateQREvent.cancel();
|
||||
};
|
||||
|
||||
private getDevice(): DeviceInfo {
|
||||
const deviceId = this.props.request && this.props.request.channel.deviceId;
|
||||
return MatrixClientPeg.get().getStoredDevice(MatrixClientPeg.get().getUserId(), deviceId);
|
||||
private getDevice(): DeviceInfo | null {
|
||||
const cli = MatrixClientPeg.get();
|
||||
return cli.getStoredDevice(cli.getSafeUserId(), this.props.request?.channel.deviceId);
|
||||
}
|
||||
|
||||
private renderQRReciprocatePhase(): JSX.Element {
|
||||
|
@ -253,7 +257,7 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
|
|||
private renderVerifiedPhase(): JSX.Element {
|
||||
const { member, request } = this.props;
|
||||
|
||||
let text: string;
|
||||
let text: string | undefined;
|
||||
if (!request.isSelfVerification) {
|
||||
if (this.props.isRoomEncrypted) {
|
||||
text = _t("Verify all users in a room to ensure it's secure.");
|
||||
|
@ -348,7 +352,7 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
|
|||
const emojis = this.state.sasEvent ? (
|
||||
<VerificationShowSas
|
||||
displayName={displayName}
|
||||
device={this.getDevice()}
|
||||
device={this.getDevice() ?? undefined}
|
||||
sas={this.state.sasEvent.sas}
|
||||
onCancel={this.onSasMismatchesClick}
|
||||
onDone={this.onSasMatchesClick}
|
||||
|
@ -383,11 +387,11 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
|
|||
};
|
||||
|
||||
private onSasMatchesClick = (): void => {
|
||||
this.state.sasEvent.confirm();
|
||||
this.state.sasEvent?.confirm();
|
||||
};
|
||||
|
||||
private onSasMismatchesClick = (): void => {
|
||||
this.state.sasEvent.mismatch();
|
||||
this.state.sasEvent?.mismatch();
|
||||
};
|
||||
|
||||
private updateVerifierState = (): void => {
|
||||
|
|
|
@ -55,9 +55,9 @@ const WidgetCard: React.FC<IProps> = ({ room, widgetId, onClose }) => {
|
|||
// Don't render anything as we are about to transition
|
||||
if (!app || !isRight) return null;
|
||||
|
||||
let contextMenu;
|
||||
let contextMenu: JSX.Element | undefined;
|
||||
if (menuDisplayed) {
|
||||
const rect = handle.current.getBoundingClientRect();
|
||||
const rect = handle.current!.getBoundingClientRect();
|
||||
contextMenu = (
|
||||
<WidgetContextMenu
|
||||
chevronFace={ChevronFace.None}
|
||||
|
@ -92,7 +92,7 @@ const WidgetCard: React.FC<IProps> = ({ room, widgetId, onClose }) => {
|
|||
fullWidth
|
||||
showMenubar={false}
|
||||
room={room}
|
||||
userId={cli.getUserId()}
|
||||
userId={cli.getSafeUserId()}
|
||||
creatorUserId={app.creatorUserId}
|
||||
widgetPageTitle={WidgetUtils.getWidgetDataTitle(app)}
|
||||
waitForIframeLoad={app.waitForIframeLoad}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
>
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -46,7 +46,7 @@ interface IKeyboardShortcutProps {
|
|||
export const KeyboardShortcut: React.FC<IKeyboardShortcutProps> = ({ value, className = "mx_KeyboardShortcut" }) => {
|
||||
if (!value) return null;
|
||||
|
||||
const modifiersElement = [];
|
||||
const modifiersElement: JSX.Element[] = [];
|
||||
if (value.ctrlOrCmdKey) {
|
||||
modifiersElement.push(<KeyboardKey key="ctrlOrCmdKey" name={IS_MAC ? Key.META : Key.CONTROL} />);
|
||||
} else if (value.ctrlKey) {
|
||||
|
|
|
@ -41,7 +41,7 @@ import SdkConfig from "../../../SdkConfig";
|
|||
import AccessibleButton from "../elements/AccessibleButton";
|
||||
import TagComposer from "../elements/TagComposer";
|
||||
import { objectClone } from "../../../utils/objects";
|
||||
import { arrayDiff } from "../../../utils/arrays";
|
||||
import { arrayDiff, filterBoolean } from "../../../utils/arrays";
|
||||
import { clearAllNotifications, getLocalNotificationAccountDataEventType } from "../../../utils/notifications";
|
||||
import {
|
||||
updateExistingPushRulesWithActions,
|
||||
|
@ -167,7 +167,7 @@ const maximumVectorState = (
|
|||
if (!definition.syncedRuleIds?.length) {
|
||||
return undefined;
|
||||
}
|
||||
const vectorState = definition.syncedRuleIds.reduce<VectorState>((maxVectorState, ruleId) => {
|
||||
const vectorState = definition.syncedRuleIds.reduce<VectorState | undefined>((maxVectorState, ruleId) => {
|
||||
// already set to maximum
|
||||
if (maxVectorState === VectorState.Loud) {
|
||||
return maxVectorState;
|
||||
|
@ -345,7 +345,7 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
|
|||
for (const rule of defaultRules[category]) {
|
||||
const definition: VectorPushRuleDefinition = VectorPushRulesDefinitions[rule.rule_id];
|
||||
const vectorState = definition.ruleToVectorState(rule);
|
||||
preparedNewState.vectorPushRules[category].push({
|
||||
preparedNewState.vectorPushRules[category]!.push({
|
||||
ruleId: rule.rule_id,
|
||||
rule,
|
||||
vectorState,
|
||||
|
@ -355,7 +355,7 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
|
|||
}
|
||||
|
||||
// Quickly sort the rules for display purposes
|
||||
preparedNewState.vectorPushRules[category].sort((a, b) => {
|
||||
preparedNewState.vectorPushRules[category]!.sort((a, b) => {
|
||||
let idxA = RULE_DISPLAY_ORDER.indexOf(a.ruleId);
|
||||
let idxB = RULE_DISPLAY_ORDER.indexOf(b.ruleId);
|
||||
|
||||
|
@ -367,7 +367,7 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
|
|||
});
|
||||
|
||||
if (category === KEYWORD_RULE_CATEGORY) {
|
||||
preparedNewState.vectorPushRules[category].push({
|
||||
preparedNewState.vectorPushRules[category]!.push({
|
||||
ruleId: KEYWORD_RULE_ID,
|
||||
description: _t("Messages containing keywords"),
|
||||
vectorState: preparedNewState.vectorKeywordRuleInfo.vectorState,
|
||||
|
@ -540,8 +540,8 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
|
|||
private async setKeywords(keywords: string[], originalRules: IAnnotatedPushRule[]): Promise<void> {
|
||||
try {
|
||||
// De-duplicate and remove empties
|
||||
keywords = Array.from(new Set(keywords)).filter((k) => !!k);
|
||||
const oldKeywords = Array.from(new Set(originalRules.map((r) => r.pattern))).filter((k) => !!k);
|
||||
keywords = filterBoolean(Array.from(new Set(keywords)));
|
||||
const oldKeywords = filterBoolean(Array.from(new Set(originalRules.map((r) => r.pattern))));
|
||||
|
||||
// Note: Technically because of the UI interaction (at the time of writing), the diff
|
||||
// will only ever be +/-1 so we don't really have to worry about efficiently handling
|
||||
|
@ -555,13 +555,13 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
|
|||
}
|
||||
}
|
||||
|
||||
let ruleVectorState = this.state.vectorKeywordRuleInfo.vectorState;
|
||||
let ruleVectorState = this.state.vectorKeywordRuleInfo?.vectorState;
|
||||
if (ruleVectorState === VectorState.Off) {
|
||||
// When the current global keywords rule is OFF, we need to look at
|
||||
// the flavor of existing rules to apply the same actions
|
||||
// when creating the new rule.
|
||||
if (originalRules.length) {
|
||||
ruleVectorState = PushRuleVectorState.contentRuleVectorStateKind(originalRules[0]);
|
||||
ruleVectorState = PushRuleVectorState.contentRuleVectorStateKind(originalRules[0]) ?? undefined;
|
||||
} else {
|
||||
ruleVectorState = VectorState.On; // default
|
||||
}
|
||||
|
@ -776,7 +776,7 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
|
|||
/>
|
||||
);
|
||||
|
||||
const fieldsetRows = this.state.vectorPushRules[category]?.map((r) => (
|
||||
const fieldsetRows = this.state.vectorPushRules?.[category]?.map((r) => (
|
||||
<fieldset
|
||||
key={category + r.ruleId}
|
||||
data-testid={category + r.ruleId}
|
||||
|
@ -840,7 +840,7 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
|
|||
</tr>
|
||||
));
|
||||
|
||||
if (!rows.length) return null; // no targets to show
|
||||
if (!rows?.length) return null; // no targets to show
|
||||
|
||||
return (
|
||||
<div className="mx_UserNotifSettings_floatingSection">
|
||||
|
|
|
@ -72,7 +72,7 @@ interface IProps {
|
|||
|
||||
interface IState {
|
||||
defaultIdServer?: string;
|
||||
currentClientIdServer: string;
|
||||
currentClientIdServer?: string;
|
||||
idServer?: string;
|
||||
error?: string;
|
||||
busy: boolean;
|
||||
|
|
|
@ -402,14 +402,14 @@ export default class RolesRoomSettingsTab extends React.Component<IProps> {
|
|||
{banned.map((member) => {
|
||||
const banEvent = member.events.member?.getContent();
|
||||
const sender = room?.getMember(member.events.member.getSender());
|
||||
let bannedBy = member.events.member.getSender(); // start by falling back to mxid
|
||||
let bannedBy = member.events.member?.getSender(); // start by falling back to mxid
|
||||
if (sender) bannedBy = sender.name;
|
||||
return (
|
||||
<BannedUser
|
||||
key={member.userId}
|
||||
canUnban={canBanUsers}
|
||||
member={member}
|
||||
reason={banEvent.reason}
|
||||
reason={banEvent?.reason}
|
||||
by={bannedBy}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -61,7 +61,7 @@ interface IProps {
|
|||
|
||||
interface IState {
|
||||
language: string;
|
||||
spellCheckEnabled: boolean;
|
||||
spellCheckEnabled?: boolean;
|
||||
spellCheckLanguages: string[];
|
||||
haveIdServer: boolean;
|
||||
serverSupportsSeparateAddAndBind?: boolean;
|
||||
|
@ -406,7 +406,7 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta
|
|||
<div className="mx_SettingsTab_section mx_SettingsTab_section_spellcheck">
|
||||
<span className="mx_SettingsTab_subheading">
|
||||
{_t("Spell check")}
|
||||
<ToggleSwitch checked={this.state.spellCheckEnabled} onChange={this.onSpellCheckEnabledChange} />
|
||||
<ToggleSwitch checked={!!this.state.spellCheckEnabled} onChange={this.onSpellCheckEnabledChange} />
|
||||
</span>
|
||||
{this.state.spellCheckEnabled && !IS_MAC && (
|
||||
<SpellCheckSettings
|
||||
|
|
|
@ -56,7 +56,7 @@ const confirmSignOut = async (sessionsToSignOutCount: number): Promise<boolean>
|
|||
});
|
||||
const [confirmed] = await finished;
|
||||
|
||||
return confirmed;
|
||||
return !!confirmed;
|
||||
};
|
||||
|
||||
const useSignOut = (
|
||||
|
|
|
@ -61,7 +61,7 @@ export default class VoiceUserSettingsTab extends React.Component<{}, IState> {
|
|||
|
||||
private refreshMediaDevices = async (stream?: MediaStream): Promise<void> => {
|
||||
this.setState({
|
||||
mediaDevices: await MediaDeviceHandler.getDevices(),
|
||||
mediaDevices: (await MediaDeviceHandler.getDevices()) ?? null,
|
||||
[MediaDeviceKindEnum.AudioOutput]: MediaDeviceHandler.getAudioOutput(),
|
||||
[MediaDeviceKindEnum.AudioInput]: MediaDeviceHandler.getAudioInput(),
|
||||
[MediaDeviceKindEnum.VideoInput]: MediaDeviceHandler.getVideoInput(),
|
||||
|
@ -105,8 +105,8 @@ export default class VoiceUserSettingsTab extends React.Component<{}, IState> {
|
|||
}
|
||||
|
||||
private renderDropdown(kind: MediaDeviceKindEnum, label: string): ReactNode {
|
||||
const devices = this.state.mediaDevices[kind].slice(0);
|
||||
if (devices.length === 0) return null;
|
||||
const devices = this.state.mediaDevices?.[kind].slice(0);
|
||||
if (!devices?.length) return null;
|
||||
|
||||
const defaultDevice = MediaDeviceHandler.getDefaultDevice(devices);
|
||||
return (
|
||||
|
|
|
@ -77,7 +77,7 @@ export default class VerificationRequestToast extends React.PureComponent<IProps
|
|||
const device = await cli.getDevice(request.channel.deviceId);
|
||||
const ip = device.last_seen_ip;
|
||||
this.setState({
|
||||
device: cli.getStoredDevice(cli.getUserId()!, request.channel.deviceId),
|
||||
device: cli.getStoredDevice(cli.getUserId()!, request.channel.deviceId) ?? undefined,
|
||||
ip,
|
||||
});
|
||||
}
|
||||
|
@ -118,7 +118,7 @@ export default class VerificationRequestToast extends React.PureComponent<IProps
|
|||
should_peek: false,
|
||||
metricsTrigger: "VerificationRequest",
|
||||
});
|
||||
const member = cli.getUser(request.otherUserId);
|
||||
const member = cli.getUser(request.otherUserId) ?? undefined;
|
||||
RightPanelStore.instance.setCards(
|
||||
[
|
||||
{ phase: RightPanelPhases.RoomSummary },
|
||||
|
|
|
@ -158,14 +158,14 @@ export const Lobby: FC<LobbyProps> = ({ room, joinCallButtonDisabledTooltip, con
|
|||
// non-blank labels for the devices.
|
||||
let stream: MediaStream | null = null;
|
||||
try {
|
||||
if (devices.audioinput.length > 0) {
|
||||
if (devices!.audioinput.length > 0) {
|
||||
// Holding just an audio stream will be enough to get us all device labels, so
|
||||
// if video is muted, don't bother requesting video.
|
||||
stream = await navigator.mediaDevices.getUserMedia({
|
||||
audio: true,
|
||||
video: !videoMuted && devices.videoinput.length > 0 && { deviceId: videoInputId },
|
||||
video: !videoMuted && devices!.videoinput.length > 0 && { deviceId: videoInputId },
|
||||
});
|
||||
} else if (devices.videoinput.length > 0) {
|
||||
} else if (devices!.videoinput.length > 0) {
|
||||
// We have to resort to a video stream, even if video is supposed to be muted.
|
||||
stream = await navigator.mediaDevices.getUserMedia({ video: { deviceId: videoInputId } });
|
||||
}
|
||||
|
@ -182,7 +182,7 @@ export const Lobby: FC<LobbyProps> = ({ room, joinCallButtonDisabledTooltip, con
|
|||
stream = null;
|
||||
}
|
||||
|
||||
return [stream, devices.audioinput, devices.videoinput];
|
||||
return [stream, devices?.audioinput ?? [], devices?.videoinput ?? []];
|
||||
},
|
||||
[videoInputId, videoMuted],
|
||||
[null, [], []],
|
||||
|
|
|
@ -22,7 +22,7 @@ import { Action } from "../actions";
|
|||
export interface ViewRoomErrorPayload extends Pick<ActionPayload, "action"> {
|
||||
action: Action.ViewRoomError;
|
||||
// eslint-disable-next-line camelcase
|
||||
room_id: Room["roomId"];
|
||||
room_id: Room["roomId"] | null;
|
||||
// eslint-disable-next-line camelcase
|
||||
room_alias?: string;
|
||||
err?: MatrixError;
|
||||
|
|
|
@ -47,7 +47,7 @@ export default class HistoryManager {
|
|||
this.removedSinceLastPush = false;
|
||||
}
|
||||
|
||||
private shouldPush(inputType: string, diff: IDiff): boolean {
|
||||
private shouldPush(inputType?: string, diff?: IDiff): boolean {
|
||||
// right now we can only push a step after
|
||||
// the input has been applied to the model,
|
||||
// so we can't push the state before something happened.
|
||||
|
@ -102,7 +102,7 @@ export default class HistoryManager {
|
|||
}
|
||||
|
||||
// needs to persist parts and caret position
|
||||
public tryPush(model: EditorModel, caret: Caret, inputType: string, diff: IDiff): boolean {
|
||||
public tryPush(model: EditorModel, caret: Caret, inputType?: string, diff?: IDiff): boolean {
|
||||
// ignore state restoration echos.
|
||||
// these respect the inputType values of the input event,
|
||||
// but are actually passed in from MessageEditor calling model.reset()
|
||||
|
|
|
@ -171,7 +171,7 @@ export default class EditorModel {
|
|||
this._autoComplete = null;
|
||||
this.autoCompletePartIdx = null;
|
||||
}
|
||||
this.updateCallback(caret, inputType);
|
||||
this.updateCallback?.(caret, inputType);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -119,7 +119,7 @@ export default class AutoRageshakeStore extends AsyncStoreWithClient<IState> {
|
|||
room_id: ev.getRoomId(),
|
||||
session_id: sessionId,
|
||||
device_id: wireContent.device_id,
|
||||
user_id: ev.getSender(),
|
||||
user_id: ev.getSender()!,
|
||||
sender_key: wireContent.sender_key,
|
||||
};
|
||||
|
||||
|
|
|
@ -62,14 +62,14 @@ export class ModalWidgetStore extends AsyncStoreWithClient<IState> {
|
|||
): void => {
|
||||
if (this.modalInstance) return;
|
||||
this.openSourceWidgetId = sourceWidget.id;
|
||||
this.openSourceWidgetRoomId = widgetRoomId;
|
||||
this.openSourceWidgetRoomId = widgetRoomId ?? null;
|
||||
this.modalInstance = Modal.createDialog(
|
||||
ModalWidgetDialog,
|
||||
{
|
||||
widgetDefinition: { ...requestData },
|
||||
widgetRoomId,
|
||||
sourceWidgetId: sourceWidget.id,
|
||||
onFinished: (success: boolean, data?: IModalWidgetReturnData) => {
|
||||
onFinished: (success, data) => {
|
||||
if (!success) {
|
||||
this.closeModalWidget(sourceWidget, widgetRoomId, { "m.exited": true });
|
||||
} else {
|
||||
|
@ -81,13 +81,17 @@ export class ModalWidgetStore extends AsyncStoreWithClient<IState> {
|
|||
this.modalInstance = null;
|
||||
},
|
||||
},
|
||||
null,
|
||||
undefined,
|
||||
/* priority = */ false,
|
||||
/* static = */ true,
|
||||
);
|
||||
};
|
||||
|
||||
public closeModalWidget = (sourceWidget: Widget, widgetRoomId?: string, data?: IModalWidgetReturnData): void => {
|
||||
public closeModalWidget = (
|
||||
sourceWidget: Widget,
|
||||
widgetRoomId: string | undefined,
|
||||
data: IModalWidgetReturnData,
|
||||
): void => {
|
||||
if (!this.modalInstance) return;
|
||||
if (this.openSourceWidgetId === sourceWidget.id && this.openSourceWidgetRoomId === widgetRoomId) {
|
||||
this.openSourceWidgetId = null;
|
||||
|
|
|
@ -50,4 +50,4 @@ export class RoomScrollStateStore {
|
|||
if (window.mxRoomScrollStateStore === undefined) {
|
||||
window.mxRoomScrollStateStore = new RoomScrollStateStore();
|
||||
}
|
||||
export default window.mxRoomScrollStateStore;
|
||||
export default window.mxRoomScrollStateStore!;
|
||||
|
|
|
@ -395,7 +395,6 @@ export class RoomViewStore extends EventEmitter {
|
|||
roomId: payload.room_id,
|
||||
initialEventId: null,
|
||||
initialEventPixelOffset: null,
|
||||
isInitialEventHighlighted: null,
|
||||
initialEventScrollIntoView: true,
|
||||
roomAlias: null,
|
||||
roomLoading: true,
|
||||
|
@ -567,13 +566,13 @@ export class RoomViewStore extends EventEmitter {
|
|||
}
|
||||
}
|
||||
|
||||
private getInvitingUserId(roomId: string): string {
|
||||
private getInvitingUserId(roomId: string): string | undefined {
|
||||
const cli = MatrixClientPeg.get();
|
||||
const room = cli.getRoom(roomId);
|
||||
if (room?.getMyMembership() === "invite") {
|
||||
const myMember = room.getMember(cli.getUserId());
|
||||
const myMember = room.getMember(cli.getSafeUserId());
|
||||
const inviteEvent = myMember ? myMember.events.member : null;
|
||||
return inviteEvent && inviteEvent.getSender();
|
||||
return inviteEvent?.getSender();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -278,28 +278,28 @@ export default class RightPanelStore extends ReadyWatchingStore {
|
|||
// (A nicer fix could be to indicate, that the right panel is loading if there is missing state data and re-emit if the data is available)
|
||||
switch (card.phase) {
|
||||
case RightPanelPhases.ThreadView:
|
||||
if (!card.state.threadHeadEvent) {
|
||||
if (!card.state?.threadHeadEvent) {
|
||||
logger.warn("removed card from right panel because of missing threadHeadEvent in card state");
|
||||
}
|
||||
return !!card.state.threadHeadEvent;
|
||||
return !!card.state?.threadHeadEvent;
|
||||
case RightPanelPhases.RoomMemberInfo:
|
||||
case RightPanelPhases.SpaceMemberInfo:
|
||||
case RightPanelPhases.EncryptionPanel:
|
||||
if (!card.state.member) {
|
||||
if (!card.state?.member) {
|
||||
logger.warn("removed card from right panel because of missing member in card state");
|
||||
}
|
||||
return !!card.state.member;
|
||||
return !!card.state?.member;
|
||||
case RightPanelPhases.Room3pidMemberInfo:
|
||||
case RightPanelPhases.Space3pidMemberInfo:
|
||||
if (!card.state.memberInfoEvent) {
|
||||
if (!card.state?.memberInfoEvent) {
|
||||
logger.warn("removed card from right panel because of missing memberInfoEvent in card state");
|
||||
}
|
||||
return !!card.state.memberInfoEvent;
|
||||
return !!card.state?.memberInfoEvent;
|
||||
case RightPanelPhases.Widget:
|
||||
if (!card.state.widgetId) {
|
||||
if (!card.state?.widgetId) {
|
||||
logger.warn("removed card from right panel because of missing widgetId in card state");
|
||||
}
|
||||
return !!card.state.widgetId;
|
||||
return !!card.state?.widgetId;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -168,7 +168,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||
return this.rootSpaces;
|
||||
}
|
||||
|
||||
public get activeSpace(): SpaceKey | undefined {
|
||||
public get activeSpace(): SpaceKey {
|
||||
return this._activeSpace;
|
||||
}
|
||||
|
||||
|
@ -1018,7 +1018,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||
private onRoomStateMembers = (ev: MatrixEvent): void => {
|
||||
const room = this.matrixClient.getRoom(ev.getRoomId());
|
||||
|
||||
const userId = ev.getStateKey();
|
||||
const userId = ev.getStateKey()!;
|
||||
if (
|
||||
room?.isSpaceRoom() && // only consider space rooms
|
||||
DMRoomMap.shared().getDMRoomsForUserId(userId).length > 0 && // only consider members we have a DM with
|
||||
|
@ -1049,9 +1049,9 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||
private onRoomFavouriteChange(room: Room): void {
|
||||
if (this.enabledMetaSpaces.includes(MetaSpace.Favourites)) {
|
||||
if (room.tags[DefaultTagID.Favourite]) {
|
||||
this.roomIdsBySpace.get(MetaSpace.Favourites).add(room.roomId);
|
||||
this.roomIdsBySpace.get(MetaSpace.Favourites)?.add(room.roomId);
|
||||
} else {
|
||||
this.roomIdsBySpace.get(MetaSpace.Favourites).delete(room.roomId);
|
||||
this.roomIdsBySpace.get(MetaSpace.Favourites)?.delete(room.roomId);
|
||||
}
|
||||
this.emit(MetaSpace.Favourites);
|
||||
}
|
||||
|
@ -1064,7 +1064,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||
const homeRooms = this.roomIdsBySpace.get(MetaSpace.Home);
|
||||
if (this.showInHomeSpace(room)) {
|
||||
homeRooms?.add(room.roomId);
|
||||
} else if (!this.roomIdsBySpace.get(MetaSpace.Orphans).has(room.roomId)) {
|
||||
} else if (!this.roomIdsBySpace.get(MetaSpace.Orphans)?.has(room.roomId)) {
|
||||
this.roomIdsBySpace.get(MetaSpace.Home)?.delete(room.roomId);
|
||||
}
|
||||
|
||||
|
@ -1076,7 +1076,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
|||
}
|
||||
|
||||
if (enabledMetaSpaces.has(MetaSpace.Orphans) || enabledMetaSpaces.has(MetaSpace.Home)) {
|
||||
if (isDm && this.roomIdsBySpace.get(MetaSpace.Orphans).delete(room.roomId)) {
|
||||
if (isDm && this.roomIdsBySpace.get(MetaSpace.Orphans)?.delete(room.roomId)) {
|
||||
this.emit(MetaSpace.Orphans);
|
||||
this.emit(MetaSpace.Home);
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { defer } from "matrix-js-sdk/src/utils";
|
||||
import React from "react";
|
||||
|
||||
import BaseDialog from "../../../components/views/dialogs/BaseDialog";
|
||||
|
@ -46,11 +45,7 @@ export const ConfirmListenBroadcastStopCurrentDialog: React.FC<Props> = ({ onFin
|
|||
};
|
||||
|
||||
export const showConfirmListenBroadcastStopCurrentDialog = async (): Promise<boolean> => {
|
||||
const { promise, resolve } = defer<boolean>();
|
||||
|
||||
Modal.createDialog(ConfirmListenBroadcastStopCurrentDialog, {
|
||||
onFinished: resolve,
|
||||
});
|
||||
|
||||
return promise;
|
||||
const { finished } = Modal.createDialog(ConfirmListenBroadcastStopCurrentDialog);
|
||||
const [confirmed] = await finished;
|
||||
return !!confirmed;
|
||||
};
|
|
@ -35,7 +35,7 @@ export const useVoiceBroadcastPlayback = (
|
|||
position: number;
|
||||
timeLeft: number;
|
||||
};
|
||||
sender: RoomMember;
|
||||
sender: RoomMember | null;
|
||||
liveness: VoiceBroadcastLiveness;
|
||||
playbackState: VoiceBroadcastPlaybackState;
|
||||
toggle(): void;
|
||||
|
|
|
@ -44,7 +44,7 @@ const showStopBroadcastingDialog = async (): Promise<boolean> => {
|
|||
button: _t("Yes, stop broadcast"),
|
||||
});
|
||||
const [confirmed] = await finished;
|
||||
return confirmed;
|
||||
return !!confirmed;
|
||||
};
|
||||
|
||||
export const useVoiceBroadcastRecording = (
|
||||
|
@ -54,7 +54,7 @@ export const useVoiceBroadcastRecording = (
|
|||
timeLeft: number;
|
||||
recordingState: VoiceBroadcastRecordingState;
|
||||
room: Room;
|
||||
sender: RoomMember;
|
||||
sender: RoomMember | null;
|
||||
stopRecording(): void;
|
||||
toggleRecording(): void;
|
||||
} => {
|
||||
|
|
|
@ -32,7 +32,7 @@ export * from "./components/atoms/VoiceBroadcastHeader";
|
|||
export * from "./components/atoms/VoiceBroadcastPlaybackControl";
|
||||
export * from "./components/atoms/VoiceBroadcastRecordingConnectionError";
|
||||
export * from "./components/atoms/VoiceBroadcastRoomSubtitle";
|
||||
export * from "./components/molecules/ConfirmListeBroadcastStopCurrent";
|
||||
export * from "./components/molecules/ConfirmListenBroadcastStopCurrent";
|
||||
export * from "./components/molecules/VoiceBroadcastPlaybackBody";
|
||||
export * from "./components/molecules/VoiceBroadcastSmallPlaybackBody";
|
||||
export * from "./components/molecules/VoiceBroadcastPreRecordingPip";
|
||||
|
|
|
@ -674,7 +674,7 @@ describe("MessagePanel", function () {
|
|||
// Increase the length of the loop here to test performance issues with
|
||||
// rendering
|
||||
|
||||
const events = [];
|
||||
const events: MatrixEvent[] = [];
|
||||
for (let i = 0; i < 100; i++) {
|
||||
events.push(
|
||||
TestUtilsMatrix.mkEvent({
|
||||
|
|
|
@ -35,6 +35,7 @@ import RightPanelStore from "../../../src/stores/right-panel/RightPanelStore";
|
|||
import { UPDATE_EVENT } from "../../../src/stores/AsyncStore";
|
||||
import { WidgetLayoutStore } from "../../../src/stores/widgets/WidgetLayoutStore";
|
||||
import { SdkContextClass } from "../../../src/contexts/SDKContext";
|
||||
import { RoomPermalinkCreator } from "../../../src/utils/permalinks/Permalinks";
|
||||
|
||||
const RightPanelBase = wrapInMatrixClientContext(_RightPanel);
|
||||
|
||||
|
@ -110,7 +111,13 @@ describe("RightPanel", () => {
|
|||
});
|
||||
await viewedRoom;
|
||||
|
||||
const { container } = render(<RightPanel room={r1} resizeNotifier={resizeNotifier} />);
|
||||
const { container } = render(
|
||||
<RightPanel
|
||||
room={r1}
|
||||
resizeNotifier={resizeNotifier}
|
||||
permalinkCreator={new RoomPermalinkCreator(r1, r1.roomId)}
|
||||
/>,
|
||||
);
|
||||
expect(container.getElementsByClassName("mx_RoomSummaryCard")).toHaveLength(1);
|
||||
|
||||
const switchedPhases = waitForRpsUpdate();
|
||||
|
@ -152,7 +159,13 @@ describe("RightPanel", () => {
|
|||
await spinUpStores();
|
||||
|
||||
// Run initial render with room 1, and also running lifecycle methods
|
||||
const { container, rerender } = render(<RightPanel room={r1} resizeNotifier={resizeNotifier} />);
|
||||
const { container, rerender } = render(
|
||||
<RightPanel
|
||||
room={r1}
|
||||
resizeNotifier={resizeNotifier}
|
||||
permalinkCreator={new RoomPermalinkCreator(r1, r1.roomId)}
|
||||
/>,
|
||||
);
|
||||
// Wait for RPS room 1 updates to fire
|
||||
const rpsUpdated = waitForRpsUpdate();
|
||||
dis.dispatch({
|
||||
|
@ -172,7 +185,13 @@ describe("RightPanel", () => {
|
|||
room_id: "r2",
|
||||
});
|
||||
await _rpsUpdated;
|
||||
rerender(<RightPanel room={r2} resizeNotifier={resizeNotifier} />);
|
||||
rerender(
|
||||
<RightPanel
|
||||
room={r2}
|
||||
resizeNotifier={resizeNotifier}
|
||||
permalinkCreator={new RoomPermalinkCreator(r2, r2.roomId)}
|
||||
/>,
|
||||
);
|
||||
|
||||
// After all that setup, now to the interesting part...
|
||||
// We want to verify that as we change to room 2, we should always have
|
||||
|
|
|
@ -50,6 +50,7 @@ import { ElementWidgetCapabilities } from "../../../../src/stores/widgets/Elemen
|
|||
import { ElementWidget } from "../../../../src/stores/widgets/StopGapWidget";
|
||||
import { WidgetMessagingStore } from "../../../../src/stores/widgets/WidgetMessagingStore";
|
||||
import { ModuleRunner } from "../../../../src/modules/ModuleRunner";
|
||||
import { RoomPermalinkCreator } from "../../../../src/utils/permalinks/Permalinks";
|
||||
|
||||
describe("AppTile", () => {
|
||||
let cli: MatrixClient;
|
||||
|
@ -153,7 +154,11 @@ describe("AppTile", () => {
|
|||
// Run initial render with room 1, and also running lifecycle methods
|
||||
const renderResult = render(
|
||||
<MatrixClientContext.Provider value={cli}>
|
||||
<RightPanel room={r1} resizeNotifier={resizeNotifier} />
|
||||
<RightPanel
|
||||
room={r1}
|
||||
resizeNotifier={resizeNotifier}
|
||||
permalinkCreator={new RoomPermalinkCreator(r1, r1.roomId)}
|
||||
/>
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
// Wait for RPS room 1 updates to fire
|
||||
|
@ -178,7 +183,11 @@ describe("AppTile", () => {
|
|||
|
||||
renderResult.rerender(
|
||||
<MatrixClientContext.Provider value={cli}>
|
||||
<RightPanel room={r2} resizeNotifier={resizeNotifier} />
|
||||
<RightPanel
|
||||
room={r2}
|
||||
resizeNotifier={resizeNotifier}
|
||||
permalinkCreator={new RoomPermalinkCreator(r2, r2.roomId)}
|
||||
/>
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
|
||||
|
@ -214,7 +223,11 @@ describe("AppTile", () => {
|
|||
// Run initial render with room 1, and also running lifecycle methods
|
||||
const renderResult = render(
|
||||
<MatrixClientContext.Provider value={cli}>
|
||||
<RightPanel room={r1} resizeNotifier={resizeNotifier} />
|
||||
<RightPanel
|
||||
room={r1}
|
||||
resizeNotifier={resizeNotifier}
|
||||
permalinkCreator={new RoomPermalinkCreator(r1, r1.roomId)}
|
||||
/>
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
// Wait for RPS room 1 updates to fire
|
||||
|
@ -256,7 +269,11 @@ describe("AppTile", () => {
|
|||
});
|
||||
renderResult.rerender(
|
||||
<MatrixClientContext.Provider value={cli}>
|
||||
<RightPanel room={r2} resizeNotifier={resizeNotifier} />
|
||||
<RightPanel
|
||||
room={r2}
|
||||
resizeNotifier={resizeNotifier}
|
||||
permalinkCreator={new RoomPermalinkCreator(r2, r2.roomId)}
|
||||
/>
|
||||
</MatrixClientContext.Provider>,
|
||||
);
|
||||
await rpsUpdated2;
|
||||
|
|
|
@ -68,7 +68,7 @@ describe("PreferencesUserSettingsTab", () => {
|
|||
|
||||
const expectSetValueToHaveBeenCalled = (
|
||||
name: string,
|
||||
roomId: string | undefined,
|
||||
roomId: string | null,
|
||||
level: SettingLevel,
|
||||
value: boolean,
|
||||
) => expect(SettingsStore.setValue).toHaveBeenCalledWith(name, roomId, level, value);
|
||||
|
|
Loading…
Reference in a new issue