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.
|
* The combo is evaluated strictly, i.e. the KeyboardEvent must match exactly what is specified in the KeyCombo.
|
||||||
*/
|
*/
|
||||||
export type KeyCombo = {
|
export type KeyCombo = {
|
||||||
key?: string;
|
key: string;
|
||||||
|
|
||||||
/** On PC: ctrl is pressed; on Mac: meta is pressed */
|
/** On PC: ctrl is pressed; on Mac: meta is pressed */
|
||||||
ctrlOrCmdKey?: boolean;
|
ctrlOrCmdKey?: boolean;
|
||||||
|
|
|
@ -306,7 +306,7 @@ class MatrixClientPegClass implements IMatrixClientPeg {
|
||||||
homeserverUrl: this.matrixClient.baseUrl,
|
homeserverUrl: this.matrixClient.baseUrl,
|
||||||
identityServerUrl: this.matrixClient.idBaseUrl,
|
identityServerUrl: this.matrixClient.idBaseUrl,
|
||||||
userId: this.matrixClient.credentials.userId,
|
userId: this.matrixClient.credentials.userId,
|
||||||
deviceId: this.matrixClient.getDeviceId(),
|
deviceId: this.matrixClient.getDeviceId() ?? undefined,
|
||||||
accessToken: this.matrixClient.getAccessToken(),
|
accessToken: this.matrixClient.getAccessToken(),
|
||||||
guest: this.matrixClient.isGuest(),
|
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));
|
const newSlice = result.results.slice(Math.max(result.results.length - newResultCount, 0));
|
||||||
restoreEncryptionInfo(newSlice);
|
restoreEncryptionInfo(newSlice);
|
||||||
|
|
||||||
searchResult.pendingRequest = null;
|
searchResult.pendingRequest = undefined;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,8 @@ interface IProps {
|
||||||
defaultName?: string;
|
defaultName?: string;
|
||||||
parentSpace?: Room;
|
parentSpace?: Room;
|
||||||
defaultEncrypted?: boolean;
|
defaultEncrypted?: boolean;
|
||||||
onFinished(proceed?: boolean, opts?: IOpts): void;
|
onFinished(proceed?: false): void;
|
||||||
|
onFinished(proceed: true, opts: IOpts): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
|
|
|
@ -46,7 +46,8 @@ interface IProps {
|
||||||
widgetDefinition: IModalWidgetOpenRequestData;
|
widgetDefinition: IModalWidgetOpenRequestData;
|
||||||
widgetRoomId?: string;
|
widgetRoomId?: string;
|
||||||
sourceWidgetId: string;
|
sourceWidgetId: string;
|
||||||
onFinished(success?: boolean, data?: IModalWidgetReturnData): void;
|
onFinished(success: true, data: IModalWidgetReturnData): void;
|
||||||
|
onFinished(success?: false, data?: void): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
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
|
// We want to recalculate zoom whenever the window's size changes
|
||||||
window.addEventListener("resize", this.recalculateZoom);
|
window.addEventListener("resize", this.recalculateZoom);
|
||||||
// After the image loads for the first time we want to calculate the zoom
|
// 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 {
|
public componentWillUnmount(): void {
|
||||||
this.focusLock.current.removeEventListener("wheel", this.onWheel);
|
this.focusLock.current.removeEventListener("wheel", this.onWheel);
|
||||||
window.removeEventListener("resize", this.recalculateZoom);
|
window.removeEventListener("resize", this.recalculateZoom);
|
||||||
this.image.current.removeEventListener("load", this.imageLoaded);
|
this.image.current?.removeEventListener("load", this.imageLoaded);
|
||||||
}
|
}
|
||||||
|
|
||||||
private imageLoaded = (): void => {
|
private imageLoaded = (): void => {
|
||||||
|
@ -171,6 +171,7 @@ export default class ImageView extends React.Component<IProps, IState> {
|
||||||
private setZoomAndRotation = (inputRotation?: number): void => {
|
private setZoomAndRotation = (inputRotation?: number): void => {
|
||||||
const image = this.image.current;
|
const image = this.image.current;
|
||||||
const imageWrapper = this.imageWrapper.current;
|
const imageWrapper = this.imageWrapper.current;
|
||||||
|
if (!image || !imageWrapper) return;
|
||||||
|
|
||||||
const rotation = inputRotation ?? this.state.rotation;
|
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.
|
// Zoom relative to the given point on the image.
|
||||||
// First we need to figure out the offset of the anchor point
|
// First we need to figure out the offset of the anchor point
|
||||||
// relative to the center of the image, accounting for rotation.
|
// relative to the center of the image, accounting for rotation.
|
||||||
let offsetX;
|
let offsetX: number | undefined;
|
||||||
let offsetY;
|
let offsetY: number | undefined;
|
||||||
// The modulo operator can return negative values for some
|
// The modulo operator can return negative values for some
|
||||||
// rotations, so we have to do some extra work to normalize it
|
// rotations, so we have to do some extra work to normalize it
|
||||||
switch (((this.state.rotation % 360) + 360) % 360) {
|
switch (((this.state.rotation % 360) + 360) % 360) {
|
||||||
|
@ -310,7 +311,7 @@ export default class ImageView extends React.Component<IProps, IState> {
|
||||||
private onDownloadClick = (): void => {
|
private onDownloadClick = (): void => {
|
||||||
const a = document.createElement("a");
|
const a = document.createElement("a");
|
||||||
a.href = this.props.src;
|
a.href = this.props.src;
|
||||||
a.download = this.props.name;
|
if (this.props.name) a.download = this.props.name;
|
||||||
a.target = "_blank";
|
a.target = "_blank";
|
||||||
a.rel = "noreferrer noopener";
|
a.rel = "noreferrer noopener";
|
||||||
a.click();
|
a.click();
|
||||||
|
@ -334,9 +335,9 @@ export default class ImageView extends React.Component<IProps, IState> {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
dis.dispatch<ViewRoomPayload>({
|
dis.dispatch<ViewRoomPayload>({
|
||||||
action: Action.ViewRoom,
|
action: Action.ViewRoom,
|
||||||
event_id: this.props.mxEvent.getId(),
|
event_id: this.props.mxEvent?.getId(),
|
||||||
highlighted: true,
|
highlighted: true,
|
||||||
room_id: this.props.mxEvent.getRoomId(),
|
room_id: this.props.mxEvent?.getRoomId(),
|
||||||
metricsTrigger: undefined, // room doesn't change
|
metricsTrigger: undefined, // room doesn't change
|
||||||
});
|
});
|
||||||
this.props.onFinished();
|
this.props.onFinished();
|
||||||
|
@ -440,11 +441,11 @@ export default class ImageView extends React.Component<IProps, IState> {
|
||||||
|
|
||||||
let info: JSX.Element | undefined;
|
let info: JSX.Element | undefined;
|
||||||
if (showEventMeta) {
|
if (showEventMeta) {
|
||||||
const mxEvent = this.props.mxEvent;
|
const mxEvent = this.props.mxEvent!;
|
||||||
const showTwelveHour = SettingsStore.getValue("showTwelveHourTimestamps");
|
const showTwelveHour = SettingsStore.getValue("showTwelveHourTimestamps");
|
||||||
let permalink = "#";
|
let permalink = "#";
|
||||||
if (this.props.permalinkCreator) {
|
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();
|
const senderName = mxEvent.sender?.name ?? mxEvent.getSender();
|
||||||
|
@ -453,7 +454,7 @@ export default class ImageView extends React.Component<IProps, IState> {
|
||||||
<a
|
<a
|
||||||
href={permalink}
|
href={permalink}
|
||||||
onClick={this.onPermalinkClicked}
|
onClick={this.onPermalinkClicked}
|
||||||
aria-label={formatFullDate(new Date(this.props.mxEvent.getTs()), showTwelveHour, false)}
|
aria-label={formatFullDate(new Date(mxEvent.getTs()), showTwelveHour, false)}
|
||||||
>
|
>
|
||||||
<MessageTimestamp
|
<MessageTimestamp
|
||||||
showFullDate={true}
|
showFullDate={true}
|
||||||
|
|
|
@ -66,17 +66,17 @@ interface IProps<T> {
|
||||||
// should typically be less than `overflowItems` unless applying
|
// should typically be less than `overflowItems` unless applying
|
||||||
// margins in the parent component when using multiple LazyRenderList in one viewport.
|
// margins in the parent component when using multiple LazyRenderList in one viewport.
|
||||||
// use 0 to only rerender when items will come into view.
|
// 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,
|
// the amount of items to add at the top and bottom to render,
|
||||||
// so not every scroll of causes a rerender.
|
// so not every scroll of causes a rerender.
|
||||||
overflowItems?: number;
|
overflowItems: number;
|
||||||
|
|
||||||
element?: string;
|
element?: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
renderRange: ItemRange | null;
|
renderRange: ItemRange;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class LazyRenderList<T = any> extends React.Component<IProps<T>, IState> {
|
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>) {
|
public constructor(props: IProps<T>) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = LazyRenderList.getDerivedStateFromProps(props, {} as IState) as IState;
|
||||||
renderRange: null,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static getDerivedStateFromProps(props: IProps<unknown>, state: IState): Partial<IState> | null {
|
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 EncryptionEvent = forwardRef<HTMLDivElement, IProps>(({ mxEvent, timestamp }, ref) => {
|
||||||
const cli = useContext(MatrixClientContext);
|
const cli = useContext(MatrixClientContext);
|
||||||
const roomId = mxEvent.getRoomId();
|
const roomId = mxEvent.getRoomId()!;
|
||||||
const isRoomEncrypted = MatrixClientPeg.get().isRoomEncrypted(roomId);
|
const isRoomEncrypted = MatrixClientPeg.get().isRoomEncrypted(roomId);
|
||||||
|
|
||||||
const prevContent = mxEvent.getPrevContent() as IRoomEncryption;
|
const prevContent = mxEvent.getPrevContent() as IRoomEncryption;
|
||||||
|
@ -51,13 +51,13 @@ const EncryptionEvent = forwardRef<HTMLDivElement, IProps>(({ mxEvent, timestamp
|
||||||
if (prevContent.algorithm === ALGORITHM) {
|
if (prevContent.algorithm === ALGORITHM) {
|
||||||
subtitle = _t("Some encryption parameters have been changed.");
|
subtitle = _t("Some encryption parameters have been changed.");
|
||||||
} else if (dmPartner) {
|
} else if (dmPartner) {
|
||||||
const displayName = room.getMember(dmPartner)?.rawDisplayName || dmPartner;
|
const displayName = room?.getMember(dmPartner)?.rawDisplayName || dmPartner;
|
||||||
subtitle = _t(
|
subtitle = _t(
|
||||||
"Messages here are end-to-end encrypted. " +
|
"Messages here are end-to-end encrypted. " +
|
||||||
"Verify %(displayName)s in their profile - tap on their avatar.",
|
"Verify %(displayName)s in their profile - tap on their avatar.",
|
||||||
{ displayName },
|
{ displayName },
|
||||||
);
|
);
|
||||||
} else if (isLocalRoom(room)) {
|
} else if (room && isLocalRoom(room)) {
|
||||||
subtitle = _t("Messages in this chat will be end-to-end encrypted.");
|
subtitle = _t("Messages in this chat will be end-to-end encrypted.");
|
||||||
} else {
|
} else {
|
||||||
subtitle = _t(
|
subtitle = _t(
|
||||||
|
|
|
@ -183,7 +183,7 @@ const MBeaconBody: React.FC<IBodyProps> = React.forwardRef(({ mxEvent, getRelati
|
||||||
<SmartMarker
|
<SmartMarker
|
||||||
map={map}
|
map={map}
|
||||||
id={`${mapId}-marker`}
|
id={`${mapId}-marker`}
|
||||||
geoUri={latestLocationState.uri}
|
geoUri={latestLocationState?.uri}
|
||||||
roomMember={markerRoomMember ?? undefined}
|
roomMember={markerRoomMember ?? undefined}
|
||||||
useMemberColor
|
useMemberColor
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -47,10 +47,10 @@ interface IProps {
|
||||||
room: Room;
|
room: Room;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
resizeNotifier: ResizeNotifier;
|
resizeNotifier: ResizeNotifier;
|
||||||
permalinkCreator?: RoomPermalinkCreator;
|
permalinkCreator: RoomPermalinkCreator;
|
||||||
e2eStatus?: E2EStatus;
|
e2eStatus?: E2EStatus;
|
||||||
classNames?: string;
|
classNames?: string;
|
||||||
timelineSet?: EventTimelineSet;
|
timelineSet: EventTimelineSet;
|
||||||
timelineRenderingType?: TimelineRenderingType;
|
timelineRenderingType?: TimelineRenderingType;
|
||||||
showComposer?: boolean;
|
showComposer?: boolean;
|
||||||
composerRelation?: IEventRelation;
|
composerRelation?: IEventRelation;
|
||||||
|
@ -77,7 +77,7 @@ export default class TimelineCard extends React.Component<IProps, IState> {
|
||||||
private layoutWatcherRef: string;
|
private layoutWatcherRef: string;
|
||||||
private timelinePanel = React.createRef<TimelinePanel>();
|
private timelinePanel = React.createRef<TimelinePanel>();
|
||||||
private card = React.createRef<HTMLDivElement>();
|
private card = React.createRef<HTMLDivElement>();
|
||||||
private readReceiptsSettingWatcher: string;
|
private readReceiptsSettingWatcher: string | undefined;
|
||||||
|
|
||||||
public constructor(props: IProps) {
|
public constructor(props: IProps) {
|
||||||
super(props);
|
super(props);
|
||||||
|
@ -87,7 +87,6 @@ export default class TimelineCard extends React.Component<IProps, IState> {
|
||||||
atEndOfLiveTimeline: true,
|
atEndOfLiveTimeline: true,
|
||||||
narrow: false,
|
narrow: false,
|
||||||
};
|
};
|
||||||
this.readReceiptsSettingWatcher = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount(): void {
|
public componentDidMount(): void {
|
||||||
|
@ -129,7 +128,7 @@ export default class TimelineCard extends React.Component<IProps, IState> {
|
||||||
case Action.EditEvent:
|
case Action.EditEvent:
|
||||||
this.setState(
|
this.setState(
|
||||||
{
|
{
|
||||||
editState: payload.event ? new EditorStateTransfer(payload.event) : null,
|
editState: payload.event ? new EditorStateTransfer(payload.event) : undefined,
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
if (payload.event) {
|
if (payload.event) {
|
||||||
|
@ -199,7 +198,7 @@ export default class TimelineCard extends React.Component<IProps, IState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
const highlightedEventId = this.state.isInitialEventHighlighted ? this.state.initialEventId : null;
|
const highlightedEventId = this.state.isInitialEventHighlighted ? this.state.initialEventId : undefined;
|
||||||
|
|
||||||
let jumpToBottom;
|
let jumpToBottom;
|
||||||
if (!this.state.atEndOfLiveTimeline) {
|
if (!this.state.atEndOfLiveTimeline) {
|
||||||
|
@ -232,7 +231,7 @@ export default class TimelineCard extends React.Component<IProps, IState> {
|
||||||
header={this.renderTimelineCardHeader()}
|
header={this.renderTimelineCardHeader()}
|
||||||
ref={this.card}
|
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">
|
<div className="mx_TimelineCard_timeline">
|
||||||
{jumpToBottom}
|
{jumpToBottom}
|
||||||
<TimelinePanel
|
<TimelinePanel
|
||||||
|
|
|
@ -69,8 +69,9 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
|
||||||
const showQR: boolean = request.otherPartySupportsMethod(SCAN_QR_CODE_METHOD);
|
const showQR: boolean = request.otherPartySupportsMethod(SCAN_QR_CODE_METHOD);
|
||||||
const brand = SdkConfig.get().brand;
|
const brand = SdkConfig.get().brand;
|
||||||
|
|
||||||
const noCommonMethodError: JSX.Element =
|
let noCommonMethodError: JSX.Element | undefined;
|
||||||
!showSAS && !showQR ? (
|
if (!showSAS && !showQR) {
|
||||||
|
noCommonMethodError = (
|
||||||
<p>
|
<p>
|
||||||
{_t(
|
{_t(
|
||||||
"The device you are trying to verify doesn't support scanning a " +
|
"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 },
|
{ brand },
|
||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
) : null;
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (this.props.layout === "dialog") {
|
if (this.props.layout === "dialog") {
|
||||||
// HACK: This is a terrible idea.
|
// HACK: This is a terrible idea.
|
||||||
let qrBlockDialog: JSX.Element;
|
let qrBlockDialog: JSX.Element | undefined;
|
||||||
let sasBlockDialog: JSX.Element;
|
let sasBlockDialog: JSX.Element | undefined;
|
||||||
if (showQR) {
|
if (showQR) {
|
||||||
qrBlockDialog = (
|
qrBlockDialog = (
|
||||||
<div className="mx_VerificationPanel_QRPhase_startOption">
|
<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) {
|
if (showQR) {
|
||||||
qrBlock = (
|
qrBlock = (
|
||||||
<div className="mx_UserInfo_container">
|
<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) {
|
if (showSAS) {
|
||||||
const disabled = this.state.emojiButtonClicked;
|
const disabled = this.state.emojiButtonClicked;
|
||||||
const sasLabel = showQR
|
const sasLabel = showQR
|
||||||
|
@ -189,18 +191,20 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
|
||||||
}
|
}
|
||||||
|
|
||||||
private onReciprocateYesClick = (): void => {
|
private onReciprocateYesClick = (): void => {
|
||||||
|
if (!this.state.reciprocateQREvent) return;
|
||||||
this.setState({ reciprocateButtonClicked: true });
|
this.setState({ reciprocateButtonClicked: true });
|
||||||
this.state.reciprocateQREvent.confirm();
|
this.state.reciprocateQREvent.confirm();
|
||||||
};
|
};
|
||||||
|
|
||||||
private onReciprocateNoClick = (): void => {
|
private onReciprocateNoClick = (): void => {
|
||||||
|
if (!this.state.reciprocateQREvent) return;
|
||||||
this.setState({ reciprocateButtonClicked: true });
|
this.setState({ reciprocateButtonClicked: true });
|
||||||
this.state.reciprocateQREvent.cancel();
|
this.state.reciprocateQREvent.cancel();
|
||||||
};
|
};
|
||||||
|
|
||||||
private getDevice(): DeviceInfo {
|
private getDevice(): DeviceInfo | null {
|
||||||
const deviceId = this.props.request && this.props.request.channel.deviceId;
|
const cli = MatrixClientPeg.get();
|
||||||
return MatrixClientPeg.get().getStoredDevice(MatrixClientPeg.get().getUserId(), deviceId);
|
return cli.getStoredDevice(cli.getSafeUserId(), this.props.request?.channel.deviceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderQRReciprocatePhase(): JSX.Element {
|
private renderQRReciprocatePhase(): JSX.Element {
|
||||||
|
@ -253,7 +257,7 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
|
||||||
private renderVerifiedPhase(): JSX.Element {
|
private renderVerifiedPhase(): JSX.Element {
|
||||||
const { member, request } = this.props;
|
const { member, request } = this.props;
|
||||||
|
|
||||||
let text: string;
|
let text: string | undefined;
|
||||||
if (!request.isSelfVerification) {
|
if (!request.isSelfVerification) {
|
||||||
if (this.props.isRoomEncrypted) {
|
if (this.props.isRoomEncrypted) {
|
||||||
text = _t("Verify all users in a room to ensure it's secure.");
|
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 ? (
|
const emojis = this.state.sasEvent ? (
|
||||||
<VerificationShowSas
|
<VerificationShowSas
|
||||||
displayName={displayName}
|
displayName={displayName}
|
||||||
device={this.getDevice()}
|
device={this.getDevice() ?? undefined}
|
||||||
sas={this.state.sasEvent.sas}
|
sas={this.state.sasEvent.sas}
|
||||||
onCancel={this.onSasMismatchesClick}
|
onCancel={this.onSasMismatchesClick}
|
||||||
onDone={this.onSasMatchesClick}
|
onDone={this.onSasMatchesClick}
|
||||||
|
@ -383,11 +387,11 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
|
||||||
};
|
};
|
||||||
|
|
||||||
private onSasMatchesClick = (): void => {
|
private onSasMatchesClick = (): void => {
|
||||||
this.state.sasEvent.confirm();
|
this.state.sasEvent?.confirm();
|
||||||
};
|
};
|
||||||
|
|
||||||
private onSasMismatchesClick = (): void => {
|
private onSasMismatchesClick = (): void => {
|
||||||
this.state.sasEvent.mismatch();
|
this.state.sasEvent?.mismatch();
|
||||||
};
|
};
|
||||||
|
|
||||||
private updateVerifierState = (): void => {
|
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
|
// Don't render anything as we are about to transition
|
||||||
if (!app || !isRight) return null;
|
if (!app || !isRight) return null;
|
||||||
|
|
||||||
let contextMenu;
|
let contextMenu: JSX.Element | undefined;
|
||||||
if (menuDisplayed) {
|
if (menuDisplayed) {
|
||||||
const rect = handle.current.getBoundingClientRect();
|
const rect = handle.current!.getBoundingClientRect();
|
||||||
contextMenu = (
|
contextMenu = (
|
||||||
<WidgetContextMenu
|
<WidgetContextMenu
|
||||||
chevronFace={ChevronFace.None}
|
chevronFace={ChevronFace.None}
|
||||||
|
@ -92,7 +92,7 @@ const WidgetCard: React.FC<IProps> = ({ room, widgetId, onClose }) => {
|
||||||
fullWidth
|
fullWidth
|
||||||
showMenubar={false}
|
showMenubar={false}
|
||||||
room={room}
|
room={room}
|
||||||
userId={cli.getUserId()}
|
userId={cli.getSafeUserId()}
|
||||||
creatorUserId={app.creatorUserId}
|
creatorUserId={app.creatorUserId}
|
||||||
widgetPageTitle={WidgetUtils.getWidgetDataTitle(app)}
|
widgetPageTitle={WidgetUtils.getWidgetDataTitle(app)}
|
||||||
waitForIframeLoad={app.waitForIframeLoad}
|
waitForIframeLoad={app.waitForIframeLoad}
|
||||||
|
|
|
@ -252,10 +252,10 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isEmpty) {
|
if (isEmpty) {
|
||||||
this.formatBarRef.current.hide();
|
this.formatBarRef.current?.hide();
|
||||||
}
|
}
|
||||||
this.setState({
|
this.setState({
|
||||||
autoComplete: this.props.model.autoComplete,
|
autoComplete: this.props.model.autoComplete ?? undefined,
|
||||||
// if a change is happening then clear the showVisualBell
|
// if a change is happening then clear the showVisualBell
|
||||||
showVisualBell: diff ? false : this.state.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 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") {
|
if (isTyping && this.props.model.parts[0].type === "command") {
|
||||||
const { cmd } = parseCommandString(this.props.model.parts[0].text);
|
const { cmd } = parseCommandString(this.props.model.parts[0].text);
|
||||||
const command = CommandMap.get(cmd);
|
const command = CommandMap.get(cmd!);
|
||||||
if (!command || !command.isEnabled() || command.category !== CommandCategories.messages) {
|
if (!command?.isEnabled() || command.category !== CommandCategories.messages) {
|
||||||
isTyping = false;
|
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) {
|
if (this.props.onChange) {
|
||||||
this.props.onChange();
|
this.props.onChange();
|
||||||
|
@ -280,14 +284,14 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
|
||||||
|
|
||||||
private showPlaceholder(): void {
|
private showPlaceholder(): void {
|
||||||
// escape single quotes
|
// escape single quotes
|
||||||
const placeholder = this.props.placeholder.replace(/'/g, "\\'");
|
const placeholder = this.props.placeholder?.replace(/'/g, "\\'");
|
||||||
this.editorRef.current.style.setProperty("--placeholder", `'${placeholder}'`);
|
this.editorRef.current?.style.setProperty("--placeholder", `'${placeholder}'`);
|
||||||
this.editorRef.current.classList.add("mx_BasicMessageComposer_inputEmpty");
|
this.editorRef.current?.classList.add("mx_BasicMessageComposer_inputEmpty");
|
||||||
}
|
}
|
||||||
|
|
||||||
private hidePlaceholder(): void {
|
private hidePlaceholder(): void {
|
||||||
this.editorRef.current.classList.remove("mx_BasicMessageComposer_inputEmpty");
|
this.editorRef.current?.classList.remove("mx_BasicMessageComposer_inputEmpty");
|
||||||
this.editorRef.current.style.removeProperty("--placeholder");
|
this.editorRef.current?.style.removeProperty("--placeholder");
|
||||||
}
|
}
|
||||||
|
|
||||||
private onCompositionStart = (): void => {
|
private onCompositionStart = (): void => {
|
||||||
|
@ -327,9 +331,9 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
|
||||||
}
|
}
|
||||||
|
|
||||||
private onCutCopy = (event: ClipboardEvent, type: string): void => {
|
private onCutCopy = (event: ClipboardEvent, type: string): void => {
|
||||||
const selection = document.getSelection();
|
const selection = document.getSelection()!;
|
||||||
const text = selection.toString();
|
const text = selection.toString();
|
||||||
if (text) {
|
if (text && this.editorRef.current) {
|
||||||
const { model } = this.props;
|
const { model } = this.props;
|
||||||
const range = getRangeForSelection(this.editorRef.current, model, selection);
|
const range = getRangeForSelection(this.editorRef.current, model, selection);
|
||||||
const selectedParts = range.parts.map((p) => p.serialize());
|
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;
|
const { model } = this.props;
|
||||||
this._isCaretAtEnd = position.isAtEnd(model);
|
this._isCaretAtEnd = position.isAtEnd(model);
|
||||||
this.lastCaret = position.asOffset(model);
|
this.lastCaret = position.asOffset(model);
|
||||||
this.lastSelection = cloneSelection(document.getSelection());
|
this.lastSelection = cloneSelection(document.getSelection()!);
|
||||||
}
|
}
|
||||||
|
|
||||||
private refreshLastCaretIfNeeded(): DocumentOffset | undefined {
|
private refreshLastCaretIfNeeded(): DocumentOffset | undefined {
|
||||||
|
@ -422,7 +426,7 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
|
||||||
if (!this.editorRef.current) {
|
if (!this.editorRef.current) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const selection = document.getSelection();
|
const selection = document.getSelection()!;
|
||||||
if (!this.lastSelection || !selectionEquals(this.lastSelection, selection)) {
|
if (!this.lastSelection || !selectionEquals(this.lastSelection, selection)) {
|
||||||
this.lastSelection = cloneSelection(selection);
|
this.lastSelection = cloneSelection(selection);
|
||||||
const { caret, text } = getCaretOffsetAndText(this.editorRef.current, 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;
|
const { isEmpty } = this.props.model;
|
||||||
|
|
||||||
this.refreshLastCaretIfNeeded();
|
this.refreshLastCaretIfNeeded();
|
||||||
const selection = document.getSelection();
|
const selection = document.getSelection()!;
|
||||||
if (this.hasTextSelected && selection.isCollapsed) {
|
if (this.hasTextSelected && selection.isCollapsed) {
|
||||||
this.hasTextSelected = false;
|
this.hasTextSelected = false;
|
||||||
this.formatBarRef.current?.hide();
|
this.formatBarRef.current?.hide();
|
||||||
|
@ -485,7 +489,7 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
|
||||||
const model = this.props.model;
|
const model = this.props.model;
|
||||||
let handled = false;
|
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
|
// This surrounds the selected text with a character. This is
|
||||||
// intentionally left out of the keybinding manager as the keybinds
|
// intentionally left out of the keybinding manager as the keybinds
|
||||||
// here shouldn't be changeable
|
// here shouldn't be changeable
|
||||||
|
@ -538,7 +542,7 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
|
||||||
this.tabCompleteName();
|
this.tabCompleteName();
|
||||||
handled = true;
|
handled = true;
|
||||||
} else if ([KeyBindingAction.Delete, KeyBindingAction.Backspace].includes(accessibilityAction!)) {
|
} else if ([KeyBindingAction.Delete, KeyBindingAction.Backspace].includes(accessibilityAction!)) {
|
||||||
this.formatBarRef.current.hide();
|
this.formatBarRef.current?.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (handled) {
|
if (handled) {
|
||||||
|
@ -654,7 +658,7 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
|
||||||
|
|
||||||
private onAutoCompleteConfirm = (completion: ICompletion): void => {
|
private onAutoCompleteConfirm = (completion: ICompletion): void => {
|
||||||
this.modifiedFlag = true;
|
this.modifiedFlag = true;
|
||||||
this.props.model.autoComplete.onComponentConfirm(completion);
|
this.props.model.autoComplete?.onComponentConfirm(completion);
|
||||||
};
|
};
|
||||||
|
|
||||||
private onAutoCompleteSelectionChange = (completionIndex: number): void => {
|
private onAutoCompleteSelectionChange = (completionIndex: number): void => {
|
||||||
|
@ -691,9 +695,9 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
|
||||||
|
|
||||||
public componentWillUnmount(): void {
|
public componentWillUnmount(): void {
|
||||||
document.removeEventListener("selectionchange", this.onSelectionChange);
|
document.removeEventListener("selectionchange", this.onSelectionChange);
|
||||||
this.editorRef.current.removeEventListener("input", this.onInput, true);
|
this.editorRef.current?.removeEventListener("input", this.onInput, true);
|
||||||
this.editorRef.current.removeEventListener("compositionstart", this.onCompositionStart, true);
|
this.editorRef.current?.removeEventListener("compositionstart", this.onCompositionStart, true);
|
||||||
this.editorRef.current.removeEventListener("compositionend", this.onCompositionEnd, true);
|
this.editorRef.current?.removeEventListener("compositionend", this.onCompositionEnd, true);
|
||||||
SettingsStore.unwatchSetting(this.useMarkdownHandle);
|
SettingsStore.unwatchSetting(this.useMarkdownHandle);
|
||||||
SettingsStore.unwatchSetting(this.emoticonSettingHandle);
|
SettingsStore.unwatchSetting(this.emoticonSettingHandle);
|
||||||
SettingsStore.unwatchSetting(this.shouldShowPillAvatarSettingHandle);
|
SettingsStore.unwatchSetting(this.shouldShowPillAvatarSettingHandle);
|
||||||
|
@ -716,10 +720,10 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
|
||||||
this.updateEditorState(this.getInitialCaretPosition());
|
this.updateEditorState(this.getInitialCaretPosition());
|
||||||
// attach input listener by hand so React doesn't proxy the events,
|
// attach input listener by hand so React doesn't proxy the events,
|
||||||
// as the proxied event doesn't support inputType, which we need.
|
// as the proxied event doesn't support inputType, which we need.
|
||||||
this.editorRef.current.addEventListener("input", this.onInput, true);
|
this.editorRef.current?.addEventListener("input", this.onInput, true);
|
||||||
this.editorRef.current.addEventListener("compositionstart", this.onCompositionStart, true);
|
this.editorRef.current?.addEventListener("compositionstart", this.onCompositionStart, true);
|
||||||
this.editorRef.current.addEventListener("compositionend", this.onCompositionEnd, true);
|
this.editorRef.current?.addEventListener("compositionend", this.onCompositionEnd, true);
|
||||||
this.editorRef.current.focus();
|
this.editorRef.current?.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
private getInitialCaretPosition(): DocumentPosition {
|
private getInitialCaretPosition(): DocumentPosition {
|
||||||
|
@ -826,7 +830,7 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
|
||||||
}
|
}
|
||||||
|
|
||||||
public focus(): void {
|
public focus(): void {
|
||||||
this.editorRef.current.focus();
|
this.editorRef.current?.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
public insertMention(userId: string): void {
|
public insertMention(userId: string): void {
|
||||||
|
|
|
@ -149,7 +149,7 @@ class EditMessageComposer extends React.Component<IEditMessageComposerProps, ISt
|
||||||
this.dispatcherRef = dis.register(this.onAction);
|
this.dispatcherRef = dis.register(this.onAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
private getRoom(): Room {
|
private getRoom(): Room | null {
|
||||||
return this.props.mxClient.getRoom(this.props.editState.getEvent().getRoomId());
|
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[] {
|
private get events(): MatrixEvent[] {
|
||||||
const liveTimelineEvents = this.context.liveTimeline.getEvents();
|
const liveTimelineEvents = this.context.liveTimeline?.getEvents();
|
||||||
const pendingEvents = this.getRoom().getPendingEvents();
|
const pendingEvents = this.getRoom()?.getPendingEvents();
|
||||||
const isInThread = Boolean(this.props.editState.getEvent().getThread());
|
const isInThread = Boolean(this.props.editState.getEvent().getThread());
|
||||||
return liveTimelineEvents.concat(isInThread ? [] : pendingEvents);
|
return liveTimelineEvents?.concat(isInThread ? [] : pendingEvents) ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
private cancelEdit = (): void => {
|
private cancelEdit = (): void => {
|
||||||
|
@ -304,10 +304,10 @@ class EditMessageComposer extends React.Component<IEditMessageComposerProps, ISt
|
||||||
});
|
});
|
||||||
|
|
||||||
// Replace emoticon at the end of the message
|
// Replace emoticon at the end of the message
|
||||||
if (SettingsStore.getValue("MessageComposerInput.autoReplaceEmoji")) {
|
if (SettingsStore.getValue("MessageComposerInput.autoReplaceEmoji") && this.editorRef.current) {
|
||||||
const caret = this.editorRef.current?.getCaret();
|
const caret = this.editorRef.current.getCaret();
|
||||||
const position = this.model.positionForOffset(caret.offset, caret.atNodeEnd);
|
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 editContent = createEditContent(this.model, editedEvent);
|
||||||
const newContent = editContent["m.new_content"];
|
const newContent = editContent["m.new_content"];
|
||||||
|
@ -382,7 +382,7 @@ class EditMessageComposer extends React.Component<IEditMessageComposerProps, ISt
|
||||||
// store caret and serialized parts in the
|
// store caret and serialized parts in the
|
||||||
// editorstate so it can be restored when the remote echo event tile gets rendered
|
// editorstate so it can be restored when the remote echo event tile gets rendered
|
||||||
// in case we're currently editing a pending event
|
// in case we're currently editing a pending event
|
||||||
const sel = document.getSelection();
|
const sel = document.getSelection()!;
|
||||||
let caret: DocumentOffset | undefined;
|
let caret: DocumentOffset | undefined;
|
||||||
if (sel.focusNode) {
|
if (sel.focusNode) {
|
||||||
caret = getCaretOffsetAndText(this.editorRef.current?.editorRef.current, sel).caret;
|
caret = getCaretOffsetAndText(this.editorRef.current?.editorRef.current, sel).caret;
|
||||||
|
|
|
@ -238,7 +238,7 @@ interface IState {
|
||||||
|
|
||||||
isQuoteExpanded?: boolean;
|
isQuoteExpanded?: boolean;
|
||||||
|
|
||||||
thread: Thread;
|
thread: Thread | null;
|
||||||
threadNotification?: NotificationCountType;
|
threadNotification?: NotificationCountType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -438,12 +438,12 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
|
||||||
if (thread.id === this.props.mxEvent.getId()) {
|
if (thread.id === this.props.mxEvent.getId()) {
|
||||||
this.updateThread(thread);
|
this.updateThread(thread);
|
||||||
const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId());
|
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 {
|
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
|
* Accessing the threads value through the room due to a race condition
|
||||||
* that will be solved when there are proper backend support for threads
|
* 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) {
|
if (!thread) {
|
||||||
const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId());
|
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;
|
return thread ?? null;
|
||||||
}
|
}
|
||||||
|
@ -471,7 +471,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderThreadInfo(): React.ReactNode {
|
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 (
|
return (
|
||||||
<ThreadSummary mxEvent={this.props.mxEvent} thread={this.state.thread} data-testid="thread-summary" />
|
<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.preventDefault();
|
||||||
evt.stopPropagation();
|
evt.stopPropagation();
|
||||||
const { permalinkCreator, mxEvent } = this.props;
|
const { permalinkCreator, mxEvent } = this.props;
|
||||||
const matrixToUrl = permalinkCreator.forEvent(mxEvent.getId());
|
const matrixToUrl = permalinkCreator.forEvent(mxEvent.getId()!);
|
||||||
await copyPlaintext(matrixToUrl);
|
await copyPlaintext(matrixToUrl);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1104,7 +1104,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
|
||||||
const useIRCLayout = this.props.layout === Layout.IRC;
|
const useIRCLayout = this.props.layout === Layout.IRC;
|
||||||
const groupTimestamp = !useIRCLayout ? linkedTimestamp : null;
|
const groupTimestamp = !useIRCLayout ? linkedTimestamp : null;
|
||||||
const ircTimestamp = 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 groupPadlock = !useIRCLayout && !isBubbleMessage && this.renderE2EPadlock();
|
||||||
const ircPadlock = useIRCLayout && !isBubbleMessage && this.renderE2EPadlock();
|
const ircPadlock = useIRCLayout && !isBubbleMessage && this.renderE2EPadlock();
|
||||||
|
|
||||||
|
@ -1493,7 +1493,7 @@ class E2ePadlock extends React.Component<IE2ePadlockProps, IE2ePadlockState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ISentReceiptProps {
|
interface ISentReceiptProps {
|
||||||
messageState: string; // TODO: Types for message sending state
|
messageState: EventStatus | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function SentReceipt({ messageState }: ISentReceiptProps): JSX.Element {
|
function SentReceipt({ messageState }: ISentReceiptProps): JSX.Element {
|
||||||
|
|
|
@ -164,7 +164,7 @@ const NewRoomIntro: React.FC = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const creator = room.currentState.getStateEvents(EventType.RoomCreate, "")?.getSender();
|
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;
|
let createdText: string;
|
||||||
if (creator === cli.getUserId()) {
|
if (creator === cli.getUserId()) {
|
||||||
|
@ -178,7 +178,7 @@ const NewRoomIntro: React.FC = () => {
|
||||||
let parentSpace: Room | undefined;
|
let parentSpace: Room | undefined;
|
||||||
if (
|
if (
|
||||||
SpaceStore.instance.activeSpaceRoom?.canInvite(cli.getSafeUserId()) &&
|
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;
|
parentSpace = SpaceStore.instance.activeSpaceRoom;
|
||||||
}
|
}
|
||||||
|
|
|
@ -231,7 +231,7 @@ const RoomListHeader: React.FC<IProps> = ({ onVisibilityChange }) => {
|
||||||
|
|
||||||
contextMenu = (
|
contextMenu = (
|
||||||
<IconizedContextMenu
|
<IconizedContextMenu
|
||||||
{...contextMenuBelow(plusMenuHandle.current.getBoundingClientRect())}
|
{...contextMenuBelow(plusMenuHandle.current!.getBoundingClientRect())}
|
||||||
onFinished={closePlusMenu}
|
onFinished={closePlusMenu}
|
||||||
compact
|
compact
|
||||||
>
|
>
|
||||||
|
@ -357,7 +357,7 @@ const RoomListHeader: React.FC<IProps> = ({ onVisibilityChange }) => {
|
||||||
|
|
||||||
contextMenu = (
|
contextMenu = (
|
||||||
<IconizedContextMenu
|
<IconizedContextMenu
|
||||||
{...contextMenuBelow(plusMenuHandle.current.getBoundingClientRect())}
|
{...contextMenuBelow(plusMenuHandle.current!.getBoundingClientRect())}
|
||||||
onFinished={closePlusMenu}
|
onFinished={closePlusMenu}
|
||||||
compact
|
compact
|
||||||
>
|
>
|
||||||
|
|
|
@ -227,12 +227,14 @@ export class SendMessageComposer extends React.Component<ISendMessageComposerPro
|
||||||
// selection must be collapsed and caret at start
|
// selection must be collapsed and caret at start
|
||||||
if (this.editorRef.current?.isSelectionCollapsed() && this.editorRef.current?.isCaretAtStart()) {
|
if (this.editorRef.current?.isSelectionCollapsed() && this.editorRef.current?.isCaretAtStart()) {
|
||||||
const events = this.context.liveTimeline
|
const events = this.context.liveTimeline
|
||||||
.getEvents()
|
?.getEvents()
|
||||||
.concat(replyingToThread ? [] : this.props.room.getPendingEvents());
|
.concat(replyingToThread ? [] : this.props.room.getPendingEvents());
|
||||||
const editEvent = findEditableEvent({
|
const editEvent = events
|
||||||
|
? findEditableEvent({
|
||||||
events,
|
events,
|
||||||
isForward: false,
|
isForward: false,
|
||||||
});
|
})
|
||||||
|
: undefined;
|
||||||
if (editEvent) {
|
if (editEvent) {
|
||||||
// We're selecting history, so prevent the key event from doing anything else
|
// We're selecting history, so prevent the key event from doing anything else
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
@ -297,7 +299,7 @@ export class SendMessageComposer extends React.Component<ISendMessageComposerPro
|
||||||
if (events[i].getType() === EventType.RoomMessage) {
|
if (events[i].getType() === EventType.RoomMessage) {
|
||||||
let shouldReact = true;
|
let shouldReact = true;
|
||||||
const lastMessage = events[i];
|
const lastMessage = events[i];
|
||||||
const userId = MatrixClientPeg.get().getUserId();
|
const userId = MatrixClientPeg.get().getSafeUserId();
|
||||||
const messageReactions = this.props.room.relations.getChildEventsForEvent(
|
const messageReactions = this.props.room.relations.getChildEventsForEvent(
|
||||||
lastMessage.getId()!,
|
lastMessage.getId()!,
|
||||||
RelationType.Annotation,
|
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 we have already sent this reaction, don't redact but don't re-send
|
||||||
if (messageReactions) {
|
if (messageReactions) {
|
||||||
const myReactionEvents =
|
const myReactionEvents =
|
||||||
messageReactions.getAnnotationsBySender()[userId] || new Set<MatrixEvent>();
|
messageReactions.getAnnotationsBySender()?.[userId] || new Set<MatrixEvent>();
|
||||||
const myReactionKeys = [...myReactionEvents]
|
const myReactionKeys = [...myReactionEvents]
|
||||||
.filter((event) => !event.isRedacted())
|
.filter((event) => !event.isRedacted())
|
||||||
.map((event) => event.getRelation().key);
|
.map((event) => event.getRelation()?.key);
|
||||||
shouldReact = !myReactionKeys.includes(reaction);
|
shouldReact = !myReactionKeys.includes(reaction);
|
||||||
}
|
}
|
||||||
if (shouldReact) {
|
if (shouldReact) {
|
||||||
|
|
|
@ -46,7 +46,7 @@ interface IKeyboardShortcutProps {
|
||||||
export const KeyboardShortcut: React.FC<IKeyboardShortcutProps> = ({ value, className = "mx_KeyboardShortcut" }) => {
|
export const KeyboardShortcut: React.FC<IKeyboardShortcutProps> = ({ value, className = "mx_KeyboardShortcut" }) => {
|
||||||
if (!value) return null;
|
if (!value) return null;
|
||||||
|
|
||||||
const modifiersElement = [];
|
const modifiersElement: JSX.Element[] = [];
|
||||||
if (value.ctrlOrCmdKey) {
|
if (value.ctrlOrCmdKey) {
|
||||||
modifiersElement.push(<KeyboardKey key="ctrlOrCmdKey" name={IS_MAC ? Key.META : Key.CONTROL} />);
|
modifiersElement.push(<KeyboardKey key="ctrlOrCmdKey" name={IS_MAC ? Key.META : Key.CONTROL} />);
|
||||||
} else if (value.ctrlKey) {
|
} else if (value.ctrlKey) {
|
||||||
|
|
|
@ -41,7 +41,7 @@ import SdkConfig from "../../../SdkConfig";
|
||||||
import AccessibleButton from "../elements/AccessibleButton";
|
import AccessibleButton from "../elements/AccessibleButton";
|
||||||
import TagComposer from "../elements/TagComposer";
|
import TagComposer from "../elements/TagComposer";
|
||||||
import { objectClone } from "../../../utils/objects";
|
import { objectClone } from "../../../utils/objects";
|
||||||
import { arrayDiff } from "../../../utils/arrays";
|
import { arrayDiff, filterBoolean } from "../../../utils/arrays";
|
||||||
import { clearAllNotifications, getLocalNotificationAccountDataEventType } from "../../../utils/notifications";
|
import { clearAllNotifications, getLocalNotificationAccountDataEventType } from "../../../utils/notifications";
|
||||||
import {
|
import {
|
||||||
updateExistingPushRulesWithActions,
|
updateExistingPushRulesWithActions,
|
||||||
|
@ -167,7 +167,7 @@ const maximumVectorState = (
|
||||||
if (!definition.syncedRuleIds?.length) {
|
if (!definition.syncedRuleIds?.length) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
const vectorState = definition.syncedRuleIds.reduce<VectorState>((maxVectorState, ruleId) => {
|
const vectorState = definition.syncedRuleIds.reduce<VectorState | undefined>((maxVectorState, ruleId) => {
|
||||||
// already set to maximum
|
// already set to maximum
|
||||||
if (maxVectorState === VectorState.Loud) {
|
if (maxVectorState === VectorState.Loud) {
|
||||||
return maxVectorState;
|
return maxVectorState;
|
||||||
|
@ -345,7 +345,7 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
|
||||||
for (const rule of defaultRules[category]) {
|
for (const rule of defaultRules[category]) {
|
||||||
const definition: VectorPushRuleDefinition = VectorPushRulesDefinitions[rule.rule_id];
|
const definition: VectorPushRuleDefinition = VectorPushRulesDefinitions[rule.rule_id];
|
||||||
const vectorState = definition.ruleToVectorState(rule);
|
const vectorState = definition.ruleToVectorState(rule);
|
||||||
preparedNewState.vectorPushRules[category].push({
|
preparedNewState.vectorPushRules[category]!.push({
|
||||||
ruleId: rule.rule_id,
|
ruleId: rule.rule_id,
|
||||||
rule,
|
rule,
|
||||||
vectorState,
|
vectorState,
|
||||||
|
@ -355,7 +355,7 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Quickly sort the rules for display purposes
|
// 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 idxA = RULE_DISPLAY_ORDER.indexOf(a.ruleId);
|
||||||
let idxB = RULE_DISPLAY_ORDER.indexOf(b.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) {
|
if (category === KEYWORD_RULE_CATEGORY) {
|
||||||
preparedNewState.vectorPushRules[category].push({
|
preparedNewState.vectorPushRules[category]!.push({
|
||||||
ruleId: KEYWORD_RULE_ID,
|
ruleId: KEYWORD_RULE_ID,
|
||||||
description: _t("Messages containing keywords"),
|
description: _t("Messages containing keywords"),
|
||||||
vectorState: preparedNewState.vectorKeywordRuleInfo.vectorState,
|
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> {
|
private async setKeywords(keywords: string[], originalRules: IAnnotatedPushRule[]): Promise<void> {
|
||||||
try {
|
try {
|
||||||
// De-duplicate and remove empties
|
// De-duplicate and remove empties
|
||||||
keywords = Array.from(new Set(keywords)).filter((k) => !!k);
|
keywords = filterBoolean(Array.from(new Set(keywords)));
|
||||||
const oldKeywords = Array.from(new Set(originalRules.map((r) => r.pattern))).filter((k) => !!k);
|
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
|
// 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
|
// 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) {
|
if (ruleVectorState === VectorState.Off) {
|
||||||
// When the current global keywords rule is OFF, we need to look at
|
// When the current global keywords rule is OFF, we need to look at
|
||||||
// the flavor of existing rules to apply the same actions
|
// the flavor of existing rules to apply the same actions
|
||||||
// when creating the new rule.
|
// when creating the new rule.
|
||||||
if (originalRules.length) {
|
if (originalRules.length) {
|
||||||
ruleVectorState = PushRuleVectorState.contentRuleVectorStateKind(originalRules[0]);
|
ruleVectorState = PushRuleVectorState.contentRuleVectorStateKind(originalRules[0]) ?? undefined;
|
||||||
} else {
|
} else {
|
||||||
ruleVectorState = VectorState.On; // default
|
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
|
<fieldset
|
||||||
key={category + r.ruleId}
|
key={category + r.ruleId}
|
||||||
data-testid={category + r.ruleId}
|
data-testid={category + r.ruleId}
|
||||||
|
@ -840,7 +840,7 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
|
||||||
</tr>
|
</tr>
|
||||||
));
|
));
|
||||||
|
|
||||||
if (!rows.length) return null; // no targets to show
|
if (!rows?.length) return null; // no targets to show
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx_UserNotifSettings_floatingSection">
|
<div className="mx_UserNotifSettings_floatingSection">
|
||||||
|
|
|
@ -72,7 +72,7 @@ interface IProps {
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
defaultIdServer?: string;
|
defaultIdServer?: string;
|
||||||
currentClientIdServer: string;
|
currentClientIdServer?: string;
|
||||||
idServer?: string;
|
idServer?: string;
|
||||||
error?: string;
|
error?: string;
|
||||||
busy: boolean;
|
busy: boolean;
|
||||||
|
|
|
@ -402,14 +402,14 @@ export default class RolesRoomSettingsTab extends React.Component<IProps> {
|
||||||
{banned.map((member) => {
|
{banned.map((member) => {
|
||||||
const banEvent = member.events.member?.getContent();
|
const banEvent = member.events.member?.getContent();
|
||||||
const sender = room?.getMember(member.events.member.getSender());
|
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;
|
if (sender) bannedBy = sender.name;
|
||||||
return (
|
return (
|
||||||
<BannedUser
|
<BannedUser
|
||||||
key={member.userId}
|
key={member.userId}
|
||||||
canUnban={canBanUsers}
|
canUnban={canBanUsers}
|
||||||
member={member}
|
member={member}
|
||||||
reason={banEvent.reason}
|
reason={banEvent?.reason}
|
||||||
by={bannedBy}
|
by={bannedBy}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -61,7 +61,7 @@ interface IProps {
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
language: string;
|
language: string;
|
||||||
spellCheckEnabled: boolean;
|
spellCheckEnabled?: boolean;
|
||||||
spellCheckLanguages: string[];
|
spellCheckLanguages: string[];
|
||||||
haveIdServer: boolean;
|
haveIdServer: boolean;
|
||||||
serverSupportsSeparateAddAndBind?: 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">
|
<div className="mx_SettingsTab_section mx_SettingsTab_section_spellcheck">
|
||||||
<span className="mx_SettingsTab_subheading">
|
<span className="mx_SettingsTab_subheading">
|
||||||
{_t("Spell check")}
|
{_t("Spell check")}
|
||||||
<ToggleSwitch checked={this.state.spellCheckEnabled} onChange={this.onSpellCheckEnabledChange} />
|
<ToggleSwitch checked={!!this.state.spellCheckEnabled} onChange={this.onSpellCheckEnabledChange} />
|
||||||
</span>
|
</span>
|
||||||
{this.state.spellCheckEnabled && !IS_MAC && (
|
{this.state.spellCheckEnabled && !IS_MAC && (
|
||||||
<SpellCheckSettings
|
<SpellCheckSettings
|
||||||
|
|
|
@ -56,7 +56,7 @@ const confirmSignOut = async (sessionsToSignOutCount: number): Promise<boolean>
|
||||||
});
|
});
|
||||||
const [confirmed] = await finished;
|
const [confirmed] = await finished;
|
||||||
|
|
||||||
return confirmed;
|
return !!confirmed;
|
||||||
};
|
};
|
||||||
|
|
||||||
const useSignOut = (
|
const useSignOut = (
|
||||||
|
|
|
@ -61,7 +61,7 @@ export default class VoiceUserSettingsTab extends React.Component<{}, IState> {
|
||||||
|
|
||||||
private refreshMediaDevices = async (stream?: MediaStream): Promise<void> => {
|
private refreshMediaDevices = async (stream?: MediaStream): Promise<void> => {
|
||||||
this.setState({
|
this.setState({
|
||||||
mediaDevices: await MediaDeviceHandler.getDevices(),
|
mediaDevices: (await MediaDeviceHandler.getDevices()) ?? null,
|
||||||
[MediaDeviceKindEnum.AudioOutput]: MediaDeviceHandler.getAudioOutput(),
|
[MediaDeviceKindEnum.AudioOutput]: MediaDeviceHandler.getAudioOutput(),
|
||||||
[MediaDeviceKindEnum.AudioInput]: MediaDeviceHandler.getAudioInput(),
|
[MediaDeviceKindEnum.AudioInput]: MediaDeviceHandler.getAudioInput(),
|
||||||
[MediaDeviceKindEnum.VideoInput]: MediaDeviceHandler.getVideoInput(),
|
[MediaDeviceKindEnum.VideoInput]: MediaDeviceHandler.getVideoInput(),
|
||||||
|
@ -105,8 +105,8 @@ export default class VoiceUserSettingsTab extends React.Component<{}, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderDropdown(kind: MediaDeviceKindEnum, label: string): ReactNode {
|
private renderDropdown(kind: MediaDeviceKindEnum, label: string): ReactNode {
|
||||||
const devices = this.state.mediaDevices[kind].slice(0);
|
const devices = this.state.mediaDevices?.[kind].slice(0);
|
||||||
if (devices.length === 0) return null;
|
if (!devices?.length) return null;
|
||||||
|
|
||||||
const defaultDevice = MediaDeviceHandler.getDefaultDevice(devices);
|
const defaultDevice = MediaDeviceHandler.getDefaultDevice(devices);
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -77,7 +77,7 @@ export default class VerificationRequestToast extends React.PureComponent<IProps
|
||||||
const device = await cli.getDevice(request.channel.deviceId);
|
const device = await cli.getDevice(request.channel.deviceId);
|
||||||
const ip = device.last_seen_ip;
|
const ip = device.last_seen_ip;
|
||||||
this.setState({
|
this.setState({
|
||||||
device: cli.getStoredDevice(cli.getUserId()!, request.channel.deviceId),
|
device: cli.getStoredDevice(cli.getUserId()!, request.channel.deviceId) ?? undefined,
|
||||||
ip,
|
ip,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -118,7 +118,7 @@ export default class VerificationRequestToast extends React.PureComponent<IProps
|
||||||
should_peek: false,
|
should_peek: false,
|
||||||
metricsTrigger: "VerificationRequest",
|
metricsTrigger: "VerificationRequest",
|
||||||
});
|
});
|
||||||
const member = cli.getUser(request.otherUserId);
|
const member = cli.getUser(request.otherUserId) ?? undefined;
|
||||||
RightPanelStore.instance.setCards(
|
RightPanelStore.instance.setCards(
|
||||||
[
|
[
|
||||||
{ phase: RightPanelPhases.RoomSummary },
|
{ phase: RightPanelPhases.RoomSummary },
|
||||||
|
|
|
@ -158,14 +158,14 @@ export const Lobby: FC<LobbyProps> = ({ room, joinCallButtonDisabledTooltip, con
|
||||||
// non-blank labels for the devices.
|
// non-blank labels for the devices.
|
||||||
let stream: MediaStream | null = null;
|
let stream: MediaStream | null = null;
|
||||||
try {
|
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
|
// Holding just an audio stream will be enough to get us all device labels, so
|
||||||
// if video is muted, don't bother requesting video.
|
// if video is muted, don't bother requesting video.
|
||||||
stream = await navigator.mediaDevices.getUserMedia({
|
stream = await navigator.mediaDevices.getUserMedia({
|
||||||
audio: true,
|
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.
|
// We have to resort to a video stream, even if video is supposed to be muted.
|
||||||
stream = await navigator.mediaDevices.getUserMedia({ video: { deviceId: videoInputId } });
|
stream = await navigator.mediaDevices.getUserMedia({ video: { deviceId: videoInputId } });
|
||||||
}
|
}
|
||||||
|
@ -182,7 +182,7 @@ export const Lobby: FC<LobbyProps> = ({ room, joinCallButtonDisabledTooltip, con
|
||||||
stream = null;
|
stream = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return [stream, devices.audioinput, devices.videoinput];
|
return [stream, devices?.audioinput ?? [], devices?.videoinput ?? []];
|
||||||
},
|
},
|
||||||
[videoInputId, videoMuted],
|
[videoInputId, videoMuted],
|
||||||
[null, [], []],
|
[null, [], []],
|
||||||
|
|
|
@ -22,7 +22,7 @@ import { Action } from "../actions";
|
||||||
export interface ViewRoomErrorPayload extends Pick<ActionPayload, "action"> {
|
export interface ViewRoomErrorPayload extends Pick<ActionPayload, "action"> {
|
||||||
action: Action.ViewRoomError;
|
action: Action.ViewRoomError;
|
||||||
// eslint-disable-next-line camelcase
|
// eslint-disable-next-line camelcase
|
||||||
room_id: Room["roomId"];
|
room_id: Room["roomId"] | null;
|
||||||
// eslint-disable-next-line camelcase
|
// eslint-disable-next-line camelcase
|
||||||
room_alias?: string;
|
room_alias?: string;
|
||||||
err?: MatrixError;
|
err?: MatrixError;
|
||||||
|
|
|
@ -47,7 +47,7 @@ export default class HistoryManager {
|
||||||
this.removedSinceLastPush = false;
|
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
|
// right now we can only push a step after
|
||||||
// the input has been applied to the model,
|
// the input has been applied to the model,
|
||||||
// so we can't push the state before something happened.
|
// 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
|
// 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.
|
// ignore state restoration echos.
|
||||||
// these respect the inputType values of the input event,
|
// these respect the inputType values of the input event,
|
||||||
// but are actually passed in from MessageEditor calling model.reset()
|
// but are actually passed in from MessageEditor calling model.reset()
|
||||||
|
|
|
@ -171,7 +171,7 @@ export default class EditorModel {
|
||||||
this._autoComplete = null;
|
this._autoComplete = null;
|
||||||
this.autoCompletePartIdx = 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(),
|
room_id: ev.getRoomId(),
|
||||||
session_id: sessionId,
|
session_id: sessionId,
|
||||||
device_id: wireContent.device_id,
|
device_id: wireContent.device_id,
|
||||||
user_id: ev.getSender(),
|
user_id: ev.getSender()!,
|
||||||
sender_key: wireContent.sender_key,
|
sender_key: wireContent.sender_key,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -62,14 +62,14 @@ export class ModalWidgetStore extends AsyncStoreWithClient<IState> {
|
||||||
): void => {
|
): void => {
|
||||||
if (this.modalInstance) return;
|
if (this.modalInstance) return;
|
||||||
this.openSourceWidgetId = sourceWidget.id;
|
this.openSourceWidgetId = sourceWidget.id;
|
||||||
this.openSourceWidgetRoomId = widgetRoomId;
|
this.openSourceWidgetRoomId = widgetRoomId ?? null;
|
||||||
this.modalInstance = Modal.createDialog(
|
this.modalInstance = Modal.createDialog(
|
||||||
ModalWidgetDialog,
|
ModalWidgetDialog,
|
||||||
{
|
{
|
||||||
widgetDefinition: { ...requestData },
|
widgetDefinition: { ...requestData },
|
||||||
widgetRoomId,
|
widgetRoomId,
|
||||||
sourceWidgetId: sourceWidget.id,
|
sourceWidgetId: sourceWidget.id,
|
||||||
onFinished: (success: boolean, data?: IModalWidgetReturnData) => {
|
onFinished: (success, data) => {
|
||||||
if (!success) {
|
if (!success) {
|
||||||
this.closeModalWidget(sourceWidget, widgetRoomId, { "m.exited": true });
|
this.closeModalWidget(sourceWidget, widgetRoomId, { "m.exited": true });
|
||||||
} else {
|
} else {
|
||||||
|
@ -81,13 +81,17 @@ export class ModalWidgetStore extends AsyncStoreWithClient<IState> {
|
||||||
this.modalInstance = null;
|
this.modalInstance = null;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
null,
|
undefined,
|
||||||
/* priority = */ false,
|
/* priority = */ false,
|
||||||
/* static = */ true,
|
/* 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.modalInstance) return;
|
||||||
if (this.openSourceWidgetId === sourceWidget.id && this.openSourceWidgetRoomId === widgetRoomId) {
|
if (this.openSourceWidgetId === sourceWidget.id && this.openSourceWidgetRoomId === widgetRoomId) {
|
||||||
this.openSourceWidgetId = null;
|
this.openSourceWidgetId = null;
|
||||||
|
|
|
@ -50,4 +50,4 @@ export class RoomScrollStateStore {
|
||||||
if (window.mxRoomScrollStateStore === undefined) {
|
if (window.mxRoomScrollStateStore === undefined) {
|
||||||
window.mxRoomScrollStateStore = new RoomScrollStateStore();
|
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,
|
roomId: payload.room_id,
|
||||||
initialEventId: null,
|
initialEventId: null,
|
||||||
initialEventPixelOffset: null,
|
initialEventPixelOffset: null,
|
||||||
isInitialEventHighlighted: null,
|
|
||||||
initialEventScrollIntoView: true,
|
initialEventScrollIntoView: true,
|
||||||
roomAlias: null,
|
roomAlias: null,
|
||||||
roomLoading: true,
|
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 cli = MatrixClientPeg.get();
|
||||||
const room = cli.getRoom(roomId);
|
const room = cli.getRoom(roomId);
|
||||||
if (room?.getMyMembership() === "invite") {
|
if (room?.getMyMembership() === "invite") {
|
||||||
const myMember = room.getMember(cli.getUserId());
|
const myMember = room.getMember(cli.getSafeUserId());
|
||||||
const inviteEvent = myMember ? myMember.events.member : null;
|
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)
|
// (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) {
|
switch (card.phase) {
|
||||||
case RightPanelPhases.ThreadView:
|
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");
|
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.RoomMemberInfo:
|
||||||
case RightPanelPhases.SpaceMemberInfo:
|
case RightPanelPhases.SpaceMemberInfo:
|
||||||
case RightPanelPhases.EncryptionPanel:
|
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");
|
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.Room3pidMemberInfo:
|
||||||
case RightPanelPhases.Space3pidMemberInfo:
|
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");
|
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:
|
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");
|
logger.warn("removed card from right panel because of missing widgetId in card state");
|
||||||
}
|
}
|
||||||
return !!card.state.widgetId;
|
return !!card.state?.widgetId;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -168,7 +168,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
||||||
return this.rootSpaces;
|
return this.rootSpaces;
|
||||||
}
|
}
|
||||||
|
|
||||||
public get activeSpace(): SpaceKey | undefined {
|
public get activeSpace(): SpaceKey {
|
||||||
return this._activeSpace;
|
return this._activeSpace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1018,7 +1018,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
||||||
private onRoomStateMembers = (ev: MatrixEvent): void => {
|
private onRoomStateMembers = (ev: MatrixEvent): void => {
|
||||||
const room = this.matrixClient.getRoom(ev.getRoomId());
|
const room = this.matrixClient.getRoom(ev.getRoomId());
|
||||||
|
|
||||||
const userId = ev.getStateKey();
|
const userId = ev.getStateKey()!;
|
||||||
if (
|
if (
|
||||||
room?.isSpaceRoom() && // only consider space rooms
|
room?.isSpaceRoom() && // only consider space rooms
|
||||||
DMRoomMap.shared().getDMRoomsForUserId(userId).length > 0 && // only consider members we have a DM with
|
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 {
|
private onRoomFavouriteChange(room: Room): void {
|
||||||
if (this.enabledMetaSpaces.includes(MetaSpace.Favourites)) {
|
if (this.enabledMetaSpaces.includes(MetaSpace.Favourites)) {
|
||||||
if (room.tags[DefaultTagID.Favourite]) {
|
if (room.tags[DefaultTagID.Favourite]) {
|
||||||
this.roomIdsBySpace.get(MetaSpace.Favourites).add(room.roomId);
|
this.roomIdsBySpace.get(MetaSpace.Favourites)?.add(room.roomId);
|
||||||
} else {
|
} else {
|
||||||
this.roomIdsBySpace.get(MetaSpace.Favourites).delete(room.roomId);
|
this.roomIdsBySpace.get(MetaSpace.Favourites)?.delete(room.roomId);
|
||||||
}
|
}
|
||||||
this.emit(MetaSpace.Favourites);
|
this.emit(MetaSpace.Favourites);
|
||||||
}
|
}
|
||||||
|
@ -1064,7 +1064,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
|
||||||
const homeRooms = this.roomIdsBySpace.get(MetaSpace.Home);
|
const homeRooms = this.roomIdsBySpace.get(MetaSpace.Home);
|
||||||
if (this.showInHomeSpace(room)) {
|
if (this.showInHomeSpace(room)) {
|
||||||
homeRooms?.add(room.roomId);
|
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);
|
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 (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.Orphans);
|
||||||
this.emit(MetaSpace.Home);
|
this.emit(MetaSpace.Home);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,6 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { defer } from "matrix-js-sdk/src/utils";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import BaseDialog from "../../../components/views/dialogs/BaseDialog";
|
import BaseDialog from "../../../components/views/dialogs/BaseDialog";
|
||||||
|
@ -46,11 +45,7 @@ export const ConfirmListenBroadcastStopCurrentDialog: React.FC<Props> = ({ onFin
|
||||||
};
|
};
|
||||||
|
|
||||||
export const showConfirmListenBroadcastStopCurrentDialog = async (): Promise<boolean> => {
|
export const showConfirmListenBroadcastStopCurrentDialog = async (): Promise<boolean> => {
|
||||||
const { promise, resolve } = defer<boolean>();
|
const { finished } = Modal.createDialog(ConfirmListenBroadcastStopCurrentDialog);
|
||||||
|
const [confirmed] = await finished;
|
||||||
Modal.createDialog(ConfirmListenBroadcastStopCurrentDialog, {
|
return !!confirmed;
|
||||||
onFinished: resolve,
|
|
||||||
});
|
|
||||||
|
|
||||||
return promise;
|
|
||||||
};
|
};
|
|
@ -35,7 +35,7 @@ export const useVoiceBroadcastPlayback = (
|
||||||
position: number;
|
position: number;
|
||||||
timeLeft: number;
|
timeLeft: number;
|
||||||
};
|
};
|
||||||
sender: RoomMember;
|
sender: RoomMember | null;
|
||||||
liveness: VoiceBroadcastLiveness;
|
liveness: VoiceBroadcastLiveness;
|
||||||
playbackState: VoiceBroadcastPlaybackState;
|
playbackState: VoiceBroadcastPlaybackState;
|
||||||
toggle(): void;
|
toggle(): void;
|
||||||
|
|
|
@ -44,7 +44,7 @@ const showStopBroadcastingDialog = async (): Promise<boolean> => {
|
||||||
button: _t("Yes, stop broadcast"),
|
button: _t("Yes, stop broadcast"),
|
||||||
});
|
});
|
||||||
const [confirmed] = await finished;
|
const [confirmed] = await finished;
|
||||||
return confirmed;
|
return !!confirmed;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useVoiceBroadcastRecording = (
|
export const useVoiceBroadcastRecording = (
|
||||||
|
@ -54,7 +54,7 @@ export const useVoiceBroadcastRecording = (
|
||||||
timeLeft: number;
|
timeLeft: number;
|
||||||
recordingState: VoiceBroadcastRecordingState;
|
recordingState: VoiceBroadcastRecordingState;
|
||||||
room: Room;
|
room: Room;
|
||||||
sender: RoomMember;
|
sender: RoomMember | null;
|
||||||
stopRecording(): void;
|
stopRecording(): void;
|
||||||
toggleRecording(): void;
|
toggleRecording(): void;
|
||||||
} => {
|
} => {
|
||||||
|
|
|
@ -32,7 +32,7 @@ export * from "./components/atoms/VoiceBroadcastHeader";
|
||||||
export * from "./components/atoms/VoiceBroadcastPlaybackControl";
|
export * from "./components/atoms/VoiceBroadcastPlaybackControl";
|
||||||
export * from "./components/atoms/VoiceBroadcastRecordingConnectionError";
|
export * from "./components/atoms/VoiceBroadcastRecordingConnectionError";
|
||||||
export * from "./components/atoms/VoiceBroadcastRoomSubtitle";
|
export * from "./components/atoms/VoiceBroadcastRoomSubtitle";
|
||||||
export * from "./components/molecules/ConfirmListeBroadcastStopCurrent";
|
export * from "./components/molecules/ConfirmListenBroadcastStopCurrent";
|
||||||
export * from "./components/molecules/VoiceBroadcastPlaybackBody";
|
export * from "./components/molecules/VoiceBroadcastPlaybackBody";
|
||||||
export * from "./components/molecules/VoiceBroadcastSmallPlaybackBody";
|
export * from "./components/molecules/VoiceBroadcastSmallPlaybackBody";
|
||||||
export * from "./components/molecules/VoiceBroadcastPreRecordingPip";
|
export * from "./components/molecules/VoiceBroadcastPreRecordingPip";
|
||||||
|
|
|
@ -674,7 +674,7 @@ describe("MessagePanel", function () {
|
||||||
// Increase the length of the loop here to test performance issues with
|
// Increase the length of the loop here to test performance issues with
|
||||||
// rendering
|
// rendering
|
||||||
|
|
||||||
const events = [];
|
const events: MatrixEvent[] = [];
|
||||||
for (let i = 0; i < 100; i++) {
|
for (let i = 0; i < 100; i++) {
|
||||||
events.push(
|
events.push(
|
||||||
TestUtilsMatrix.mkEvent({
|
TestUtilsMatrix.mkEvent({
|
||||||
|
|
|
@ -35,6 +35,7 @@ import RightPanelStore from "../../../src/stores/right-panel/RightPanelStore";
|
||||||
import { UPDATE_EVENT } from "../../../src/stores/AsyncStore";
|
import { UPDATE_EVENT } from "../../../src/stores/AsyncStore";
|
||||||
import { WidgetLayoutStore } from "../../../src/stores/widgets/WidgetLayoutStore";
|
import { WidgetLayoutStore } from "../../../src/stores/widgets/WidgetLayoutStore";
|
||||||
import { SdkContextClass } from "../../../src/contexts/SDKContext";
|
import { SdkContextClass } from "../../../src/contexts/SDKContext";
|
||||||
|
import { RoomPermalinkCreator } from "../../../src/utils/permalinks/Permalinks";
|
||||||
|
|
||||||
const RightPanelBase = wrapInMatrixClientContext(_RightPanel);
|
const RightPanelBase = wrapInMatrixClientContext(_RightPanel);
|
||||||
|
|
||||||
|
@ -110,7 +111,13 @@ describe("RightPanel", () => {
|
||||||
});
|
});
|
||||||
await viewedRoom;
|
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);
|
expect(container.getElementsByClassName("mx_RoomSummaryCard")).toHaveLength(1);
|
||||||
|
|
||||||
const switchedPhases = waitForRpsUpdate();
|
const switchedPhases = waitForRpsUpdate();
|
||||||
|
@ -152,7 +159,13 @@ describe("RightPanel", () => {
|
||||||
await spinUpStores();
|
await spinUpStores();
|
||||||
|
|
||||||
// Run initial render with room 1, and also running lifecycle methods
|
// 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
|
// Wait for RPS room 1 updates to fire
|
||||||
const rpsUpdated = waitForRpsUpdate();
|
const rpsUpdated = waitForRpsUpdate();
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
|
@ -172,7 +185,13 @@ describe("RightPanel", () => {
|
||||||
room_id: "r2",
|
room_id: "r2",
|
||||||
});
|
});
|
||||||
await _rpsUpdated;
|
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...
|
// After all that setup, now to the interesting part...
|
||||||
// We want to verify that as we change to room 2, we should always have
|
// 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 { ElementWidget } from "../../../../src/stores/widgets/StopGapWidget";
|
||||||
import { WidgetMessagingStore } from "../../../../src/stores/widgets/WidgetMessagingStore";
|
import { WidgetMessagingStore } from "../../../../src/stores/widgets/WidgetMessagingStore";
|
||||||
import { ModuleRunner } from "../../../../src/modules/ModuleRunner";
|
import { ModuleRunner } from "../../../../src/modules/ModuleRunner";
|
||||||
|
import { RoomPermalinkCreator } from "../../../../src/utils/permalinks/Permalinks";
|
||||||
|
|
||||||
describe("AppTile", () => {
|
describe("AppTile", () => {
|
||||||
let cli: MatrixClient;
|
let cli: MatrixClient;
|
||||||
|
@ -153,7 +154,11 @@ describe("AppTile", () => {
|
||||||
// Run initial render with room 1, and also running lifecycle methods
|
// Run initial render with room 1, and also running lifecycle methods
|
||||||
const renderResult = render(
|
const renderResult = render(
|
||||||
<MatrixClientContext.Provider value={cli}>
|
<MatrixClientContext.Provider value={cli}>
|
||||||
<RightPanel room={r1} resizeNotifier={resizeNotifier} />
|
<RightPanel
|
||||||
|
room={r1}
|
||||||
|
resizeNotifier={resizeNotifier}
|
||||||
|
permalinkCreator={new RoomPermalinkCreator(r1, r1.roomId)}
|
||||||
|
/>
|
||||||
</MatrixClientContext.Provider>,
|
</MatrixClientContext.Provider>,
|
||||||
);
|
);
|
||||||
// Wait for RPS room 1 updates to fire
|
// Wait for RPS room 1 updates to fire
|
||||||
|
@ -178,7 +183,11 @@ describe("AppTile", () => {
|
||||||
|
|
||||||
renderResult.rerender(
|
renderResult.rerender(
|
||||||
<MatrixClientContext.Provider value={cli}>
|
<MatrixClientContext.Provider value={cli}>
|
||||||
<RightPanel room={r2} resizeNotifier={resizeNotifier} />
|
<RightPanel
|
||||||
|
room={r2}
|
||||||
|
resizeNotifier={resizeNotifier}
|
||||||
|
permalinkCreator={new RoomPermalinkCreator(r2, r2.roomId)}
|
||||||
|
/>
|
||||||
</MatrixClientContext.Provider>,
|
</MatrixClientContext.Provider>,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -214,7 +223,11 @@ describe("AppTile", () => {
|
||||||
// Run initial render with room 1, and also running lifecycle methods
|
// Run initial render with room 1, and also running lifecycle methods
|
||||||
const renderResult = render(
|
const renderResult = render(
|
||||||
<MatrixClientContext.Provider value={cli}>
|
<MatrixClientContext.Provider value={cli}>
|
||||||
<RightPanel room={r1} resizeNotifier={resizeNotifier} />
|
<RightPanel
|
||||||
|
room={r1}
|
||||||
|
resizeNotifier={resizeNotifier}
|
||||||
|
permalinkCreator={new RoomPermalinkCreator(r1, r1.roomId)}
|
||||||
|
/>
|
||||||
</MatrixClientContext.Provider>,
|
</MatrixClientContext.Provider>,
|
||||||
);
|
);
|
||||||
// Wait for RPS room 1 updates to fire
|
// Wait for RPS room 1 updates to fire
|
||||||
|
@ -256,7 +269,11 @@ describe("AppTile", () => {
|
||||||
});
|
});
|
||||||
renderResult.rerender(
|
renderResult.rerender(
|
||||||
<MatrixClientContext.Provider value={cli}>
|
<MatrixClientContext.Provider value={cli}>
|
||||||
<RightPanel room={r2} resizeNotifier={resizeNotifier} />
|
<RightPanel
|
||||||
|
room={r2}
|
||||||
|
resizeNotifier={resizeNotifier}
|
||||||
|
permalinkCreator={new RoomPermalinkCreator(r2, r2.roomId)}
|
||||||
|
/>
|
||||||
</MatrixClientContext.Provider>,
|
</MatrixClientContext.Provider>,
|
||||||
);
|
);
|
||||||
await rpsUpdated2;
|
await rpsUpdated2;
|
||||||
|
|
|
@ -68,7 +68,7 @@ describe("PreferencesUserSettingsTab", () => {
|
||||||
|
|
||||||
const expectSetValueToHaveBeenCalled = (
|
const expectSetValueToHaveBeenCalled = (
|
||||||
name: string,
|
name: string,
|
||||||
roomId: string | undefined,
|
roomId: string | null,
|
||||||
level: SettingLevel,
|
level: SettingLevel,
|
||||||
value: boolean,
|
value: boolean,
|
||||||
) => expect(SettingsStore.setValue).toHaveBeenCalledWith(name, roomId, level, value);
|
) => expect(SettingsStore.setValue).toHaveBeenCalledWith(name, roomId, level, value);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue