Make more code conform to strict null checks (#10219
* Make more code conform to strict null checks * Fix types * Fix tests * Fix remaining test assertions * Iterate PR
This commit is contained in:
parent
4c79ecf141
commit
76b82b4b2b
130 changed files with 603 additions and 603 deletions
|
@ -49,7 +49,7 @@ export type Binding = {
|
||||||
*/
|
*/
|
||||||
export default class AddThreepid {
|
export default class AddThreepid {
|
||||||
private sessionId: string;
|
private sessionId: string;
|
||||||
private submitUrl: string;
|
private submitUrl?: string;
|
||||||
private clientSecret: string;
|
private clientSecret: string;
|
||||||
private bind: boolean;
|
private bind: boolean;
|
||||||
|
|
||||||
|
@ -93,7 +93,7 @@ export default class AddThreepid {
|
||||||
if (await MatrixClientPeg.get().doesServerSupportSeparateAddAndBind()) {
|
if (await MatrixClientPeg.get().doesServerSupportSeparateAddAndBind()) {
|
||||||
// For separate bind, request a token directly from the IS.
|
// For separate bind, request a token directly from the IS.
|
||||||
const authClient = new IdentityAuthClient();
|
const authClient = new IdentityAuthClient();
|
||||||
const identityAccessToken = await authClient.getAccessToken();
|
const identityAccessToken = (await authClient.getAccessToken()) ?? undefined;
|
||||||
return MatrixClientPeg.get()
|
return MatrixClientPeg.get()
|
||||||
.requestEmailToken(emailAddress, this.clientSecret, 1, undefined, identityAccessToken)
|
.requestEmailToken(emailAddress, this.clientSecret, 1, undefined, identityAccessToken)
|
||||||
.then(
|
.then(
|
||||||
|
@ -155,7 +155,7 @@ export default class AddThreepid {
|
||||||
if (await MatrixClientPeg.get().doesServerSupportSeparateAddAndBind()) {
|
if (await MatrixClientPeg.get().doesServerSupportSeparateAddAndBind()) {
|
||||||
// For separate bind, request a token directly from the IS.
|
// For separate bind, request a token directly from the IS.
|
||||||
const authClient = new IdentityAuthClient();
|
const authClient = new IdentityAuthClient();
|
||||||
const identityAccessToken = await authClient.getAccessToken();
|
const identityAccessToken = (await authClient.getAccessToken()) ?? undefined;
|
||||||
return MatrixClientPeg.get()
|
return MatrixClientPeg.get()
|
||||||
.requestMsisdnToken(phoneCountry, phoneNumber, this.clientSecret, 1, undefined, identityAccessToken)
|
.requestMsisdnToken(phoneCountry, phoneNumber, this.clientSecret, 1, undefined, identityAccessToken)
|
||||||
.then(
|
.then(
|
||||||
|
@ -184,7 +184,7 @@ export default class AddThreepid {
|
||||||
* with a "message" property which contains a human-readable message detailing why
|
* with a "message" property which contains a human-readable message detailing why
|
||||||
* the request failed.
|
* the request failed.
|
||||||
*/
|
*/
|
||||||
public async checkEmailLinkClicked(): Promise<[boolean, IAuthData | Error | null]> {
|
public async checkEmailLinkClicked(): Promise<[boolean, IAuthData | Error | null] | undefined> {
|
||||||
try {
|
try {
|
||||||
if (await MatrixClientPeg.get().doesServerSupportSeparateAddAndBind()) {
|
if (await MatrixClientPeg.get().doesServerSupportSeparateAddAndBind()) {
|
||||||
if (this.bind) {
|
if (this.bind) {
|
||||||
|
@ -282,7 +282,7 @@ export default class AddThreepid {
|
||||||
* with a "message" property which contains a human-readable message detailing why
|
* with a "message" property which contains a human-readable message detailing why
|
||||||
* the request failed.
|
* the request failed.
|
||||||
*/
|
*/
|
||||||
public async haveMsisdnToken(msisdnToken: string): Promise<any[]> {
|
public async haveMsisdnToken(msisdnToken: string): Promise<any[] | undefined> {
|
||||||
const authClient = new IdentityAuthClient();
|
const authClient = new IdentityAuthClient();
|
||||||
const supportsSeparateAddAndBind = await MatrixClientPeg.get().doesServerSupportSeparateAddAndBind();
|
const supportsSeparateAddAndBind = await MatrixClientPeg.get().doesServerSupportSeparateAddAndBind();
|
||||||
|
|
||||||
|
|
|
@ -546,7 +546,7 @@ export default class ContentMessages {
|
||||||
if (upload.cancelled) throw new UploadCanceledError();
|
if (upload.cancelled) throw new UploadCanceledError();
|
||||||
const threadId = relation?.rel_type === THREAD_RELATION_TYPE.name ? relation.event_id : null;
|
const threadId = relation?.rel_type === THREAD_RELATION_TYPE.name ? relation.event_id : null;
|
||||||
|
|
||||||
const response = await matrixClient.sendMessage(roomId, threadId, content);
|
const response = await matrixClient.sendMessage(roomId, threadId ?? null, content);
|
||||||
|
|
||||||
if (SettingsStore.getValue("Performance.addSendMessageTimingMetadata")) {
|
if (SettingsStore.getValue("Performance.addSendMessageTimingMetadata")) {
|
||||||
sendRoundTripMetric(matrixClient, roomId, response.event_id);
|
sendRoundTripMetric(matrixClient, roomId, response.event_id);
|
||||||
|
|
|
@ -1024,13 +1024,12 @@ export default class LegacyCallHandler extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
public answerCall(roomId: string): void {
|
public answerCall(roomId: string): void {
|
||||||
const call = this.calls.get(roomId);
|
|
||||||
|
|
||||||
this.stopRingingIfPossible(call.callId);
|
|
||||||
|
|
||||||
// no call to answer
|
// no call to answer
|
||||||
if (!this.calls.has(roomId)) return;
|
if (!this.calls.has(roomId)) return;
|
||||||
|
|
||||||
|
const call = this.calls.get(roomId)!;
|
||||||
|
this.stopRingingIfPossible(call.callId);
|
||||||
|
|
||||||
if (this.getAllActiveCalls().length > 1) {
|
if (this.getAllActiveCalls().length > 1) {
|
||||||
Modal.createDialog(ErrorDialog, {
|
Modal.createDialog(ErrorDialog, {
|
||||||
title: _t("Too Many Calls"),
|
title: _t("Too Many Calls"),
|
||||||
|
|
|
@ -287,7 +287,7 @@ export function handleInvalidStoreError(e: InvalidStoreError): Promise<void> {
|
||||||
return MatrixClientPeg.get().store.deleteAllData();
|
return MatrixClientPeg.get().store.deleteAllData();
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
PlatformPeg.get().reload();
|
PlatformPeg.get()?.reload();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -519,7 +519,7 @@ export async function setLoggedIn(credentials: IMatrixClientCreds): Promise<Matr
|
||||||
stopMatrixClient();
|
stopMatrixClient();
|
||||||
const pickleKey =
|
const pickleKey =
|
||||||
credentials.userId && credentials.deviceId
|
credentials.userId && credentials.deviceId
|
||||||
? await PlatformPeg.get().createPickleKey(credentials.userId, credentials.deviceId)
|
? await PlatformPeg.get()?.createPickleKey(credentials.userId, credentials.deviceId)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
if (pickleKey) {
|
if (pickleKey) {
|
||||||
|
|
|
@ -166,7 +166,7 @@ class MatrixClientPegClass implements IMatrixClientPeg {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const registrationTime = parseInt(window.localStorage.getItem("mx_registration_time"), 10);
|
const registrationTime = parseInt(window.localStorage.getItem("mx_registration_time")!, 10);
|
||||||
const diff = Date.now() - registrationTime;
|
const diff = Date.now() - registrationTime;
|
||||||
return diff / 36e5 <= hours;
|
return diff / 36e5 <= hours;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -176,7 +176,7 @@ class MatrixClientPegClass implements IMatrixClientPeg {
|
||||||
|
|
||||||
public userRegisteredAfter(timestamp: Date): boolean {
|
public userRegisteredAfter(timestamp: Date): boolean {
|
||||||
try {
|
try {
|
||||||
const registrationTime = parseInt(window.localStorage.getItem("mx_registration_time"), 10);
|
const registrationTime = parseInt(window.localStorage.getItem("mx_registration_time")!, 10);
|
||||||
return timestamp.getTime() <= registrationTime;
|
return timestamp.getTime() <= registrationTime;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -292,7 +292,7 @@ class MatrixClientPegClass implements IMatrixClientPeg {
|
||||||
}
|
}
|
||||||
|
|
||||||
public getCredentials(): IMatrixClientCreds {
|
public getCredentials(): IMatrixClientCreds {
|
||||||
let copiedCredentials = this.currentClientCreds;
|
let copiedCredentials: IMatrixClientCreds | null = this.currentClientCreds;
|
||||||
if (this.currentClientCreds?.userId !== this.matrixClient?.credentials?.userId) {
|
if (this.currentClientCreds?.userId !== this.matrixClient?.credentials?.userId) {
|
||||||
// cached credentials belong to a different user - don't use them
|
// cached credentials belong to a different user - don't use them
|
||||||
copiedCredentials = null;
|
copiedCredentials = null;
|
||||||
|
|
|
@ -64,7 +64,7 @@ export default class MediaDeviceHandler extends EventEmitter {
|
||||||
*
|
*
|
||||||
* @return Promise<IMediaDevices> The available media devices
|
* @return Promise<IMediaDevices> The available media devices
|
||||||
*/
|
*/
|
||||||
public static async getDevices(): Promise<IMediaDevices> {
|
public static async getDevices(): Promise<IMediaDevices | undefined> {
|
||||||
try {
|
try {
|
||||||
const devices = await navigator.mediaDevices.enumerateDevices();
|
const devices = await navigator.mediaDevices.enumerateDevices();
|
||||||
const output: Record<MediaDeviceKindEnum, MediaDeviceInfo[]> = {
|
const output: Record<MediaDeviceKindEnum, MediaDeviceInfo[]> = {
|
||||||
|
|
|
@ -54,8 +54,8 @@ export default class PosthogTrackers {
|
||||||
}
|
}
|
||||||
|
|
||||||
private view: Views = Views.LOADING;
|
private view: Views = Views.LOADING;
|
||||||
private pageType?: PageType = null;
|
private pageType?: PageType;
|
||||||
private override?: ScreenName = null;
|
private override?: ScreenName;
|
||||||
|
|
||||||
public trackPageChange(view: Views, pageType: PageType | undefined, durationMs: number): void {
|
public trackPageChange(view: Views, pageType: PageType | undefined, durationMs: number): void {
|
||||||
this.view = view;
|
this.view = view;
|
||||||
|
@ -66,7 +66,7 @@ export default class PosthogTrackers {
|
||||||
|
|
||||||
private trackPage(durationMs?: number): void {
|
private trackPage(durationMs?: number): void {
|
||||||
const screenName =
|
const screenName =
|
||||||
this.view === Views.LOGGED_IN ? loggedInPageTypeMap[this.pageType] : notLoggedInMap[this.view];
|
this.view === Views.LOGGED_IN ? loggedInPageTypeMap[this.pageType!] : notLoggedInMap[this.view];
|
||||||
PosthogAnalytics.instance.trackEvent<ScreenEvent>({
|
PosthogAnalytics.instance.trackEvent<ScreenEvent>({
|
||||||
eventName: "$pageview",
|
eventName: "$pageview",
|
||||||
$current_url: screenName,
|
$current_url: screenName,
|
||||||
|
@ -85,7 +85,7 @@ export default class PosthogTrackers {
|
||||||
|
|
||||||
public clearOverride(screenName: ScreenName): void {
|
public clearOverride(screenName: ScreenName): void {
|
||||||
if (screenName !== this.override) return;
|
if (screenName !== this.override) return;
|
||||||
this.override = null;
|
this.override = undefined;
|
||||||
this.trackPage();
|
this.trackPage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,6 @@ export const DEFAULTS: IConfigOptions = {
|
||||||
brand: "Element",
|
brand: "Element",
|
||||||
integrations_ui_url: "https://scalar.vector.im/",
|
integrations_ui_url: "https://scalar.vector.im/",
|
||||||
integrations_rest_url: "https://scalar.vector.im/api",
|
integrations_rest_url: "https://scalar.vector.im/api",
|
||||||
bug_report_endpoint_url: null,
|
|
||||||
uisi_autorageshake_app: "element-auto-uisi",
|
uisi_autorageshake_app: "element-auto-uisi",
|
||||||
|
|
||||||
jitsi: {
|
jitsi: {
|
||||||
|
|
|
@ -198,7 +198,7 @@ function reject(error?: any): RunResult {
|
||||||
return { error };
|
return { error };
|
||||||
}
|
}
|
||||||
|
|
||||||
function success(promise?: Promise<any>): RunResult {
|
function success(promise: Promise<any> = Promise.resolve()): RunResult {
|
||||||
return { promise };
|
return { promise };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -221,7 +221,7 @@ export const Commands = [
|
||||||
command: "spoiler",
|
command: "spoiler",
|
||||||
args: "<message>",
|
args: "<message>",
|
||||||
description: _td("Sends the given message as a spoiler"),
|
description: _td("Sends the given message as a spoiler"),
|
||||||
runFn: function (roomId, message) {
|
runFn: function (roomId, message = "") {
|
||||||
return successSync(ContentHelpers.makeHtmlMessage(message, `<span data-mx-spoiler>${message}</span>`));
|
return successSync(ContentHelpers.makeHtmlMessage(message, `<span data-mx-spoiler>${message}</span>`));
|
||||||
},
|
},
|
||||||
category: CommandCategories.messages,
|
category: CommandCategories.messages,
|
||||||
|
@ -282,7 +282,7 @@ export const Commands = [
|
||||||
command: "plain",
|
command: "plain",
|
||||||
args: "<message>",
|
args: "<message>",
|
||||||
description: _td("Sends a message as plain text, without interpreting it as markdown"),
|
description: _td("Sends a message as plain text, without interpreting it as markdown"),
|
||||||
runFn: function (roomId, messages) {
|
runFn: function (roomId, messages = "") {
|
||||||
return successSync(ContentHelpers.makeTextMessage(messages));
|
return successSync(ContentHelpers.makeTextMessage(messages));
|
||||||
},
|
},
|
||||||
category: CommandCategories.messages,
|
category: CommandCategories.messages,
|
||||||
|
@ -291,7 +291,7 @@ export const Commands = [
|
||||||
command: "html",
|
command: "html",
|
||||||
args: "<message>",
|
args: "<message>",
|
||||||
description: _td("Sends a message as html, without interpreting it as markdown"),
|
description: _td("Sends a message as html, without interpreting it as markdown"),
|
||||||
runFn: function (roomId, messages) {
|
runFn: function (roomId, messages = "") {
|
||||||
return successSync(ContentHelpers.makeHtmlMessage(messages, messages));
|
return successSync(ContentHelpers.makeHtmlMessage(messages, messages));
|
||||||
},
|
},
|
||||||
category: CommandCategories.messages,
|
category: CommandCategories.messages,
|
||||||
|
|
|
@ -27,6 +27,7 @@ import { timeout } from "../utils/promise";
|
||||||
import AutocompleteProvider, { ICommand } from "./AutocompleteProvider";
|
import AutocompleteProvider, { ICommand } from "./AutocompleteProvider";
|
||||||
import SpaceProvider from "./SpaceProvider";
|
import SpaceProvider from "./SpaceProvider";
|
||||||
import { TimelineRenderingType } from "../contexts/RoomContext";
|
import { TimelineRenderingType } from "../contexts/RoomContext";
|
||||||
|
import { filterBoolean } from "../utils/arrays";
|
||||||
|
|
||||||
export interface ISelectionRange {
|
export interface ISelectionRange {
|
||||||
beginning?: boolean; // whether the selection is in the first block of the editor or not
|
beginning?: boolean; // whether the selection is in the first block of the editor or not
|
||||||
|
@ -55,7 +56,7 @@ const PROVIDER_COMPLETION_TIMEOUT = 3000;
|
||||||
export interface IProviderCompletions {
|
export interface IProviderCompletions {
|
||||||
completions: ICompletion[];
|
completions: ICompletion[];
|
||||||
provider: AutocompleteProvider;
|
provider: AutocompleteProvider;
|
||||||
command: ICommand;
|
command: Partial<ICommand>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Autocompleter {
|
export default class Autocompleter {
|
||||||
|
@ -98,8 +99,8 @@ export default class Autocompleter {
|
||||||
);
|
);
|
||||||
|
|
||||||
// map then filter to maintain the index for the map-operation, for this.providers to line up
|
// map then filter to maintain the index for the map-operation, for this.providers to line up
|
||||||
return completionsList
|
return filterBoolean(
|
||||||
.map((completions, i) => {
|
completionsList.map((completions, i) => {
|
||||||
if (!completions || !completions.length) return;
|
if (!completions || !completions.length) return;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -112,7 +113,7 @@ export default class Autocompleter {
|
||||||
*/
|
*/
|
||||||
command: this.providers[i].getCurrentCommand(query, selection, force),
|
command: this.providers[i].getCurrentCommand(query, selection, force),
|
||||||
};
|
};
|
||||||
})
|
}),
|
||||||
.filter(Boolean) as IProviderCompletions[];
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ import SettingsStore from "../settings/SettingsStore";
|
||||||
import { EMOJI, IEmoji, getEmojiFromUnicode } from "../emoji";
|
import { EMOJI, IEmoji, getEmojiFromUnicode } from "../emoji";
|
||||||
import { TimelineRenderingType } from "../contexts/RoomContext";
|
import { TimelineRenderingType } from "../contexts/RoomContext";
|
||||||
import * as recent from "../emojipicker/recent";
|
import * as recent from "../emojipicker/recent";
|
||||||
|
import { filterBoolean } from "../utils/arrays";
|
||||||
|
|
||||||
const LIMIT = 20;
|
const LIMIT = 20;
|
||||||
|
|
||||||
|
@ -94,7 +95,7 @@ export default class EmojiProvider extends AutocompleteProvider {
|
||||||
shouldMatchWordsOnly: true,
|
shouldMatchWordsOnly: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.recentlyUsed = Array.from(new Set(recent.get().map(getEmojiFromUnicode).filter(Boolean))) as IEmoji[];
|
this.recentlyUsed = Array.from(new Set(filterBoolean(recent.get().map(getEmojiFromUnicode))));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getCompletions(
|
public async getCompletions(
|
||||||
|
|
|
@ -46,7 +46,7 @@ interface IState {
|
||||||
export default class EmbeddedPage extends React.PureComponent<IProps, IState> {
|
export default class EmbeddedPage extends React.PureComponent<IProps, IState> {
|
||||||
public static contextType = MatrixClientContext;
|
public static contextType = MatrixClientContext;
|
||||||
private unmounted = false;
|
private unmounted = false;
|
||||||
private dispatcherRef: string = null;
|
private dispatcherRef: string | null = null;
|
||||||
|
|
||||||
public constructor(props: IProps, context: typeof MatrixClientContext) {
|
public constructor(props: IProps, context: typeof MatrixClientContext) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
|
@ -64,7 +64,7 @@ export default class EmbeddedPage extends React.PureComponent<IProps, IState> {
|
||||||
let res: Response;
|
let res: Response;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
res = await fetch(this.props.url, { method: "GET" });
|
res = await fetch(this.props.url!, { method: "GET" });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (this.unmounted) return;
|
if (this.unmounted) return;
|
||||||
logger.warn(`Error loading page: ${err}`);
|
logger.warn(`Error loading page: ${err}`);
|
||||||
|
@ -84,7 +84,7 @@ export default class EmbeddedPage extends React.PureComponent<IProps, IState> {
|
||||||
|
|
||||||
if (this.props.replaceMap) {
|
if (this.props.replaceMap) {
|
||||||
Object.keys(this.props.replaceMap).forEach((key) => {
|
Object.keys(this.props.replaceMap).forEach((key) => {
|
||||||
body = body.split(key).join(this.props.replaceMap[key]);
|
body = body.split(key).join(this.props.replaceMap![key]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,8 +123,7 @@ export default class EmbeddedPage extends React.PureComponent<IProps, IState> {
|
||||||
const client = this.context || MatrixClientPeg.get();
|
const client = this.context || MatrixClientPeg.get();
|
||||||
const isGuest = client ? client.isGuest() : true;
|
const isGuest = client ? client.isGuest() : true;
|
||||||
const className = this.props.className;
|
const className = this.props.className;
|
||||||
const classes = classnames({
|
const classes = classnames(className, {
|
||||||
[className]: true,
|
|
||||||
[`${className}_guest`]: isGuest,
|
[`${className}_guest`]: isGuest,
|
||||||
[`${className}_loggedIn`]: !!client,
|
[`${className}_loggedIn`]: !!client,
|
||||||
});
|
});
|
||||||
|
|
|
@ -40,6 +40,7 @@ const FileDropTarget: React.FC<IProps> = ({ parent, onFileDrop }) => {
|
||||||
const onDragEnter = (ev: DragEvent): void => {
|
const onDragEnter = (ev: DragEvent): void => {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
if (!ev.dataTransfer) return;
|
||||||
|
|
||||||
setState((state) => ({
|
setState((state) => ({
|
||||||
// We always increment the counter no matter the types, because dragging is
|
// We always increment the counter no matter the types, because dragging is
|
||||||
|
@ -49,7 +50,8 @@ const FileDropTarget: React.FC<IProps> = ({ parent, onFileDrop }) => {
|
||||||
// https://docs.w3cub.com/dom/datatransfer/types
|
// https://docs.w3cub.com/dom/datatransfer/types
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/Recommended_drag_types#file
|
// https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/Recommended_drag_types#file
|
||||||
dragging:
|
dragging:
|
||||||
ev.dataTransfer.types.includes("Files") || ev.dataTransfer.types.includes("application/x-moz-file")
|
ev.dataTransfer!.types.includes("Files") ||
|
||||||
|
ev.dataTransfer!.types.includes("application/x-moz-file")
|
||||||
? true
|
? true
|
||||||
: state.dragging,
|
: state.dragging,
|
||||||
}));
|
}));
|
||||||
|
@ -68,6 +70,7 @@ const FileDropTarget: React.FC<IProps> = ({ parent, onFileDrop }) => {
|
||||||
const onDragOver = (ev: DragEvent): void => {
|
const onDragOver = (ev: DragEvent): void => {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
if (!ev.dataTransfer) return;
|
||||||
|
|
||||||
ev.dataTransfer.dropEffect = "none";
|
ev.dataTransfer.dropEffect = "none";
|
||||||
|
|
||||||
|
@ -82,6 +85,7 @@ const FileDropTarget: React.FC<IProps> = ({ parent, onFileDrop }) => {
|
||||||
const onDrop = (ev: DragEvent): void => {
|
const onDrop = (ev: DragEvent): void => {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
if (!ev.dataTransfer) return;
|
||||||
onFileDrop(ev.dataTransfer);
|
onFileDrop(ev.dataTransfer);
|
||||||
|
|
||||||
setState((state) => ({
|
setState((state) => ({
|
||||||
|
|
|
@ -66,7 +66,7 @@ class FilePanel extends React.Component<IProps, IState> {
|
||||||
|
|
||||||
private onRoomTimeline = (
|
private onRoomTimeline = (
|
||||||
ev: MatrixEvent,
|
ev: MatrixEvent,
|
||||||
room: Room | null,
|
room: Room | undefined,
|
||||||
toStartOfTimeline: boolean,
|
toStartOfTimeline: boolean,
|
||||||
removed: boolean,
|
removed: boolean,
|
||||||
data: IRoomTimelineData,
|
data: IRoomTimelineData,
|
||||||
|
@ -78,7 +78,7 @@ class FilePanel extends React.Component<IProps, IState> {
|
||||||
client.decryptEventIfNeeded(ev);
|
client.decryptEventIfNeeded(ev);
|
||||||
|
|
||||||
if (ev.isBeingDecrypted()) {
|
if (ev.isBeingDecrypted()) {
|
||||||
this.decryptingEvents.add(ev.getId());
|
this.decryptingEvents.add(ev.getId()!);
|
||||||
} else {
|
} else {
|
||||||
this.addEncryptedLiveEvent(ev);
|
this.addEncryptedLiveEvent(ev);
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ class FilePanel extends React.Component<IProps, IState> {
|
||||||
|
|
||||||
private onEventDecrypted = (ev: MatrixEvent, err?: any): void => {
|
private onEventDecrypted = (ev: MatrixEvent, err?: any): void => {
|
||||||
if (ev.getRoomId() !== this.props.roomId) return;
|
if (ev.getRoomId() !== this.props.roomId) return;
|
||||||
const eventId = ev.getId();
|
const eventId = ev.getId()!;
|
||||||
|
|
||||||
if (!this.decryptingEvents.delete(eventId)) return;
|
if (!this.decryptingEvents.delete(eventId)) return;
|
||||||
if (err) return;
|
if (err) return;
|
||||||
|
@ -103,7 +103,7 @@ class FilePanel extends React.Component<IProps, IState> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.state.timelineSet.eventIdToTimeline(ev.getId())) {
|
if (!this.state.timelineSet.eventIdToTimeline(ev.getId()!)) {
|
||||||
this.state.timelineSet.addEventToTimeline(ev, timeline, false);
|
this.state.timelineSet.addEventToTimeline(ev, timeline, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,15 +56,15 @@ const getOwnProfile = (
|
||||||
userId: string,
|
userId: string,
|
||||||
): {
|
): {
|
||||||
displayName: string;
|
displayName: string;
|
||||||
avatarUrl: string;
|
avatarUrl?: string;
|
||||||
} => ({
|
} => ({
|
||||||
displayName: OwnProfileStore.instance.displayName || userId,
|
displayName: OwnProfileStore.instance.displayName || userId,
|
||||||
avatarUrl: OwnProfileStore.instance.getHttpAvatarUrl(AVATAR_SIZE),
|
avatarUrl: OwnProfileStore.instance.getHttpAvatarUrl(AVATAR_SIZE) ?? undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
const UserWelcomeTop: React.FC = () => {
|
const UserWelcomeTop: React.FC = () => {
|
||||||
const cli = useContext(MatrixClientContext);
|
const cli = useContext(MatrixClientContext);
|
||||||
const userId = cli.getUserId();
|
const userId = cli.getUserId()!;
|
||||||
const [ownProfile, setOwnProfile] = useState(getOwnProfile(userId));
|
const [ownProfile, setOwnProfile] = useState(getOwnProfile(userId));
|
||||||
useEventEmitter(OwnProfileStore.instance, UPDATE_EVENT, () => {
|
useEventEmitter(OwnProfileStore.instance, UPDATE_EVENT, () => {
|
||||||
setOwnProfile(getOwnProfile(userId));
|
setOwnProfile(getOwnProfile(userId));
|
||||||
|
|
|
@ -33,7 +33,7 @@ export const ERROR_USER_CANCELLED = new Error("User cancelled auth session");
|
||||||
|
|
||||||
type InteractiveAuthCallbackSuccess = (
|
type InteractiveAuthCallbackSuccess = (
|
||||||
success: true,
|
success: true,
|
||||||
response: IAuthData,
|
response?: IAuthData,
|
||||||
extra?: { emailSid?: string; clientSecret?: string },
|
extra?: { emailSid?: string; clientSecret?: string },
|
||||||
) => void;
|
) => void;
|
||||||
type InteractiveAuthCallbackFailure = (success: false, response: IAuthData | Error) => void;
|
type InteractiveAuthCallbackFailure = (success: false, response: IAuthData | Error) => void;
|
||||||
|
@ -94,7 +94,7 @@ interface IState {
|
||||||
|
|
||||||
export default class InteractiveAuthComponent extends React.Component<IProps, IState> {
|
export default class InteractiveAuthComponent extends React.Component<IProps, IState> {
|
||||||
private readonly authLogic: InteractiveAuth;
|
private readonly authLogic: InteractiveAuth;
|
||||||
private readonly intervalId: number = null;
|
private readonly intervalId: number | null = null;
|
||||||
private readonly stageComponent = createRef<IStageComponent>();
|
private readonly stageComponent = createRef<IStageComponent>();
|
||||||
|
|
||||||
private unmounted = false;
|
private unmounted = false;
|
||||||
|
@ -103,10 +103,7 @@ export default class InteractiveAuthComponent extends React.Component<IProps, IS
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
authStage: null,
|
|
||||||
busy: false,
|
busy: false,
|
||||||
errorText: null,
|
|
||||||
errorCode: null,
|
|
||||||
submitButtonEnabled: false,
|
submitButtonEnabled: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -213,8 +210,8 @@ export default class InteractiveAuthComponent extends React.Component<IProps, IS
|
||||||
if (busy) {
|
if (busy) {
|
||||||
this.setState({
|
this.setState({
|
||||||
busy: true,
|
busy: true,
|
||||||
errorText: null,
|
errorText: undefined,
|
||||||
errorCode: null,
|
errorCode: undefined,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// The JS SDK eagerly reports itself as "not busy" right after any
|
// The JS SDK eagerly reports itself as "not busy" right after any
|
||||||
|
|
|
@ -166,10 +166,11 @@ export default class LeftPanel extends React.Component<IProps, IState> {
|
||||||
}
|
}
|
||||||
>();
|
>();
|
||||||
|
|
||||||
let lastTopHeader;
|
let lastTopHeader: HTMLDivElement | undefined;
|
||||||
let firstBottomHeader;
|
let firstBottomHeader: HTMLDivElement | undefined;
|
||||||
for (const sublist of sublists) {
|
for (const sublist of sublists) {
|
||||||
const header = sublist.querySelector<HTMLDivElement>(".mx_RoomSublist_stickable");
|
const header = sublist.querySelector<HTMLDivElement>(".mx_RoomSublist_stickable");
|
||||||
|
if (!header) continue; // this should never occur
|
||||||
header.style.removeProperty("display"); // always clear display:none first
|
header.style.removeProperty("display"); // always clear display:none first
|
||||||
|
|
||||||
// When an element is <=40% off screen, make it take over
|
// When an element is <=40% off screen, make it take over
|
||||||
|
@ -196,7 +197,7 @@ export default class LeftPanel extends React.Component<IProps, IState> {
|
||||||
// cause a no-op update, as adding/removing properties that are/aren't there cause
|
// cause a no-op update, as adding/removing properties that are/aren't there cause
|
||||||
// layout updates.
|
// layout updates.
|
||||||
for (const header of targetStyles.keys()) {
|
for (const header of targetStyles.keys()) {
|
||||||
const style = targetStyles.get(header);
|
const style = targetStyles.get(header)!;
|
||||||
|
|
||||||
if (style.makeInvisible) {
|
if (style.makeInvisible) {
|
||||||
// we will have already removed the 'display: none', so add it back.
|
// we will have already removed the 'display: none', so add it back.
|
||||||
|
@ -324,7 +325,7 @@ export default class LeftPanel extends React.Component<IProps, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderSearchDialExplore(): React.ReactNode {
|
private renderSearchDialExplore(): React.ReactNode {
|
||||||
let dialPadButton = null;
|
let dialPadButton: JSX.Element | undefined;
|
||||||
|
|
||||||
// If we have dialer support, show a button to bring up the dial pad
|
// If we have dialer support, show a button to bring up the dial pad
|
||||||
// to start a new call
|
// to start a new call
|
||||||
|
@ -338,7 +339,7 @@ export default class LeftPanel extends React.Component<IProps, IState> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let rightButton: JSX.Element;
|
let rightButton: JSX.Element | undefined;
|
||||||
if (this.state.showBreadcrumbs === BreadcrumbsMode.Labs) {
|
if (this.state.showBreadcrumbs === BreadcrumbsMode.Labs) {
|
||||||
rightButton = <RecentlyViewedButton />;
|
rightButton = <RecentlyViewedButton />;
|
||||||
} else if (this.state.activeSpace === MetaSpace.Home && shouldShowComponent(UIComponent.ExploreRooms)) {
|
} else if (this.state.activeSpace === MetaSpace.Home && shouldShowComponent(UIComponent.ExploreRooms)) {
|
||||||
|
|
|
@ -139,15 +139,15 @@ export default class ViewSource extends React.Component<IProps, IState> {
|
||||||
private canSendStateEvent(mxEvent: MatrixEvent): boolean {
|
private canSendStateEvent(mxEvent: MatrixEvent): boolean {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
const room = cli.getRoom(mxEvent.getRoomId());
|
const room = cli.getRoom(mxEvent.getRoomId());
|
||||||
return room.currentState.mayClientSendStateEvent(mxEvent.getType(), cli);
|
return !!room?.currentState.mayClientSendStateEvent(mxEvent.getType(), cli);
|
||||||
}
|
}
|
||||||
|
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
const mxEvent = this.props.mxEvent.replacingEvent() || this.props.mxEvent; // show the replacing event, not the original, if it is an edit
|
const mxEvent = this.props.mxEvent.replacingEvent() || this.props.mxEvent; // show the replacing event, not the original, if it is an edit
|
||||||
|
|
||||||
const isEditing = this.state.isEditing;
|
const isEditing = this.state.isEditing;
|
||||||
const roomId = mxEvent.getRoomId();
|
const roomId = mxEvent.getRoomId()!;
|
||||||
const eventId = mxEvent.getId();
|
const eventId = mxEvent.getId()!;
|
||||||
const canEdit = mxEvent.isState() ? this.canSendStateEvent(mxEvent) : canEditContent(this.props.mxEvent);
|
const canEdit = mxEvent.isState() ? this.canSendStateEvent(mxEvent) : canEditContent(this.props.mxEvent);
|
||||||
return (
|
return (
|
||||||
<BaseDialog className="mx_ViewSource" onFinished={this.props.onFinished} title={_t("View Source")}>
|
<BaseDialog className="mx_ViewSource" onFinished={this.props.onFinished} title={_t("View Source")}>
|
||||||
|
|
|
@ -39,6 +39,7 @@ import AuthBody from "../../views/auth/AuthBody";
|
||||||
import AuthHeader from "../../views/auth/AuthHeader";
|
import AuthHeader from "../../views/auth/AuthHeader";
|
||||||
import AccessibleButton, { ButtonEvent } from "../../views/elements/AccessibleButton";
|
import AccessibleButton, { ButtonEvent } from "../../views/elements/AccessibleButton";
|
||||||
import { ValidatedServerConfig } from "../../../utils/ValidatedServerConfig";
|
import { ValidatedServerConfig } from "../../../utils/ValidatedServerConfig";
|
||||||
|
import { filterBoolean } from "../../../utils/arrays";
|
||||||
|
|
||||||
// These are used in several places, and come from the js-sdk's autodiscovery
|
// These are used in several places, and come from the js-sdk's autodiscovery
|
||||||
// stuff. We define them here so that they'll be picked up by i18n.
|
// stuff. We define them here so that they'll be picked up by i18n.
|
||||||
|
@ -120,15 +121,11 @@ export default class LoginComponent extends React.PureComponent<IProps, IState>
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
busy: false,
|
busy: false,
|
||||||
busyLoggingIn: null,
|
|
||||||
errorText: null,
|
errorText: null,
|
||||||
loginIncorrect: false,
|
loginIncorrect: false,
|
||||||
canTryLogin: true,
|
canTryLogin: true,
|
||||||
|
|
||||||
flows: null,
|
|
||||||
|
|
||||||
username: props.defaultUsername ? props.defaultUsername : "",
|
username: props.defaultUsername ? props.defaultUsername : "",
|
||||||
phoneCountry: null,
|
|
||||||
phoneNumber: "",
|
phoneNumber: "",
|
||||||
|
|
||||||
serverIsAlive: true,
|
serverIsAlive: true,
|
||||||
|
@ -167,7 +164,7 @@ export default class LoginComponent extends React.PureComponent<IProps, IState>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public isBusy = (): boolean => this.state.busy || this.props.busy;
|
public isBusy = (): boolean => !!this.state.busy || !!this.props.busy;
|
||||||
|
|
||||||
public onPasswordLogin: OnPasswordLogin = async (
|
public onPasswordLogin: OnPasswordLogin = async (
|
||||||
username: string | undefined,
|
username: string | undefined,
|
||||||
|
@ -349,7 +346,7 @@ export default class LoginComponent extends React.PureComponent<IProps, IState>
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
const ssoKind = ssoFlow.type === "m.login.sso" ? "sso" : "cas";
|
const ssoKind = ssoFlow.type === "m.login.sso" ? "sso" : "cas";
|
||||||
PlatformPeg.get().startSingleSignOn(
|
PlatformPeg.get()?.startSingleSignOn(
|
||||||
this.loginLogic.createTemporaryClient(),
|
this.loginLogic.createTemporaryClient(),
|
||||||
ssoKind,
|
ssoKind,
|
||||||
this.props.fragmentAfterLogin,
|
this.props.fragmentAfterLogin,
|
||||||
|
@ -511,13 +508,13 @@ export default class LoginComponent extends React.PureComponent<IProps, IState>
|
||||||
return errorText;
|
return errorText;
|
||||||
}
|
}
|
||||||
|
|
||||||
public renderLoginComponentForFlows(): JSX.Element {
|
public renderLoginComponentForFlows(): ReactNode {
|
||||||
if (!this.state.flows) return null;
|
if (!this.state.flows) return null;
|
||||||
|
|
||||||
// this is the ideal order we want to show the flows in
|
// this is the ideal order we want to show the flows in
|
||||||
const order = ["m.login.password", "m.login.sso"];
|
const order = ["m.login.password", "m.login.sso"];
|
||||||
|
|
||||||
const flows = order.map((type) => this.state.flows.find((flow) => flow.type === type)).filter(Boolean);
|
const flows = filterBoolean(order.map((type) => this.state.flows.find((flow) => flow.type === type)));
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
{flows.map((flow) => {
|
{flows.map((flow) => {
|
||||||
|
|
|
@ -65,7 +65,7 @@ export default class CaptchaForm extends React.Component<ICaptchaFormProps, ICap
|
||||||
"src",
|
"src",
|
||||||
`https://www.recaptcha.net/recaptcha/api.js?onload=mxOnRecaptchaLoaded&render=explicit`,
|
`https://www.recaptcha.net/recaptcha/api.js?onload=mxOnRecaptchaLoaded&render=explicit`,
|
||||||
);
|
);
|
||||||
this.recaptchaContainer.current.appendChild(scriptTag);
|
this.recaptchaContainer.current?.appendChild(scriptTag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,7 +95,7 @@ export default class CaptchaForm extends React.Component<ICaptchaFormProps, ICap
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("Rendering to %s", divId);
|
logger.info("Rendering to %s", divId);
|
||||||
this.captchaWidgetId = global.grecaptcha.render(divId, {
|
this.captchaWidgetId = global.grecaptcha?.render(divId, {
|
||||||
sitekey: publicKey,
|
sitekey: publicKey,
|
||||||
callback: this.props.onCaptchaResponse,
|
callback: this.props.onCaptchaResponse,
|
||||||
});
|
});
|
||||||
|
@ -113,7 +113,7 @@ export default class CaptchaForm extends React.Component<ICaptchaFormProps, ICap
|
||||||
this.renderRecaptcha(DIV_ID);
|
this.renderRecaptcha(DIV_ID);
|
||||||
// clear error if re-rendered
|
// clear error if re-rendered
|
||||||
this.setState({
|
this.setState({
|
||||||
errorText: null,
|
errorText: undefined,
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -123,7 +123,7 @@ export default class CaptchaForm extends React.Component<ICaptchaFormProps, ICap
|
||||||
}
|
}
|
||||||
|
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
let error = null;
|
let error: JSX.Element | undefined;
|
||||||
if (this.state.errorText) {
|
if (this.state.errorText) {
|
||||||
error = <div className="error">{this.state.errorText}</div>;
|
error = <div className="error">{this.state.errorText}</div>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,12 +50,12 @@ class EmailField extends PureComponent<IProps> {
|
||||||
{
|
{
|
||||||
key: "required",
|
key: "required",
|
||||||
test: ({ value, allowEmpty }) => allowEmpty || !!value,
|
test: ({ value, allowEmpty }) => allowEmpty || !!value,
|
||||||
invalid: () => _t(this.props.labelRequired),
|
invalid: () => _t(this.props.labelRequired!),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "email",
|
key: "email",
|
||||||
test: ({ value }) => !value || Email.looksValid(value),
|
test: ({ value }) => !value || Email.looksValid(value),
|
||||||
invalid: () => _t(this.props.labelInvalid),
|
invalid: () => _t(this.props.labelInvalid!),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
@ -80,7 +80,7 @@ class EmailField extends PureComponent<IProps> {
|
||||||
id={this.props.id}
|
id={this.props.id}
|
||||||
ref={this.props.fieldRef}
|
ref={this.props.fieldRef}
|
||||||
type="text"
|
type="text"
|
||||||
label={_t(this.props.label)}
|
label={_t(this.props.label!)}
|
||||||
value={this.props.value}
|
value={this.props.value}
|
||||||
autoFocus={this.props.autoFocus}
|
autoFocus={this.props.autoFocus}
|
||||||
onChange={this.props.onChange}
|
onChange={this.props.onChange}
|
||||||
|
|
|
@ -36,7 +36,7 @@ interface IProps {
|
||||||
phoneNumber: string;
|
phoneNumber: string;
|
||||||
|
|
||||||
serverConfig: ValidatedServerConfig;
|
serverConfig: ValidatedServerConfig;
|
||||||
loginIncorrect?: boolean;
|
loginIncorrect: boolean;
|
||||||
disableSubmit?: boolean;
|
disableSubmit?: boolean;
|
||||||
busy?: boolean;
|
busy?: boolean;
|
||||||
|
|
||||||
|
@ -67,9 +67,9 @@ const enum LoginField {
|
||||||
* The email/username/phone fields are fully-controlled, the password field is not.
|
* The email/username/phone fields are fully-controlled, the password field is not.
|
||||||
*/
|
*/
|
||||||
export default class PasswordLogin extends React.PureComponent<IProps, IState> {
|
export default class PasswordLogin extends React.PureComponent<IProps, IState> {
|
||||||
private [LoginField.Email]: Field;
|
private [LoginField.Email]: Field | null;
|
||||||
private [LoginField.Phone]: Field;
|
private [LoginField.Phone]: Field | null;
|
||||||
private [LoginField.MatrixId]: Field;
|
private [LoginField.MatrixId]: Field | null;
|
||||||
|
|
||||||
public static defaultProps = {
|
public static defaultProps = {
|
||||||
onUsernameChanged: function () {},
|
onUsernameChanged: function () {},
|
||||||
|
@ -93,7 +93,7 @@ export default class PasswordLogin extends React.PureComponent<IProps, IState> {
|
||||||
private onForgotPasswordClick = (ev: ButtonEvent): void => {
|
private onForgotPasswordClick = (ev: ButtonEvent): void => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
this.props.onForgotPasswordClick();
|
this.props.onForgotPasswordClick?.();
|
||||||
};
|
};
|
||||||
|
|
||||||
private onSubmitForm = async (ev: SyntheticEvent): Promise<void> => {
|
private onSubmitForm = async (ev: SyntheticEvent): Promise<void> => {
|
||||||
|
@ -116,25 +116,25 @@ export default class PasswordLogin extends React.PureComponent<IProps, IState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
private onUsernameChanged = (ev: React.ChangeEvent<HTMLInputElement>): void => {
|
private onUsernameChanged = (ev: React.ChangeEvent<HTMLInputElement>): void => {
|
||||||
this.props.onUsernameChanged(ev.target.value);
|
this.props.onUsernameChanged?.(ev.target.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
private onUsernameBlur = (ev: React.FocusEvent<HTMLInputElement>): void => {
|
private onUsernameBlur = (ev: React.FocusEvent<HTMLInputElement>): void => {
|
||||||
this.props.onUsernameBlur(ev.target.value);
|
this.props.onUsernameBlur?.(ev.target.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
private onLoginTypeChange = (ev: React.ChangeEvent<HTMLSelectElement>): void => {
|
private onLoginTypeChange = (ev: React.ChangeEvent<HTMLSelectElement>): void => {
|
||||||
const loginType = ev.target.value as IState["loginType"];
|
const loginType = ev.target.value as IState["loginType"];
|
||||||
this.setState({ loginType });
|
this.setState({ loginType });
|
||||||
this.props.onUsernameChanged(""); // Reset because email and username use the same state
|
this.props.onUsernameChanged?.(""); // Reset because email and username use the same state
|
||||||
};
|
};
|
||||||
|
|
||||||
private onPhoneCountryChanged = (country: PhoneNumberCountryDefinition): void => {
|
private onPhoneCountryChanged = (country: PhoneNumberCountryDefinition): void => {
|
||||||
this.props.onPhoneCountryChanged(country.iso2);
|
this.props.onPhoneCountryChanged?.(country.iso2);
|
||||||
};
|
};
|
||||||
|
|
||||||
private onPhoneNumberChanged = (ev: React.ChangeEvent<HTMLInputElement>): void => {
|
private onPhoneNumberChanged = (ev: React.ChangeEvent<HTMLInputElement>): void => {
|
||||||
this.props.onPhoneNumberChanged(ev.target.value);
|
this.props.onPhoneNumberChanged?.(ev.target.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
private onPasswordChanged = (ev: React.ChangeEvent<HTMLInputElement>): void => {
|
private onPasswordChanged = (ev: React.ChangeEvent<HTMLInputElement>): void => {
|
||||||
|
@ -199,7 +199,7 @@ export default class PasswordLogin extends React.PureComponent<IProps, IState> {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private markFieldValid(fieldID: LoginField, valid: boolean): void {
|
private markFieldValid(fieldID: LoginField, valid?: boolean): void {
|
||||||
const { fieldValid } = this.state;
|
const { fieldValid } = this.state;
|
||||||
fieldValid[fieldID] = valid;
|
fieldValid[fieldID] = valid;
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -368,7 +368,7 @@ export default class PasswordLogin extends React.PureComponent<IProps, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
let forgotPasswordJsx;
|
let forgotPasswordJsx: JSX.Element | undefined;
|
||||||
|
|
||||||
if (this.props.onForgotPasswordClick) {
|
if (this.props.onForgotPasswordClick) {
|
||||||
forgotPasswordJsx = (
|
forgotPasswordJsx = (
|
||||||
|
|
|
@ -34,7 +34,7 @@ interface IProps {}
|
||||||
export default class Welcome extends React.PureComponent<IProps> {
|
export default class Welcome extends React.PureComponent<IProps> {
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
const pagesConfig = SdkConfig.getObject("embedded_pages");
|
const pagesConfig = SdkConfig.getObject("embedded_pages");
|
||||||
let pageUrl = null;
|
let pageUrl!: string;
|
||||||
if (pagesConfig) {
|
if (pagesConfig) {
|
||||||
pageUrl = pagesConfig.get("welcome_url");
|
pageUrl = pagesConfig.get("welcome_url");
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ interface IProps {
|
||||||
interface IState {
|
interface IState {
|
||||||
shouldLoadBackupStatus: boolean;
|
shouldLoadBackupStatus: boolean;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
backupInfo: IKeyBackupInfo;
|
backupInfo: IKeyBackupInfo | null;
|
||||||
error?: string;
|
error?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +55,6 @@ export default class LogoutDialog extends React.Component<IProps, IState> {
|
||||||
shouldLoadBackupStatus: shouldLoadBackupStatus,
|
shouldLoadBackupStatus: shouldLoadBackupStatus,
|
||||||
loading: shouldLoadBackupStatus,
|
loading: shouldLoadBackupStatus,
|
||||||
backupInfo: null,
|
backupInfo: null,
|
||||||
error: null,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (shouldLoadBackupStatus) {
|
if (shouldLoadBackupStatus) {
|
||||||
|
@ -103,14 +102,20 @@ export default class LogoutDialog extends React.Component<IProps, IState> {
|
||||||
// A key backup exists for this account, but the creating device is not
|
// A key backup exists for this account, but the creating device is not
|
||||||
// verified, so restore the backup which will give us the keys from it and
|
// verified, so restore the backup which will give us the keys from it and
|
||||||
// allow us to trust it (ie. upload keys to it)
|
// allow us to trust it (ie. upload keys to it)
|
||||||
Modal.createDialog(RestoreKeyBackupDialog, null, null, /* priority = */ false, /* static = */ true);
|
Modal.createDialog(
|
||||||
|
RestoreKeyBackupDialog,
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
/* priority = */ false,
|
||||||
|
/* static = */ true,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
Modal.createDialogAsync(
|
Modal.createDialogAsync(
|
||||||
import("../../../async-components/views/dialogs/security/CreateKeyBackupDialog") as unknown as Promise<
|
import("../../../async-components/views/dialogs/security/CreateKeyBackupDialog") as unknown as Promise<
|
||||||
ComponentType<{}>
|
ComponentType<{}>
|
||||||
>,
|
>,
|
||||||
null,
|
undefined,
|
||||||
null,
|
undefined,
|
||||||
/* priority = */ false,
|
/* priority = */ false,
|
||||||
/* static = */ true,
|
/* static = */ true,
|
||||||
);
|
);
|
||||||
|
|
|
@ -93,6 +93,7 @@ import { isLocalRoom } from "../../../../utils/localRoom/isLocalRoom";
|
||||||
import { shouldShowFeedback } from "../../../../utils/Feedback";
|
import { shouldShowFeedback } from "../../../../utils/Feedback";
|
||||||
import RoomAvatar from "../../avatars/RoomAvatar";
|
import RoomAvatar from "../../avatars/RoomAvatar";
|
||||||
import { useFeatureEnabled } from "../../../../hooks/useSettings";
|
import { useFeatureEnabled } from "../../../../hooks/useSettings";
|
||||||
|
import { filterBoolean } from "../../../../utils/arrays";
|
||||||
|
|
||||||
const MAX_RECENT_SEARCHES = 10;
|
const MAX_RECENT_SEARCHES = 10;
|
||||||
const SECTION_LIMIT = 50; // only show 50 results per section for performance reasons
|
const SECTION_LIMIT = 50; // only show 50 results per section for performance reasons
|
||||||
|
@ -173,13 +174,13 @@ const toPublicRoomResult = (publicRoom: IPublicRoomsChunkRoom): IPublicRoomResul
|
||||||
publicRoom,
|
publicRoom,
|
||||||
section: Section.PublicRooms,
|
section: Section.PublicRooms,
|
||||||
filter: [Filter.PublicRooms],
|
filter: [Filter.PublicRooms],
|
||||||
query: [
|
query: filterBoolean([
|
||||||
publicRoom.room_id.toLowerCase(),
|
publicRoom.room_id.toLowerCase(),
|
||||||
publicRoom.canonical_alias?.toLowerCase(),
|
publicRoom.canonical_alias?.toLowerCase(),
|
||||||
publicRoom.name?.toLowerCase(),
|
publicRoom.name?.toLowerCase(),
|
||||||
sanitizeHtml(publicRoom.topic?.toLowerCase() ?? "", { allowedTags: [] }),
|
sanitizeHtml(publicRoom.topic?.toLowerCase() ?? "", { allowedTags: [] }),
|
||||||
...(publicRoom.aliases?.map((it) => it.toLowerCase()) || []),
|
...(publicRoom.aliases?.map((it) => it.toLowerCase()) || []),
|
||||||
].filter(Boolean) as string[],
|
]),
|
||||||
});
|
});
|
||||||
|
|
||||||
const toRoomResult = (room: Room): IRoomResult => {
|
const toRoomResult = (room: Room): IRoomResult => {
|
||||||
|
|
|
@ -32,7 +32,7 @@ interface IProps {
|
||||||
|
|
||||||
/* callback to update the value. Called with a single argument: the new
|
/* callback to update the value. Called with a single argument: the new
|
||||||
* value. */
|
* value. */
|
||||||
onSubmit?: (value: string) => Promise<{} | void>;
|
onSubmit: (value: string) => Promise<{} | void>;
|
||||||
|
|
||||||
/* should the input submit when focus is lost? */
|
/* should the input submit when focus is lost? */
|
||||||
blurToSubmit?: boolean;
|
blurToSubmit?: boolean;
|
||||||
|
@ -40,7 +40,7 @@ interface IProps {
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
busy: boolean;
|
busy: boolean;
|
||||||
errorString: string;
|
errorString: string | null;
|
||||||
value: string;
|
value: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ export default class EditableTextContainer extends React.Component<IProps, IStat
|
||||||
this.state = {
|
this.state = {
|
||||||
busy: false,
|
busy: false,
|
||||||
errorString: null,
|
errorString: null,
|
||||||
value: props.initialValue,
|
value: props.initialValue ?? "",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -113,7 +113,7 @@ const SSOButton: React.FC<ISSOButtonProps> = ({
|
||||||
brandClass = `mx_SSOButton_brand_${brandName}`;
|
brandClass = `mx_SSOButton_brand_${brandName}`;
|
||||||
icon = <img src={brandIcon} height="24" width="24" alt={brandName} />;
|
icon = <img src={brandIcon} height="24" width="24" alt={brandName} />;
|
||||||
} else if (typeof idp?.icon === "string" && idp.icon.startsWith("mxc://")) {
|
} else if (typeof idp?.icon === "string" && idp.icon.startsWith("mxc://")) {
|
||||||
const src = mediaFromMxc(idp.icon, matrixClient).getSquareThumbnailHttp(24);
|
const src = mediaFromMxc(idp.icon, matrixClient).getSquareThumbnailHttp(24) ?? undefined;
|
||||||
icon = <img src={src} height="24" width="24" alt={idp.name} />;
|
icon = <img src={src} height="24" width="24" alt={idp.name} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React, { ReactNode } from "react";
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
|
|
||||||
import EventIndexPeg from "../../../indexing/EventIndexPeg";
|
import EventIndexPeg from "../../../indexing/EventIndexPeg";
|
||||||
|
@ -31,13 +31,13 @@ export enum WarningKind {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
isRoomEncrypted: boolean;
|
isRoomEncrypted?: boolean;
|
||||||
kind: WarningKind;
|
kind: WarningKind;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function SearchWarning({ isRoomEncrypted, kind }: IProps): JSX.Element {
|
export default function SearchWarning({ isRoomEncrypted, kind }: IProps): JSX.Element {
|
||||||
if (!isRoomEncrypted) return null;
|
if (!isRoomEncrypted) return <></>;
|
||||||
if (EventIndexPeg.get()) return null;
|
if (EventIndexPeg.get()) return <></>;
|
||||||
|
|
||||||
if (EventIndexPeg.error) {
|
if (EventIndexPeg.error) {
|
||||||
return (
|
return (
|
||||||
|
@ -69,8 +69,8 @@ export default function SearchWarning({ isRoomEncrypted, kind }: IProps): JSX.El
|
||||||
const brand = SdkConfig.get("brand");
|
const brand = SdkConfig.get("brand");
|
||||||
const desktopBuilds = SdkConfig.getObject("desktop_builds");
|
const desktopBuilds = SdkConfig.getObject("desktop_builds");
|
||||||
|
|
||||||
let text = null;
|
let text: ReactNode | undefined;
|
||||||
let logo = null;
|
let logo: JSX.Element | undefined;
|
||||||
if (desktopBuilds.get("available")) {
|
if (desktopBuilds.get("available")) {
|
||||||
logo = <img src={desktopBuilds.get("logo")} />;
|
logo = <img src={desktopBuilds.get("logo")} />;
|
||||||
const buildUrl = desktopBuilds.get("url");
|
const buildUrl = desktopBuilds.get("url");
|
||||||
|
@ -116,7 +116,7 @@ export default function SearchWarning({ isRoomEncrypted, kind }: IProps): JSX.El
|
||||||
// for safety
|
// for safety
|
||||||
if (!text) {
|
if (!text) {
|
||||||
logger.warn("Unknown desktop builds warning kind: ", kind);
|
logger.warn("Unknown desktop builds warning kind: ", kind);
|
||||||
return null;
|
return <></>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -26,7 +26,7 @@ interface IResult {
|
||||||
text: string;
|
text: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IRule<T, D = void> {
|
interface IRule<T, D = undefined> {
|
||||||
key: string;
|
key: string;
|
||||||
final?: boolean;
|
final?: boolean;
|
||||||
skip?(this: T, data: Data, derivedData: D): boolean;
|
skip?(this: T, data: Data, derivedData: D): boolean;
|
||||||
|
@ -90,14 +90,12 @@ export default function withValidation<T = void, D = void>({
|
||||||
{ value, focused, allowEmpty = true }: IFieldState,
|
{ value, focused, allowEmpty = true }: IFieldState,
|
||||||
): Promise<IValidationResult> {
|
): Promise<IValidationResult> {
|
||||||
if (!value && allowEmpty) {
|
if (!value && allowEmpty) {
|
||||||
return {
|
return {};
|
||||||
valid: null,
|
|
||||||
feedback: null,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = { value, allowEmpty };
|
const data = { value, allowEmpty };
|
||||||
const derivedData: D | undefined = deriveData ? await deriveData.call(this, data) : undefined;
|
// We know that if deriveData is set then D will not be undefined
|
||||||
|
const derivedData: D = (await deriveData?.call(this, data)) as D;
|
||||||
|
|
||||||
const results: IResult[] = [];
|
const results: IResult[] = [];
|
||||||
let valid = true;
|
let valid = true;
|
||||||
|
@ -149,10 +147,7 @@ export default function withValidation<T = void, D = void>({
|
||||||
|
|
||||||
// Hide feedback when not focused
|
// Hide feedback when not focused
|
||||||
if (!focused) {
|
if (!focused) {
|
||||||
return {
|
return { valid };
|
||||||
valid,
|
|
||||||
feedback: null,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let details;
|
let details;
|
||||||
|
|
|
@ -38,7 +38,7 @@ interface IProps {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
emojis: IEmoji[];
|
emojis: IEmoji[];
|
||||||
selectedEmojis: Set<string>;
|
selectedEmojis?: Set<string>;
|
||||||
heightBefore: number;
|
heightBefore: number;
|
||||||
viewportHeight: number;
|
viewportHeight: number;
|
||||||
scrollTop: number;
|
scrollTop: number;
|
||||||
|
|
|
@ -26,6 +26,7 @@ import Search from "./Search";
|
||||||
import Preview from "./Preview";
|
import Preview from "./Preview";
|
||||||
import QuickReactions from "./QuickReactions";
|
import QuickReactions from "./QuickReactions";
|
||||||
import Category, { ICategory, CategoryKey } from "./Category";
|
import Category, { ICategory, CategoryKey } from "./Category";
|
||||||
|
import { filterBoolean } from "../../../utils/arrays";
|
||||||
|
|
||||||
export const CATEGORY_HEADER_HEIGHT = 20;
|
export const CATEGORY_HEADER_HEIGHT = 20;
|
||||||
export const EMOJI_HEIGHT = 35;
|
export const EMOJI_HEIGHT = 35;
|
||||||
|
@ -62,13 +63,12 @@ class EmojiPicker extends React.Component<IProps, IState> {
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
filter: "",
|
filter: "",
|
||||||
previewEmoji: null,
|
|
||||||
scrollTop: 0,
|
scrollTop: 0,
|
||||||
viewportHeight: 280,
|
viewportHeight: 280,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Convert recent emoji characters to emoji data, removing unknowns and duplicates
|
// Convert recent emoji characters to emoji data, removing unknowns and duplicates
|
||||||
this.recentlyUsed = Array.from(new Set(recent.get().map(getEmojiFromUnicode).filter(Boolean)));
|
this.recentlyUsed = Array.from(new Set(filterBoolean(recent.get().map(getEmojiFromUnicode))));
|
||||||
this.memoizedDataByCategory = {
|
this.memoizedDataByCategory = {
|
||||||
recent: this.recentlyUsed,
|
recent: this.recentlyUsed,
|
||||||
...DATA_BY_CATEGORY,
|
...DATA_BY_CATEGORY,
|
||||||
|
@ -230,9 +230,9 @@ class EmojiPicker extends React.Component<IProps, IState> {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
private onHoverEmojiEnd = (emoji: IEmoji): void => {
|
private onHoverEmojiEnd = (): void => {
|
||||||
this.setState({
|
this.setState({
|
||||||
previewEmoji: null,
|
previewEmoji: undefined,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -42,9 +42,7 @@ interface IState {
|
||||||
class QuickReactions extends React.Component<IProps, IState> {
|
class QuickReactions extends React.Component<IProps, IState> {
|
||||||
public constructor(props: IProps) {
|
public constructor(props: IProps) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {};
|
||||||
hover: null,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private onMouseEnter = (emoji: IEmoji): void => {
|
private onMouseEnter = (emoji: IEmoji): void => {
|
||||||
|
@ -55,7 +53,7 @@ class QuickReactions extends React.Component<IProps, IState> {
|
||||||
|
|
||||||
private onMouseLeave = (): void => {
|
private onMouseLeave = (): void => {
|
||||||
this.setState({
|
this.setState({
|
||||||
hover: null,
|
hover: undefined,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -77,8 +77,8 @@ class ReactionPicker extends React.Component<IProps, IState> {
|
||||||
if (!this.props.reactions) {
|
if (!this.props.reactions) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
const userId = MatrixClientPeg.get().getUserId();
|
const userId = MatrixClientPeg.get().getUserId()!;
|
||||||
const myAnnotations = this.props.reactions.getAnnotationsBySender()[userId] || new Set<MatrixEvent>();
|
const myAnnotations = this.props.reactions.getAnnotationsBySender()?.[userId] ?? new Set<MatrixEvent>();
|
||||||
return Object.fromEntries(
|
return Object.fromEntries(
|
||||||
[...myAnnotations]
|
[...myAnnotations]
|
||||||
.filter((event) => !event.isRedacted())
|
.filter((event) => !event.isRedacted())
|
||||||
|
@ -97,9 +97,9 @@ class ReactionPicker extends React.Component<IProps, IState> {
|
||||||
this.props.onFinished();
|
this.props.onFinished();
|
||||||
const myReactions = this.getReactions();
|
const myReactions = this.getReactions();
|
||||||
if (myReactions.hasOwnProperty(reaction)) {
|
if (myReactions.hasOwnProperty(reaction)) {
|
||||||
if (this.props.mxEvent.isRedacted() || !this.context.canSelfRedact) return;
|
if (this.props.mxEvent.isRedacted() || !this.context.canSelfRedact) return false;
|
||||||
|
|
||||||
MatrixClientPeg.get().redactEvent(this.props.mxEvent.getRoomId(), myReactions[reaction]);
|
MatrixClientPeg.get().redactEvent(this.props.mxEvent.getRoomId()!, myReactions[reaction]);
|
||||||
dis.dispatch<FocusComposerPayload>({
|
dis.dispatch<FocusComposerPayload>({
|
||||||
action: Action.FocusAComposer,
|
action: Action.FocusAComposer,
|
||||||
context: this.context.timelineRenderingType,
|
context: this.context.timelineRenderingType,
|
||||||
|
@ -107,7 +107,7 @@ class ReactionPicker extends React.Component<IProps, IState> {
|
||||||
// Tell the emoji picker not to bump this in the more frequently used list.
|
// Tell the emoji picker not to bump this in the more frequently used list.
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
MatrixClientPeg.get().sendEvent(this.props.mxEvent.getRoomId(), EventType.Reaction, {
|
MatrixClientPeg.get().sendEvent(this.props.mxEvent.getRoomId()!, EventType.Reaction, {
|
||||||
"m.relates_to": {
|
"m.relates_to": {
|
||||||
rel_type: RelationType.Annotation,
|
rel_type: RelationType.Annotation,
|
||||||
event_id: this.props.mxEvent.getId(),
|
event_id: this.props.mxEvent.getId(),
|
||||||
|
|
|
@ -32,7 +32,7 @@ class Search extends React.PureComponent<IProps> {
|
||||||
|
|
||||||
public componentDidMount(): void {
|
public componentDidMount(): void {
|
||||||
// For some reason, neither the autoFocus nor just calling focus() here worked, so here's a window.setTimeout
|
// For some reason, neither the autoFocus nor just calling focus() here worked, so here's a window.setTimeout
|
||||||
window.setTimeout(() => this.inputRef.current.focus(), 0);
|
window.setTimeout(() => this.inputRef.current?.focus(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private onKeyDown = (ev: React.KeyboardEvent): void => {
|
private onKeyDown = (ev: React.KeyboardEvent): void => {
|
||||||
|
|
|
@ -73,7 +73,7 @@ export default class DateSeparator extends React.Component<IProps, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentWillUnmount(): void {
|
public componentWillUnmount(): void {
|
||||||
SettingsStore.unwatchSetting(this.settingWatcherRef);
|
if (this.settingWatcherRef) SettingsStore.unwatchSetting(this.settingWatcherRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
private onContextMenuOpenClick = (e: React.MouseEvent): void => {
|
private onContextMenuOpenClick = (e: React.MouseEvent): void => {
|
||||||
|
@ -89,7 +89,7 @@ export default class DateSeparator extends React.Component<IProps, IState> {
|
||||||
|
|
||||||
private closeMenu = (): void => {
|
private closeMenu = (): void => {
|
||||||
this.setState({
|
this.setState({
|
||||||
contextMenuPosition: null,
|
contextMenuPosition: undefined,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -181,7 +181,7 @@ export default class DateSeparator extends React.Component<IProps, IState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
private renderJumpToDateMenu(): React.ReactElement {
|
private renderJumpToDateMenu(): React.ReactElement {
|
||||||
let contextMenu: JSX.Element;
|
let contextMenu: JSX.Element | undefined;
|
||||||
if (this.state.contextMenuPosition) {
|
if (this.state.contextMenuPosition) {
|
||||||
contextMenu = (
|
contextMenu = (
|
||||||
<IconizedContextMenu
|
<IconizedContextMenu
|
||||||
|
|
|
@ -241,7 +241,7 @@ export class MessageComposer extends React.Component<IProps, IState> {
|
||||||
|
|
||||||
private waitForOwnMember(): void {
|
private waitForOwnMember(): void {
|
||||||
// If we have the member already, do that
|
// If we have the member already, do that
|
||||||
const me = this.props.room.getMember(MatrixClientPeg.get().getUserId());
|
const me = this.props.room.getMember(MatrixClientPeg.get().getUserId()!);
|
||||||
if (me) {
|
if (me) {
|
||||||
this.setState({ me });
|
this.setState({ me });
|
||||||
return;
|
return;
|
||||||
|
@ -250,14 +250,14 @@ export class MessageComposer extends React.Component<IProps, IState> {
|
||||||
// The members should already be loading, and loadMembersIfNeeded
|
// The members should already be loading, and loadMembersIfNeeded
|
||||||
// will return the promise for the existing operation
|
// will return the promise for the existing operation
|
||||||
this.props.room.loadMembersIfNeeded().then(() => {
|
this.props.room.loadMembersIfNeeded().then(() => {
|
||||||
const me = this.props.room.getMember(MatrixClientPeg.get().getUserId());
|
const me = this.props.room.getMember(MatrixClientPeg.get().getUserId()!);
|
||||||
this.setState({ me });
|
this.setState({ me });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentWillUnmount(): void {
|
public componentWillUnmount(): void {
|
||||||
VoiceRecordingStore.instance.off(UPDATE_EVENT, this.onVoiceStoreUpdate);
|
VoiceRecordingStore.instance.off(UPDATE_EVENT, this.onVoiceStoreUpdate);
|
||||||
dis.unregister(this.dispatcherRef);
|
if (this.dispatcherRef) dis.unregister(this.dispatcherRef);
|
||||||
UIStore.instance.stopTrackingElementDimensions(`MessageComposer${this.instanceId}`);
|
UIStore.instance.stopTrackingElementDimensions(`MessageComposer${this.instanceId}`);
|
||||||
UIStore.instance.removeListener(`MessageComposer${this.instanceId}`, this.onResize);
|
UIStore.instance.removeListener(`MessageComposer${this.instanceId}`, this.onResize);
|
||||||
|
|
||||||
|
@ -268,12 +268,12 @@ export class MessageComposer extends React.Component<IProps, IState> {
|
||||||
private onTombstoneClick = (ev: ButtonEvent): void => {
|
private onTombstoneClick = (ev: ButtonEvent): void => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
|
||||||
const replacementRoomId = this.context.tombstone.getContent()["replacement_room"];
|
const replacementRoomId = this.context.tombstone?.getContent()["replacement_room"];
|
||||||
const replacementRoom = MatrixClientPeg.get().getRoom(replacementRoomId);
|
const replacementRoom = MatrixClientPeg.get().getRoom(replacementRoomId);
|
||||||
let createEventId = null;
|
let createEventId: string | undefined;
|
||||||
if (replacementRoom) {
|
if (replacementRoom) {
|
||||||
const createEvent = replacementRoom.currentState.getStateEvents(EventType.RoomCreate, "");
|
const createEvent = replacementRoom.currentState.getStateEvents(EventType.RoomCreate, "");
|
||||||
if (createEvent && createEvent.getId()) createEventId = createEvent.getId();
|
if (createEvent?.getId()) createEventId = createEvent.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
const viaServers = [this.context.tombstone.getSender().split(":").slice(1).join(":")];
|
const viaServers = [this.context.tombstone.getSender().split(":").slice(1).join(":")];
|
||||||
|
@ -408,7 +408,7 @@ export class MessageComposer extends React.Component<IProps, IState> {
|
||||||
|
|
||||||
private onRecordingEndingSoon = ({ secondsLeft }: { secondsLeft: number }): void => {
|
private onRecordingEndingSoon = ({ secondsLeft }: { secondsLeft: number }): void => {
|
||||||
this.setState({ recordingTimeLeftSeconds: secondsLeft });
|
this.setState({ recordingTimeLeftSeconds: secondsLeft });
|
||||||
window.setTimeout(() => this.setState({ recordingTimeLeftSeconds: null }), 3000);
|
window.setTimeout(() => this.setState({ recordingTimeLeftSeconds: undefined }), 3000);
|
||||||
};
|
};
|
||||||
|
|
||||||
private setStickerPickerOpen = (isStickerPickerOpen: boolean): void => {
|
private setStickerPickerOpen = (isStickerPickerOpen: boolean): void => {
|
||||||
|
|
|
@ -38,7 +38,7 @@ export function StatelessNotificationBadge({ symbol, count, color, ...props }: P
|
||||||
|
|
||||||
// Don't show a badge if we don't need to
|
// Don't show a badge if we don't need to
|
||||||
if (color === NotificationColor.None || (hideBold && color == NotificationColor.Bold)) {
|
if (color === NotificationColor.None || (hideBold && color == NotificationColor.Bold)) {
|
||||||
return null;
|
return <></>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasUnreadCount = color >= NotificationColor.Grey && (!!count || !!symbol);
|
const hasUnreadCount = color >= NotificationColor.Grey && (!!count || !!symbol);
|
||||||
|
@ -54,8 +54,8 @@ export function StatelessNotificationBadge({ symbol, count, color, ...props }: P
|
||||||
mx_NotificationBadge_visible: isEmptyBadge ? true : hasUnreadCount,
|
mx_NotificationBadge_visible: isEmptyBadge ? true : hasUnreadCount,
|
||||||
mx_NotificationBadge_highlighted: color >= NotificationColor.Red,
|
mx_NotificationBadge_highlighted: color >= NotificationColor.Red,
|
||||||
mx_NotificationBadge_dot: isEmptyBadge,
|
mx_NotificationBadge_dot: isEmptyBadge,
|
||||||
mx_NotificationBadge_2char: symbol?.length > 0 && symbol?.length < 3,
|
mx_NotificationBadge_2char: symbol && symbol.length > 0 && symbol.length < 3,
|
||||||
mx_NotificationBadge_3char: symbol?.length > 2,
|
mx_NotificationBadge_3char: symbol && symbol.length > 2,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (props.onClick) {
|
if (props.onClick) {
|
||||||
|
|
|
@ -62,7 +62,7 @@ export default class PinnedEventTile extends React.Component<IProps> {
|
||||||
eventId: string,
|
eventId: string,
|
||||||
relationType: RelationType | string,
|
relationType: RelationType | string,
|
||||||
eventType: EventType | string,
|
eventType: EventType | string,
|
||||||
): Relations => {
|
): Relations | undefined => {
|
||||||
if (eventId === this.props.event.getId()) {
|
if (eventId === this.props.event.getId()) {
|
||||||
return this.relations.get(relationType)?.get(eventType);
|
return this.relations.get(relationType)?.get(eventType);
|
||||||
}
|
}
|
||||||
|
@ -71,7 +71,7 @@ export default class PinnedEventTile extends React.Component<IProps> {
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
const sender = this.props.event.getSender();
|
const sender = this.props.event.getSender();
|
||||||
|
|
||||||
let unpinButton = null;
|
let unpinButton: JSX.Element | undefined;
|
||||||
if (this.props.onUnpinClicked) {
|
if (this.props.onUnpinClicked) {
|
||||||
unpinButton = (
|
unpinButton = (
|
||||||
<AccessibleTooltipButton
|
<AccessibleTooltipButton
|
||||||
|
|
|
@ -66,7 +66,7 @@ export function determineAvatarPosition(index: number, max: number): IAvatarPosi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function readReceiptTooltip(members: string[], hasMore: boolean): string | null {
|
export function readReceiptTooltip(members: string[], hasMore: boolean): string | undefined {
|
||||||
if (hasMore) {
|
if (hasMore) {
|
||||||
return _t("%(members)s and more", {
|
return _t("%(members)s and more", {
|
||||||
members: members.join(", "),
|
members: members.join(", "),
|
||||||
|
@ -78,8 +78,6 @@ export function readReceiptTooltip(members: string[], hasMore: boolean): string
|
||||||
});
|
});
|
||||||
} else if (members.length) {
|
} else if (members.length) {
|
||||||
return members[0];
|
return members[0];
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,7 +132,7 @@ export function ReadReceiptGroup({
|
||||||
const { hidden, position } = determineAvatarPosition(index, maxAvatars);
|
const { hidden, position } = determineAvatarPosition(index, maxAvatars);
|
||||||
|
|
||||||
const userId = receipt.userId;
|
const userId = receipt.userId;
|
||||||
let readReceiptInfo: IReadReceiptInfo;
|
let readReceiptInfo: IReadReceiptInfo | undefined;
|
||||||
|
|
||||||
if (readReceiptMap) {
|
if (readReceiptMap) {
|
||||||
readReceiptInfo = readReceiptMap[userId];
|
readReceiptInfo = readReceiptMap[userId];
|
||||||
|
@ -161,7 +159,7 @@ export function ReadReceiptGroup({
|
||||||
})
|
})
|
||||||
.reverse();
|
.reverse();
|
||||||
|
|
||||||
let remText: JSX.Element;
|
let remText: JSX.Element | undefined;
|
||||||
const remainder = readReceipts.length - maxAvatars;
|
const remainder = readReceipts.length - maxAvatars;
|
||||||
if (remainder > 0) {
|
if (remainder > 0) {
|
||||||
remText = (
|
remText = (
|
||||||
|
|
|
@ -133,7 +133,7 @@ export default class ReadReceiptMarker extends React.PureComponent<IProps, IStat
|
||||||
|
|
||||||
target.top = 0;
|
target.top = 0;
|
||||||
target.right = 0;
|
target.right = 0;
|
||||||
target.parent = null;
|
target.parent = undefined;
|
||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
// this is the mx_ReadReceiptsGroup
|
// this is the mx_ReadReceiptsGroup
|
||||||
|
@ -146,7 +146,7 @@ export default class ReadReceiptMarker extends React.PureComponent<IProps, IStat
|
||||||
|
|
||||||
target.top = 0;
|
target.top = 0;
|
||||||
target.right = 0;
|
target.right = 0;
|
||||||
target.parent = null;
|
target.parent = undefined;
|
||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,7 +179,7 @@ export default class ReadReceiptMarker extends React.PureComponent<IProps, IStat
|
||||||
: // treat new RRs as though they were off the top of the screen
|
: // treat new RRs as though they were off the top of the screen
|
||||||
-READ_AVATAR_SIZE;
|
-READ_AVATAR_SIZE;
|
||||||
|
|
||||||
const startStyles = [];
|
const startStyles: IReadReceiptMarkerStyle[] = [];
|
||||||
if (oldInfo?.right) {
|
if (oldInfo?.right) {
|
||||||
startStyles.push({
|
startStyles.push({
|
||||||
top: oldPosition - newPosition,
|
top: oldPosition - newPosition,
|
||||||
|
@ -210,7 +210,7 @@ export default class ReadReceiptMarker extends React.PureComponent<IProps, IStat
|
||||||
return (
|
return (
|
||||||
<NodeAnimator startStyles={this.state.startStyles}>
|
<NodeAnimator startStyles={this.state.startStyles}>
|
||||||
<MemberAvatar
|
<MemberAvatar
|
||||||
member={this.props.member}
|
member={this.props.member ?? null}
|
||||||
fallbackUserId={this.props.fallbackUserId}
|
fallbackUserId={this.props.fallbackUserId}
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
aria-live="off"
|
aria-live="off"
|
||||||
|
|
|
@ -168,7 +168,7 @@ export default class ReplyTile extends React.PureComponent<IProps> {
|
||||||
...this.props,
|
...this.props,
|
||||||
|
|
||||||
// overrides
|
// overrides
|
||||||
ref: null,
|
ref: undefined,
|
||||||
showUrlPreview: false,
|
showUrlPreview: false,
|
||||||
overrideBodyTypes: msgtypeOverrides,
|
overrideBodyTypes: msgtypeOverrides,
|
||||||
overrideEventTypes: evOverrides,
|
overrideEventTypes: evOverrides,
|
||||||
|
|
|
@ -37,5 +37,5 @@ export function RoomContextDetails<T extends keyof ReactHTML>({ room, component,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return <></>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,7 +62,7 @@ const RoomInfoLine: FC<IProps> = ({ room }) => {
|
||||||
roomType = room.isSpaceRoom() ? _t("Private space") : _t("Private room");
|
roomType = room.isSpaceRoom() ? _t("Private space") : _t("Private room");
|
||||||
}
|
}
|
||||||
|
|
||||||
let members: JSX.Element;
|
let members: JSX.Element | undefined;
|
||||||
if (membership === "invite" && summary) {
|
if (membership === "invite" && summary) {
|
||||||
// Don't trust local state and instead use the summary API
|
// Don't trust local state and instead use the summary API
|
||||||
members = (
|
members = (
|
||||||
|
|
|
@ -117,7 +117,7 @@ const auxButtonContextMenuPosition = (handle: RefObject<HTMLDivElement>): MenuPr
|
||||||
|
|
||||||
const DmAuxButton: React.FC<IAuxButtonProps> = ({ tabIndex, dispatcher = defaultDispatcher }) => {
|
const DmAuxButton: React.FC<IAuxButtonProps> = ({ tabIndex, dispatcher = defaultDispatcher }) => {
|
||||||
const [menuDisplayed, handle, openMenu, closeMenu] = useContextMenu<HTMLDivElement>();
|
const [menuDisplayed, handle, openMenu, closeMenu] = useContextMenu<HTMLDivElement>();
|
||||||
const activeSpace: Room = useEventEmitterState(SpaceStore.instance, UPDATE_SELECTED_SPACE, () => {
|
const activeSpace = useEventEmitterState(SpaceStore.instance, UPDATE_SELECTED_SPACE, () => {
|
||||||
return SpaceStore.instance.activeSpaceRoom;
|
return SpaceStore.instance.activeSpaceRoom;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -125,7 +125,7 @@ const DmAuxButton: React.FC<IAuxButtonProps> = ({ tabIndex, dispatcher = default
|
||||||
const showInviteUsers = shouldShowComponent(UIComponent.InviteUsers);
|
const showInviteUsers = shouldShowComponent(UIComponent.InviteUsers);
|
||||||
|
|
||||||
if (activeSpace && (showCreateRooms || showInviteUsers)) {
|
if (activeSpace && (showCreateRooms || showInviteUsers)) {
|
||||||
let contextMenu: JSX.Element;
|
let contextMenu: JSX.Element | undefined;
|
||||||
if (menuDisplayed) {
|
if (menuDisplayed) {
|
||||||
const canInvite = shouldShowSpaceInvite(activeSpace);
|
const canInvite = shouldShowSpaceInvite(activeSpace);
|
||||||
|
|
||||||
|
@ -208,7 +208,7 @@ const DmAuxButton: React.FC<IAuxButtonProps> = ({ tabIndex, dispatcher = default
|
||||||
|
|
||||||
const UntaggedAuxButton: React.FC<IAuxButtonProps> = ({ tabIndex }) => {
|
const UntaggedAuxButton: React.FC<IAuxButtonProps> = ({ tabIndex }) => {
|
||||||
const [menuDisplayed, handle, openMenu, closeMenu] = useContextMenu<HTMLDivElement>();
|
const [menuDisplayed, handle, openMenu, closeMenu] = useContextMenu<HTMLDivElement>();
|
||||||
const activeSpace = useEventEmitterState<Room>(SpaceStore.instance, UPDATE_SELECTED_SPACE, () => {
|
const activeSpace = useEventEmitterState<Room | null>(SpaceStore.instance, UPDATE_SELECTED_SPACE, () => {
|
||||||
return SpaceStore.instance.activeSpaceRoom;
|
return SpaceStore.instance.activeSpaceRoom;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -216,11 +216,11 @@ const UntaggedAuxButton: React.FC<IAuxButtonProps> = ({ tabIndex }) => {
|
||||||
const videoRoomsEnabled = useFeatureEnabled("feature_video_rooms");
|
const videoRoomsEnabled = useFeatureEnabled("feature_video_rooms");
|
||||||
const elementCallVideoRoomsEnabled = useFeatureEnabled("feature_element_call_video_rooms");
|
const elementCallVideoRoomsEnabled = useFeatureEnabled("feature_element_call_video_rooms");
|
||||||
|
|
||||||
let contextMenuContent: JSX.Element | null = null;
|
let contextMenuContent: JSX.Element | undefined;
|
||||||
if (menuDisplayed && activeSpace) {
|
if (menuDisplayed && activeSpace) {
|
||||||
const canAddRooms = activeSpace.currentState.maySendStateEvent(
|
const canAddRooms = activeSpace.currentState.maySendStateEvent(
|
||||||
EventType.SpaceChild,
|
EventType.SpaceChild,
|
||||||
MatrixClientPeg.get().getUserId(),
|
MatrixClientPeg.get().getUserId()!,
|
||||||
);
|
);
|
||||||
|
|
||||||
contextMenuContent = (
|
contextMenuContent = (
|
||||||
|
@ -469,13 +469,13 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
|
||||||
SpaceStore.instance.off(UPDATE_SUGGESTED_ROOMS, this.updateSuggestedRooms);
|
SpaceStore.instance.off(UPDATE_SUGGESTED_ROOMS, this.updateSuggestedRooms);
|
||||||
RoomListStore.instance.off(LISTS_UPDATE_EVENT, this.updateLists);
|
RoomListStore.instance.off(LISTS_UPDATE_EVENT, this.updateLists);
|
||||||
SettingsStore.unwatchSetting(this.favouriteMessageWatcher);
|
SettingsStore.unwatchSetting(this.favouriteMessageWatcher);
|
||||||
defaultDispatcher.unregister(this.dispatcherRef);
|
if (this.dispatcherRef) defaultDispatcher.unregister(this.dispatcherRef);
|
||||||
SdkContextClass.instance.roomViewStore.off(UPDATE_EVENT, this.onRoomViewStoreUpdate);
|
SdkContextClass.instance.roomViewStore.off(UPDATE_EVENT, this.onRoomViewStoreUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
private onRoomViewStoreUpdate = (): void => {
|
private onRoomViewStoreUpdate = (): void => {
|
||||||
this.setState({
|
this.setState({
|
||||||
currentRoomId: SdkContextClass.instance.roomViewStore.getRoomId(),
|
currentRoomId: SdkContextClass.instance.roomViewStore.getRoomId() ?? undefined,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -629,7 +629,7 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
|
||||||
Object.values(RoomListStore.instance.orderedLists).every((list) => !list?.length);
|
Object.values(RoomListStore.instance.orderedLists).every((list) => !list?.length);
|
||||||
|
|
||||||
return TAG_ORDER.map((orderedTagId) => {
|
return TAG_ORDER.map((orderedTagId) => {
|
||||||
let extraTiles = null;
|
let extraTiles: ReactComponentElement<typeof ExtraTile>[] | undefined;
|
||||||
if (orderedTagId === DefaultTagID.Suggested) {
|
if (orderedTagId === DefaultTagID.Suggested) {
|
||||||
extraTiles = this.renderSuggestedRooms();
|
extraTiles = this.renderSuggestedRooms();
|
||||||
} else if (this.state.feature_favourite_messages && orderedTagId === DefaultTagID.SavedItems) {
|
} else if (this.state.feature_favourite_messages && orderedTagId === DefaultTagID.SavedItems) {
|
||||||
|
|
|
@ -137,12 +137,10 @@ const RoomListHeader: React.FC<IProps> = ({ onVisibilityChange }) => {
|
||||||
}
|
}
|
||||||
}, [closeMainMenu, canShowMainMenu, mainMenuDisplayed]);
|
}, [closeMainMenu, canShowMainMenu, mainMenuDisplayed]);
|
||||||
|
|
||||||
const spaceName = useTypedEventEmitterState(activeSpace, RoomEvent.Name, () => activeSpace?.name);
|
const spaceName = useTypedEventEmitterState(activeSpace ?? undefined, RoomEvent.Name, () => activeSpace?.name);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (onVisibilityChange) {
|
onVisibilityChange?.();
|
||||||
onVisibilityChange();
|
|
||||||
}
|
|
||||||
}, [onVisibilityChange]);
|
}, [onVisibilityChange]);
|
||||||
|
|
||||||
const canExploreRooms = shouldShowComponent(UIComponent.ExploreRooms);
|
const canExploreRooms = shouldShowComponent(UIComponent.ExploreRooms);
|
||||||
|
@ -151,7 +149,7 @@ const RoomListHeader: React.FC<IProps> = ({ onVisibilityChange }) => {
|
||||||
|
|
||||||
const hasPermissionToAddSpaceChild = activeSpace?.currentState?.maySendStateEvent(
|
const hasPermissionToAddSpaceChild = activeSpace?.currentState?.maySendStateEvent(
|
||||||
EventType.SpaceChild,
|
EventType.SpaceChild,
|
||||||
cli.getUserId(),
|
cli.getUserId()!,
|
||||||
);
|
);
|
||||||
const canAddSubRooms = hasPermissionToAddSpaceChild && canCreateRooms;
|
const canAddSubRooms = hasPermissionToAddSpaceChild && canCreateRooms;
|
||||||
const canAddSubSpaces = hasPermissionToAddSpaceChild && canCreateSpaces;
|
const canAddSubSpaces = hasPermissionToAddSpaceChild && canCreateSpaces;
|
||||||
|
@ -161,7 +159,7 @@ const RoomListHeader: React.FC<IProps> = ({ onVisibilityChange }) => {
|
||||||
// communities and spaces, but is at risk of no options on the Home tab.
|
// communities and spaces, but is at risk of no options on the Home tab.
|
||||||
const canShowPlusMenu = canCreateRooms || canExploreRooms || canCreateSpaces || activeSpace;
|
const canShowPlusMenu = canCreateRooms || canExploreRooms || canCreateSpaces || activeSpace;
|
||||||
|
|
||||||
let contextMenu: JSX.Element;
|
let contextMenu: JSX.Element | undefined;
|
||||||
if (mainMenuDisplayed && mainMenuHandle.current) {
|
if (mainMenuDisplayed && mainMenuHandle.current) {
|
||||||
let ContextMenuComponent;
|
let ContextMenuComponent;
|
||||||
if (activeSpace) {
|
if (activeSpace) {
|
||||||
|
@ -179,7 +177,7 @@ const RoomListHeader: React.FC<IProps> = ({ onVisibilityChange }) => {
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else if (plusMenuDisplayed && activeSpace) {
|
} else if (plusMenuDisplayed && activeSpace) {
|
||||||
let inviteOption: JSX.Element;
|
let inviteOption: JSX.Element | undefined;
|
||||||
if (shouldShowSpaceInvite(activeSpace)) {
|
if (shouldShowSpaceInvite(activeSpace)) {
|
||||||
inviteOption = (
|
inviteOption = (
|
||||||
<IconizedContextMenuOption
|
<IconizedContextMenuOption
|
||||||
|
@ -195,8 +193,8 @@ const RoomListHeader: React.FC<IProps> = ({ onVisibilityChange }) => {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let newRoomOptions: JSX.Element;
|
let newRoomOptions: JSX.Element | undefined;
|
||||||
if (activeSpace?.currentState.maySendStateEvent(EventType.RoomAvatar, cli.getUserId())) {
|
if (activeSpace?.currentState.maySendStateEvent(EventType.RoomAvatar, cli.getUserId()!)) {
|
||||||
newRoomOptions = (
|
newRoomOptions = (
|
||||||
<>
|
<>
|
||||||
<IconizedContextMenuOption
|
<IconizedContextMenuOption
|
||||||
|
@ -265,7 +263,9 @@ const RoomListHeader: React.FC<IProps> = ({ onVisibilityChange }) => {
|
||||||
closePlusMenu();
|
closePlusMenu();
|
||||||
}}
|
}}
|
||||||
disabled={!canAddSubRooms}
|
disabled={!canAddSubRooms}
|
||||||
tooltip={!canAddSubRooms && _t("You do not have permissions to add rooms to this space")}
|
tooltip={
|
||||||
|
!canAddSubRooms ? _t("You do not have permissions to add rooms to this space") : undefined
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
{canCreateSpaces && (
|
{canCreateSpaces && (
|
||||||
<IconizedContextMenuOption
|
<IconizedContextMenuOption
|
||||||
|
@ -278,7 +278,11 @@ const RoomListHeader: React.FC<IProps> = ({ onVisibilityChange }) => {
|
||||||
closePlusMenu();
|
closePlusMenu();
|
||||||
}}
|
}}
|
||||||
disabled={!canAddSubSpaces}
|
disabled={!canAddSubSpaces}
|
||||||
tooltip={!canAddSubSpaces && _t("You do not have permissions to add spaces to this space")}
|
tooltip={
|
||||||
|
!canAddSubSpaces
|
||||||
|
? _t("You do not have permissions to add spaces to this space")
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<BetaPill />
|
<BetaPill />
|
||||||
</IconizedContextMenuOption>
|
</IconizedContextMenuOption>
|
||||||
|
@ -287,8 +291,8 @@ const RoomListHeader: React.FC<IProps> = ({ onVisibilityChange }) => {
|
||||||
</IconizedContextMenu>
|
</IconizedContextMenu>
|
||||||
);
|
);
|
||||||
} else if (plusMenuDisplayed) {
|
} else if (plusMenuDisplayed) {
|
||||||
let newRoomOpts: JSX.Element;
|
let newRoomOpts: JSX.Element | undefined;
|
||||||
let joinRoomOpt: JSX.Element;
|
let joinRoomOpt: JSX.Element | undefined;
|
||||||
|
|
||||||
if (canCreateRooms) {
|
if (canCreateRooms) {
|
||||||
newRoomOpts = (
|
newRoomOpts = (
|
||||||
|
@ -366,7 +370,7 @@ const RoomListHeader: React.FC<IProps> = ({ onVisibilityChange }) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
let title: string;
|
let title: string;
|
||||||
if (activeSpace) {
|
if (activeSpace && spaceName) {
|
||||||
title = spaceName;
|
title = spaceName;
|
||||||
} else {
|
} else {
|
||||||
title = getMetaSpaceName(spaceKey as MetaSpace, allRoomsInHome);
|
title = getMetaSpaceName(spaceKey as MetaSpace, allRoomsInHome);
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React, { ReactNode } from "react";
|
||||||
import { Room } from "matrix-js-sdk/src/models/room";
|
import { Room } from "matrix-js-sdk/src/models/room";
|
||||||
import { MatrixError } from "matrix-js-sdk/src/http-api";
|
import { MatrixError } from "matrix-js-sdk/src/http-api";
|
||||||
import { EventType, RoomType } from "matrix-js-sdk/src/@types/event";
|
import { EventType, RoomType } from "matrix-js-sdk/src/@types/event";
|
||||||
|
@ -221,25 +221,27 @@ export default class RoomPreviewBar extends React.Component<IProps, IState> {
|
||||||
return { memberName, reason };
|
return { memberName, reason };
|
||||||
}
|
}
|
||||||
|
|
||||||
private joinRule(): JoinRule {
|
private joinRule(): JoinRule | null {
|
||||||
return this.props.room?.currentState
|
return (
|
||||||
.getStateEvents(EventType.RoomJoinRules, "")
|
this.props.room?.currentState
|
||||||
?.getContent<IJoinRuleEventContent>().join_rule;
|
.getStateEvents(EventType.RoomJoinRules, "")
|
||||||
|
?.getContent<IJoinRuleEventContent>().join_rule ?? null
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private getMyMember(): RoomMember {
|
private getMyMember(): RoomMember | null {
|
||||||
return this.props.room?.getMember(MatrixClientPeg.get().getUserId());
|
return this.props.room?.getMember(MatrixClientPeg.get().getUserId()!) ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private getInviteMember(): RoomMember {
|
private getInviteMember(): RoomMember | null {
|
||||||
const { room } = this.props;
|
const { room } = this.props;
|
||||||
if (!room) {
|
if (!room) {
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
const myUserId = MatrixClientPeg.get().getUserId();
|
const myUserId = MatrixClientPeg.get().getUserId()!;
|
||||||
const inviteEvent = room.currentState.getMember(myUserId);
|
const inviteEvent = room.currentState.getMember(myUserId);
|
||||||
if (!inviteEvent) {
|
if (!inviteEvent) {
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
const inviterUserId = inviteEvent.events.member.getSender();
|
const inviterUserId = inviteEvent.events.member.getSender();
|
||||||
return room.currentState.getMember(inviterUserId);
|
return room.currentState.getMember(inviterUserId);
|
||||||
|
@ -282,15 +284,15 @@ export default class RoomPreviewBar extends React.Component<IProps, IState> {
|
||||||
const isSpace = this.props.room?.isSpaceRoom() ?? this.props.oobData?.roomType === RoomType.Space;
|
const isSpace = this.props.room?.isSpaceRoom() ?? this.props.oobData?.roomType === RoomType.Space;
|
||||||
|
|
||||||
let showSpinner = false;
|
let showSpinner = false;
|
||||||
let title;
|
let title: string | undefined;
|
||||||
let subTitle;
|
let subTitle: string | ReactNode[] | undefined;
|
||||||
let reasonElement;
|
let reasonElement: JSX.Element | undefined;
|
||||||
let primaryActionHandler;
|
let primaryActionHandler: (() => void) | undefined;
|
||||||
let primaryActionLabel;
|
let primaryActionLabel: string | undefined;
|
||||||
let secondaryActionHandler;
|
let secondaryActionHandler: (() => void) | undefined;
|
||||||
let secondaryActionLabel;
|
let secondaryActionLabel: string | undefined;
|
||||||
let footer;
|
let footer: JSX.Element | undefined;
|
||||||
const extraComponents = [];
|
const extraComponents: JSX.Element[] = [];
|
||||||
|
|
||||||
const messageCase = this.getMessageCase();
|
const messageCase = this.getMessageCase();
|
||||||
switch (messageCase) {
|
switch (messageCase) {
|
||||||
|
@ -351,7 +353,7 @@ export default class RoomPreviewBar extends React.Component<IProps, IState> {
|
||||||
} else {
|
} else {
|
||||||
title = _t("You were removed by %(memberName)s", { memberName });
|
title = _t("You were removed by %(memberName)s", { memberName });
|
||||||
}
|
}
|
||||||
subTitle = reason ? _t("Reason: %(reason)s", { reason }) : null;
|
subTitle = reason ? _t("Reason: %(reason)s", { reason }) : undefined;
|
||||||
|
|
||||||
if (isSpace) {
|
if (isSpace) {
|
||||||
primaryActionLabel = _t("Forget this space");
|
primaryActionLabel = _t("Forget this space");
|
||||||
|
@ -376,7 +378,7 @@ export default class RoomPreviewBar extends React.Component<IProps, IState> {
|
||||||
} else {
|
} else {
|
||||||
title = _t("You were banned by %(memberName)s", { memberName });
|
title = _t("You were banned by %(memberName)s", { memberName });
|
||||||
}
|
}
|
||||||
subTitle = reason ? _t("Reason: %(reason)s", { reason }) : null;
|
subTitle = reason ? _t("Reason: %(reason)s", { reason }) : undefined;
|
||||||
if (isSpace) {
|
if (isSpace) {
|
||||||
primaryActionLabel = _t("Forget this space");
|
primaryActionLabel = _t("Forget this space");
|
||||||
} else {
|
} else {
|
||||||
|
@ -499,7 +501,7 @@ export default class RoomPreviewBar extends React.Component<IProps, IState> {
|
||||||
primaryActionLabel = _t("Accept");
|
primaryActionLabel = _t("Accept");
|
||||||
}
|
}
|
||||||
|
|
||||||
const myUserId = MatrixClientPeg.get().getUserId();
|
const myUserId = MatrixClientPeg.get().getUserId()!;
|
||||||
const member = this.props.room?.currentState.getMember(myUserId);
|
const member = this.props.room?.currentState.getMember(myUserId);
|
||||||
const memberEventContent = member?.events.member?.getContent();
|
const memberEventContent = member?.events.member?.getContent();
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ import { Dispatcher } from "flux";
|
||||||
import { Enable, Resizable } from "re-resizable";
|
import { Enable, Resizable } from "re-resizable";
|
||||||
import { Direction } from "re-resizable/lib/resizer";
|
import { Direction } from "re-resizable/lib/resizer";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { ComponentType, createRef, ReactComponentElement } from "react";
|
import { ComponentType, createRef, ReactComponentElement, ReactNode } from "react";
|
||||||
|
|
||||||
import { polyfillTouchEvent } from "../../../@types/polyfill";
|
import { polyfillTouchEvent } from "../../../@types/polyfill";
|
||||||
import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts";
|
import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts";
|
||||||
|
@ -95,7 +95,7 @@ interface ResizeDelta {
|
||||||
type PartialDOMRect = Pick<DOMRect, "left" | "top" | "height">;
|
type PartialDOMRect = Pick<DOMRect, "left" | "top" | "height">;
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
contextMenuPosition: PartialDOMRect;
|
contextMenuPosition?: PartialDOMRect;
|
||||||
isResizing: boolean;
|
isResizing: boolean;
|
||||||
isExpanded: boolean; // used for the for expand of the sublist when the room list is being filtered
|
isExpanded: boolean; // used for the for expand of the sublist when the room list is being filtered
|
||||||
height: number;
|
height: number;
|
||||||
|
@ -123,7 +123,6 @@ export default class RoomSublist extends React.Component<IProps, IState> {
|
||||||
this.heightAtStart = 0;
|
this.heightAtStart = 0;
|
||||||
this.notificationState = RoomNotificationStateStore.instance.getListState(this.props.tagId);
|
this.notificationState = RoomNotificationStateStore.instance.getListState(this.props.tagId);
|
||||||
this.state = {
|
this.state = {
|
||||||
contextMenuPosition: null,
|
|
||||||
isResizing: false,
|
isResizing: false,
|
||||||
isExpanded: !this.layout.isCollapsed,
|
isExpanded: !this.layout.isCollapsed,
|
||||||
height: 0, // to be fixed in a moment, we need `rooms` to calculate this.
|
height: 0, // to be fixed in a moment, we need `rooms` to calculate this.
|
||||||
|
@ -160,10 +159,7 @@ export default class RoomSublist extends React.Component<IProps, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private get extraTiles(): ReactComponentElement<typeof ExtraTile>[] | null {
|
private get extraTiles(): ReactComponentElement<typeof ExtraTile>[] | null {
|
||||||
if (this.props.extraTiles) {
|
return this.props.extraTiles ?? null;
|
||||||
return this.props.extraTiles;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private get numTiles(): number {
|
private get numTiles(): number {
|
||||||
|
@ -390,7 +386,7 @@ export default class RoomSublist extends React.Component<IProps, IState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
private onCloseMenu = (): void => {
|
private onCloseMenu = (): void => {
|
||||||
this.setState({ contextMenuPosition: null });
|
this.setState({ contextMenuPosition: undefined });
|
||||||
};
|
};
|
||||||
|
|
||||||
private onUnreadFirstChanged = (): void => {
|
private onUnreadFirstChanged = (): void => {
|
||||||
|
@ -506,7 +502,7 @@ export default class RoomSublist extends React.Component<IProps, IState> {
|
||||||
// On ArrowLeft go to the sublist header
|
// On ArrowLeft go to the sublist header
|
||||||
case KeyBindingAction.ArrowLeft:
|
case KeyBindingAction.ArrowLeft:
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
this.headerButton.current.focus();
|
this.headerButton.current?.focus();
|
||||||
break;
|
break;
|
||||||
// Consume ArrowRight so it doesn't cause focus to get sent to composer
|
// Consume ArrowRight so it doesn't cause focus to get sent to composer
|
||||||
case KeyBindingAction.ArrowRight:
|
case KeyBindingAction.ArrowRight:
|
||||||
|
@ -557,10 +553,10 @@ export default class RoomSublist extends React.Component<IProps, IState> {
|
||||||
return tiles;
|
return tiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderMenu(): React.ReactElement {
|
private renderMenu(): ReactNode {
|
||||||
if (this.props.tagId === DefaultTagID.Suggested || this.props.tagId === DefaultTagID.SavedItems) return null; // not sortable
|
if (this.props.tagId === DefaultTagID.Suggested || this.props.tagId === DefaultTagID.SavedItems) return null; // not sortable
|
||||||
|
|
||||||
let contextMenu = null;
|
let contextMenu: JSX.Element | undefined;
|
||||||
if (this.state.contextMenuPosition) {
|
if (this.state.contextMenuPosition) {
|
||||||
let isAlphabetical = RoomListStore.instance.getTagSorting(this.props.tagId) === SortAlgorithm.Alphabetic;
|
let isAlphabetical = RoomListStore.instance.getTagSorting(this.props.tagId) === SortAlgorithm.Alphabetic;
|
||||||
let isUnreadFirst = RoomListStore.instance.getListOrder(this.props.tagId) === ListAlgorithm.Importance;
|
let isUnreadFirst = RoomListStore.instance.getListOrder(this.props.tagId) === ListAlgorithm.Importance;
|
||||||
|
@ -571,7 +567,7 @@ export default class RoomSublist extends React.Component<IProps, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invites don't get some nonsense options, so only add them if we have to.
|
// Invites don't get some nonsense options, so only add them if we have to.
|
||||||
let otherSections = null;
|
let otherSections: JSX.Element | undefined;
|
||||||
if (this.props.tagId !== DefaultTagID.Invite) {
|
if (this.props.tagId !== DefaultTagID.Invite) {
|
||||||
otherSections = (
|
otherSections = (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
|
@ -665,7 +661,7 @@ export default class RoomSublist extends React.Component<IProps, IState> {
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
let addRoomButton = null;
|
let addRoomButton: JSX.Element | undefined;
|
||||||
if (this.props.AuxButtonComponent) {
|
if (this.props.AuxButtonComponent) {
|
||||||
const AuxButtonComponent = this.props.AuxButtonComponent;
|
const AuxButtonComponent = this.props.AuxButtonComponent;
|
||||||
addRoomButton = <AuxButtonComponent tabIndex={tabIndex} />;
|
addRoomButton = <AuxButtonComponent tabIndex={tabIndex} />;
|
||||||
|
@ -747,7 +743,7 @@ export default class RoomSublist extends React.Component<IProps, IState> {
|
||||||
mx_RoomSublist_hidden: hidden,
|
mx_RoomSublist_hidden: hidden,
|
||||||
});
|
});
|
||||||
|
|
||||||
let content = null;
|
let content: JSX.Element | undefined;
|
||||||
if (this.state.roomsLoading) {
|
if (this.state.roomsLoading) {
|
||||||
content = <div className="mx_RoomSublist_skeletonUI" />;
|
content = <div className="mx_RoomSublist_skeletonUI" />;
|
||||||
} else if (visibleTiles.length > 0 && this.props.forceExpanded) {
|
} else if (visibleTiles.length > 0 && this.props.forceExpanded) {
|
||||||
|
@ -773,7 +769,7 @@ export default class RoomSublist extends React.Component<IProps, IState> {
|
||||||
// If we're hiding rooms, show a 'show more' button to the user. This button
|
// If we're hiding rooms, show a 'show more' button to the user. This button
|
||||||
// floats above the resize handle, if we have one present. If the user has all
|
// floats above the resize handle, if we have one present. If the user has all
|
||||||
// tiles visible, it becomes 'show less'.
|
// tiles visible, it becomes 'show less'.
|
||||||
let showNButton = null;
|
let showNButton: JSX.Element | undefined;
|
||||||
const hasMoreSlidingSync =
|
const hasMoreSlidingSync =
|
||||||
this.slidingSyncMode && RoomListStore.instance.getCount(this.props.tagId) > this.state.rooms.length;
|
this.slidingSyncMode && RoomListStore.instance.getCount(this.props.tagId) > this.state.rooms.length;
|
||||||
|
|
||||||
|
@ -786,7 +782,7 @@ export default class RoomSublist extends React.Component<IProps, IState> {
|
||||||
numMissing = RoomListStore.instance.getCount(this.props.tagId) - amountFullyShown;
|
numMissing = RoomListStore.instance.getCount(this.props.tagId) - amountFullyShown;
|
||||||
}
|
}
|
||||||
const label = _t("Show %(count)s more", { count: numMissing });
|
const label = _t("Show %(count)s more", { count: numMissing });
|
||||||
let showMoreText = <span className="mx_RoomSublist_showNButtonText">{label}</span>;
|
let showMoreText: ReactNode = <span className="mx_RoomSublist_showNButtonText">{label}</span>;
|
||||||
if (this.props.isMinimized) showMoreText = null;
|
if (this.props.isMinimized) showMoreText = null;
|
||||||
showNButton = (
|
showNButton = (
|
||||||
<RovingAccessibleButton
|
<RovingAccessibleButton
|
||||||
|
@ -804,7 +800,7 @@ export default class RoomSublist extends React.Component<IProps, IState> {
|
||||||
} else if (this.numTiles > this.layout.defaultVisibleTiles) {
|
} else if (this.numTiles > this.layout.defaultVisibleTiles) {
|
||||||
// we have all tiles visible - add a button to show less
|
// we have all tiles visible - add a button to show less
|
||||||
const label = _t("Show less");
|
const label = _t("Show less");
|
||||||
let showLessText = <span className="mx_RoomSublist_showNButtonText">{label}</span>;
|
let showLessText: ReactNode = <span className="mx_RoomSublist_showNButtonText">{label}</span>;
|
||||||
if (this.props.isMinimized) showLessText = null;
|
if (this.props.isMinimized) showLessText = null;
|
||||||
showNButton = (
|
showNButton = (
|
||||||
<RovingAccessibleButton
|
<RovingAccessibleButton
|
||||||
|
|
|
@ -72,13 +72,13 @@ export default class SearchBar extends React.Component<IProps, IState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
private searchIfQuery(): void {
|
private searchIfQuery(): void {
|
||||||
if (this.searchTerm.current.value) {
|
if (this.searchTerm.current?.value) {
|
||||||
this.onSearch();
|
this.onSearch();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private onSearch = (): void => {
|
private onSearch = (): void => {
|
||||||
if (!this.searchTerm.current.value.trim()) return;
|
if (!this.searchTerm.current?.value.trim()) return;
|
||||||
this.props.onSearch(this.searchTerm.current.value, this.state.scope);
|
this.props.onSearch(this.searchTerm.current.value, this.state.scope);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -368,7 +368,12 @@ export class SendMessageComposer extends React.Component<ISendMessageComposerPro
|
||||||
this.props.relation?.rel_type === THREAD_RELATION_TYPE.name ? this.props.relation?.event_id : null;
|
this.props.relation?.rel_type === THREAD_RELATION_TYPE.name ? this.props.relation?.event_id : null;
|
||||||
|
|
||||||
let commandSuccessful: boolean;
|
let commandSuccessful: boolean;
|
||||||
[content, commandSuccessful] = await runSlashCommand(cmd, args, this.props.room.roomId, threadId);
|
[content, commandSuccessful] = await runSlashCommand(
|
||||||
|
cmd,
|
||||||
|
args,
|
||||||
|
this.props.room.roomId,
|
||||||
|
threadId ?? null,
|
||||||
|
);
|
||||||
if (!commandSuccessful) {
|
if (!commandSuccessful) {
|
||||||
return; // errored
|
return; // errored
|
||||||
}
|
}
|
||||||
|
@ -425,7 +430,7 @@ export class SendMessageComposer extends React.Component<ISendMessageComposerPro
|
||||||
|
|
||||||
const prom = doMaybeLocalRoomAction(
|
const prom = doMaybeLocalRoomAction(
|
||||||
roomId,
|
roomId,
|
||||||
(actualRoomId: string) => this.props.mxClient.sendMessage(actualRoomId, threadId, content),
|
(actualRoomId: string) => this.props.mxClient.sendMessage(actualRoomId, threadId ?? null, content!),
|
||||||
this.props.mxClient,
|
this.props.mxClient,
|
||||||
);
|
);
|
||||||
if (replyToEvent) {
|
if (replyToEvent) {
|
||||||
|
@ -439,7 +444,7 @@ export class SendMessageComposer extends React.Component<ISendMessageComposerPro
|
||||||
}
|
}
|
||||||
dis.dispatch({ action: "message_sent" });
|
dis.dispatch({ action: "message_sent" });
|
||||||
CHAT_EFFECTS.forEach((effect) => {
|
CHAT_EFFECTS.forEach((effect) => {
|
||||||
if (containsEmoji(content, effect.emojis)) {
|
if (containsEmoji(content!, effect.emojis)) {
|
||||||
// For initial threads launch, chat effects are disabled
|
// For initial threads launch, chat effects are disabled
|
||||||
// see #19731
|
// see #19731
|
||||||
const isNotThread = this.props.relation?.rel_type !== THREAD_RELATION_TYPE.name;
|
const isNotThread = this.props.relation?.rel_type !== THREAD_RELATION_TYPE.name;
|
||||||
|
|
|
@ -52,9 +52,9 @@ interface IProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
imError: string;
|
imError: string | null;
|
||||||
stickerpickerWidget: IWidgetEvent;
|
stickerpickerWidget: IWidgetEvent | null;
|
||||||
widgetId: string;
|
widgetId: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Stickerpicker extends React.PureComponent<IProps, IState> {
|
export default class Stickerpicker extends React.PureComponent<IProps, IState> {
|
||||||
|
@ -71,7 +71,7 @@ export default class Stickerpicker extends React.PureComponent<IProps, IState> {
|
||||||
private popoverWidth = 300;
|
private popoverWidth = 300;
|
||||||
private popoverHeight = 300;
|
private popoverHeight = 300;
|
||||||
// This is loaded by _acquireScalarClient on an as-needed basis.
|
// This is loaded by _acquireScalarClient on an as-needed basis.
|
||||||
private scalarClient: ScalarAuthClient = null;
|
private scalarClient: ScalarAuthClient | null = null;
|
||||||
|
|
||||||
public constructor(props: IProps) {
|
public constructor(props: IProps) {
|
||||||
super(props);
|
super(props);
|
||||||
|
@ -82,13 +82,13 @@ export default class Stickerpicker extends React.PureComponent<IProps, IState> {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private acquireScalarClient(): Promise<void | ScalarAuthClient> {
|
private async acquireScalarClient(): Promise<void | undefined | null | ScalarAuthClient> {
|
||||||
if (this.scalarClient) return Promise.resolve(this.scalarClient);
|
if (this.scalarClient) return Promise.resolve(this.scalarClient);
|
||||||
// TODO: Pick the right manager for the widget
|
// TODO: Pick the right manager for the widget
|
||||||
if (IntegrationManagers.sharedInstance().hasManager()) {
|
if (IntegrationManagers.sharedInstance().hasManager()) {
|
||||||
this.scalarClient = IntegrationManagers.sharedInstance().getPrimaryManager().getScalarClient();
|
this.scalarClient = IntegrationManagers.sharedInstance().getPrimaryManager()?.getScalarClient() ?? null;
|
||||||
return this.scalarClient
|
return this.scalarClient
|
||||||
.connect()
|
?.connect()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
return this.scalarClient;
|
return this.scalarClient;
|
||||||
|
@ -170,21 +170,14 @@ export default class Stickerpicker extends React.PureComponent<IProps, IState> {
|
||||||
private updateWidget = (): void => {
|
private updateWidget = (): void => {
|
||||||
const stickerpickerWidget = WidgetUtils.getStickerpickerWidgets()[0];
|
const stickerpickerWidget = WidgetUtils.getStickerpickerWidgets()[0];
|
||||||
if (!stickerpickerWidget) {
|
if (!stickerpickerWidget) {
|
||||||
Stickerpicker.currentWidget = null;
|
Stickerpicker.currentWidget = undefined;
|
||||||
this.setState({ stickerpickerWidget: null, widgetId: null });
|
this.setState({ stickerpickerWidget: null, widgetId: null });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentWidget = Stickerpicker.currentWidget;
|
const currentWidget = Stickerpicker.currentWidget;
|
||||||
let currentUrl = null;
|
const currentUrl = currentWidget?.content?.url ?? null;
|
||||||
if (currentWidget && currentWidget.content && currentWidget.content.url) {
|
const newUrl = stickerpickerWidget?.content?.url ?? null;
|
||||||
currentUrl = currentWidget.content.url;
|
|
||||||
}
|
|
||||||
|
|
||||||
let newUrl = null;
|
|
||||||
if (stickerpickerWidget && stickerpickerWidget.content && stickerpickerWidget.content.url) {
|
|
||||||
newUrl = stickerpickerWidget.content.url;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newUrl !== currentUrl) {
|
if (newUrl !== currentUrl) {
|
||||||
// Destroy the existing frame so a new one can be created
|
// Destroy the existing frame so a new one can be created
|
||||||
|
@ -238,7 +231,7 @@ export default class Stickerpicker extends React.PureComponent<IProps, IState> {
|
||||||
private sendVisibilityToWidget(visible: boolean): void {
|
private sendVisibilityToWidget(visible: boolean): void {
|
||||||
if (!this.state.stickerpickerWidget) return;
|
if (!this.state.stickerpickerWidget) return;
|
||||||
const messaging = WidgetMessagingStore.instance.getMessagingForUid(
|
const messaging = WidgetMessagingStore.instance.getMessagingForUid(
|
||||||
WidgetUtils.calcWidgetUid(this.state.stickerpickerWidget.id, null),
|
WidgetUtils.calcWidgetUid(this.state.stickerpickerWidget.id),
|
||||||
);
|
);
|
||||||
if (messaging && visible !== this.prevSentVisibility) {
|
if (messaging && visible !== this.prevSentVisibility) {
|
||||||
messaging.updateVisibility(visible).catch((err) => {
|
messaging.updateVisibility(visible).catch((err) => {
|
||||||
|
@ -300,8 +293,8 @@ export default class Stickerpicker extends React.PureComponent<IProps, IState> {
|
||||||
room={this.props.room}
|
room={this.props.room}
|
||||||
threadId={this.props.threadId}
|
threadId={this.props.threadId}
|
||||||
fullWidth={true}
|
fullWidth={true}
|
||||||
userId={MatrixClientPeg.get().credentials.userId}
|
userId={MatrixClientPeg.get().credentials.userId!}
|
||||||
creatorUserId={stickerpickerWidget.sender || MatrixClientPeg.get().credentials.userId}
|
creatorUserId={stickerpickerWidget.sender || MatrixClientPeg.get().credentials.userId!}
|
||||||
waitForIframeLoad={true}
|
waitForIframeLoad={true}
|
||||||
showMenubar={true}
|
showMenubar={true}
|
||||||
onEditClick={this.launchManageIntegrations}
|
onEditClick={this.launchManageIntegrations}
|
||||||
|
@ -347,8 +340,8 @@ export default class Stickerpicker extends React.PureComponent<IProps, IState> {
|
||||||
private launchManageIntegrations = (): void => {
|
private launchManageIntegrations = (): void => {
|
||||||
// noinspection JSIgnoredPromiseFromCall
|
// noinspection JSIgnoredPromiseFromCall
|
||||||
IntegrationManagers.sharedInstance()
|
IntegrationManagers.sharedInstance()
|
||||||
.getPrimaryManager()
|
?.getPrimaryManager()
|
||||||
.open(this.props.room, `type_${WidgetType.STICKERPICKER.preferred}`, this.state.widgetId);
|
?.open(this.props.room, `type_${WidgetType.STICKERPICKER.preferred}`, this.state.widgetId ?? undefined);
|
||||||
};
|
};
|
||||||
|
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
|
|
|
@ -45,19 +45,19 @@ interface IState {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class ThirdPartyMemberInfo extends React.Component<IProps, IState> {
|
export default class ThirdPartyMemberInfo extends React.Component<IProps, IState> {
|
||||||
private room: Room;
|
private readonly room: Room | null;
|
||||||
|
|
||||||
public constructor(props: IProps) {
|
public constructor(props: IProps) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.room = MatrixClientPeg.get().getRoom(this.props.event.getRoomId());
|
this.room = MatrixClientPeg.get().getRoom(this.props.event.getRoomId());
|
||||||
const me = this.room.getMember(MatrixClientPeg.get().getUserId());
|
const me = this.room?.getMember(MatrixClientPeg.get().getUserId()!);
|
||||||
const powerLevels = this.room.currentState.getStateEvents("m.room.power_levels", "");
|
const powerLevels = this.room?.currentState.getStateEvents("m.room.power_levels", "");
|
||||||
|
|
||||||
let kickLevel = powerLevels ? powerLevels.getContent().kick : 50;
|
let kickLevel = powerLevels ? powerLevels.getContent().kick : 50;
|
||||||
if (typeof kickLevel !== "number") kickLevel = 50;
|
if (typeof kickLevel !== "number") kickLevel = 50;
|
||||||
|
|
||||||
const sender = this.room.getMember(this.props.event.getSender());
|
const sender = this.room?.getMember(this.props.event.getSender());
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
stateKey: this.props.event.getStateKey(),
|
stateKey: this.props.event.getStateKey(),
|
||||||
|
@ -121,7 +121,7 @@ export default class ThirdPartyMemberInfo extends React.Component<IProps, IState
|
||||||
};
|
};
|
||||||
|
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
let adminTools = null;
|
let adminTools: JSX.Element | undefined;
|
||||||
if (this.state.canKick && this.state.invited) {
|
if (this.state.canKick && this.state.invited) {
|
||||||
adminTools = (
|
adminTools = (
|
||||||
<div className="mx_MemberInfo_container">
|
<div className="mx_MemberInfo_container">
|
||||||
|
@ -135,8 +135,8 @@ export default class ThirdPartyMemberInfo extends React.Component<IProps, IState
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let scopeHeader;
|
let scopeHeader: JSX.Element | undefined;
|
||||||
if (this.room.isSpaceRoom()) {
|
if (this.room?.isSpaceRoom()) {
|
||||||
scopeHeader = (
|
scopeHeader = (
|
||||||
<div className="mx_RightPanel_scopeHeader">
|
<div className="mx_RightPanel_scopeHeader">
|
||||||
<RoomAvatar room={this.room} height={32} width={32} />
|
<RoomAvatar room={this.room} height={32} width={32} />
|
||||||
|
|
|
@ -77,18 +77,18 @@ interface IPreviewProps {
|
||||||
export const ThreadMessagePreview: React.FC<IPreviewProps> = ({ thread, showDisplayname = false }) => {
|
export const ThreadMessagePreview: React.FC<IPreviewProps> = ({ thread, showDisplayname = false }) => {
|
||||||
const cli = useContext(MatrixClientContext);
|
const cli = useContext(MatrixClientContext);
|
||||||
|
|
||||||
const lastReply = useTypedEventEmitterState(thread, ThreadEvent.Update, () => thread.replyToEvent);
|
const lastReply = useTypedEventEmitterState(thread, ThreadEvent.Update, () => thread.replyToEvent) ?? undefined;
|
||||||
// track the content as a means to regenerate the thread message preview upon edits & decryption
|
// track the content as a means to regenerate the thread message preview upon edits & decryption
|
||||||
const [content, setContent] = useState<IContent>(lastReply?.getContent());
|
const [content, setContent] = useState<IContent | undefined>(lastReply?.getContent());
|
||||||
useTypedEventEmitter(lastReply, MatrixEventEvent.Replaced, () => {
|
useTypedEventEmitter(lastReply, MatrixEventEvent.Replaced, () => {
|
||||||
setContent(lastReply.getContent());
|
setContent(lastReply!.getContent());
|
||||||
});
|
});
|
||||||
const awaitDecryption = lastReply?.shouldAttemptDecryption() || lastReply?.isBeingDecrypted();
|
const awaitDecryption = lastReply?.shouldAttemptDecryption() || lastReply?.isBeingDecrypted();
|
||||||
useTypedEventEmitter(awaitDecryption ? lastReply : null, MatrixEventEvent.Decrypted, () => {
|
useTypedEventEmitter(awaitDecryption ? lastReply : undefined, MatrixEventEvent.Decrypted, () => {
|
||||||
setContent(lastReply.getContent());
|
setContent(lastReply!.getContent());
|
||||||
});
|
});
|
||||||
|
|
||||||
const preview = useAsyncMemo(async (): Promise<string> => {
|
const preview = useAsyncMemo(async (): Promise<string | undefined> => {
|
||||||
if (!lastReply) return;
|
if (!lastReply) return;
|
||||||
await cli.decryptEventIfNeeded(lastReply);
|
await cli.decryptEventIfNeeded(lastReply);
|
||||||
return MessagePreviewStore.instance.generatePreviewForEvent(lastReply);
|
return MessagePreviewStore.instance.generatePreviewForEvent(lastReply);
|
||||||
|
|
|
@ -20,8 +20,8 @@ import { _t } from "../../../languageHandler";
|
||||||
import AccessibleButton from "../elements/AccessibleButton";
|
import AccessibleButton from "../elements/AccessibleButton";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
onScrollUpClick?: (e: React.MouseEvent) => void;
|
onScrollUpClick: (e: React.MouseEvent) => void;
|
||||||
onCloseClick?: (e: React.MouseEvent) => void;
|
onCloseClick: (e: React.MouseEvent) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class TopUnreadMessagesBar extends React.PureComponent<IProps> {
|
export default class TopUnreadMessagesBar extends React.PureComponent<IProps> {
|
||||||
|
|
|
@ -48,7 +48,7 @@ import { createVoiceMessageContent } from "../../../utils/createVoiceMessageCont
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
room: Room;
|
room: Room;
|
||||||
permalinkCreator?: RoomPermalinkCreator;
|
permalinkCreator: RoomPermalinkCreator;
|
||||||
relation?: IEventRelation;
|
relation?: IEventRelation;
|
||||||
replyToEvent?: MatrixEvent;
|
replyToEvent?: MatrixEvent;
|
||||||
}
|
}
|
||||||
|
@ -70,9 +70,7 @@ export default class VoiceRecordComposerTile extends React.PureComponent<IProps,
|
||||||
public constructor(props: IProps) {
|
public constructor(props: IProps) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {};
|
||||||
recorder: null, // no recording started by default
|
|
||||||
};
|
|
||||||
|
|
||||||
this.voiceRecordingId = VoiceRecordingStore.getVoiceRecordingId(this.props.room, this.props.relation);
|
this.voiceRecordingId = VoiceRecordingStore.getVoiceRecordingId(this.props.room, this.props.relation);
|
||||||
}
|
}
|
||||||
|
@ -163,7 +161,7 @@ export default class VoiceRecordComposerTile extends React.PureComponent<IProps,
|
||||||
await VoiceRecordingStore.instance.disposeRecording(this.voiceRecordingId);
|
await VoiceRecordingStore.instance.disposeRecording(this.voiceRecordingId);
|
||||||
|
|
||||||
// Reset back to no recording, which means no phase (ie: restart component entirely)
|
// Reset back to no recording, which means no phase (ie: restart component entirely)
|
||||||
this.setState({ recorder: null, recordingPhase: null, didUploadFail: false });
|
this.setState({ recorder: undefined, recordingPhase: undefined, didUploadFail: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
private onCancel = async (): Promise<void> => {
|
private onCancel = async (): Promise<void> => {
|
||||||
|
@ -220,7 +218,7 @@ export default class VoiceRecordComposerTile extends React.PureComponent<IProps,
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// stop any noises which might be happening
|
// stop any noises which might be happening
|
||||||
PlaybackManager.instance.pauseAllExcept(null);
|
PlaybackManager.instance.pauseAllExcept();
|
||||||
const recorder = VoiceRecordingStore.instance.startRecording(this.voiceRecordingId);
|
const recorder = VoiceRecordingStore.instance.startRecording(this.voiceRecordingId);
|
||||||
await recorder.start();
|
await recorder.start();
|
||||||
|
|
||||||
|
|
|
@ -89,7 +89,7 @@ export default class WhoIsTypingTile extends React.Component<IProps, IState> {
|
||||||
return WhoIsTypingTile.isVisible(this.state);
|
return WhoIsTypingTile.isVisible(this.state);
|
||||||
};
|
};
|
||||||
|
|
||||||
private onRoomTimeline = (event: MatrixEvent, room: Room | null): void => {
|
private onRoomTimeline = (event: MatrixEvent, room?: Room): void => {
|
||||||
if (room?.roomId === this.props.room.roomId) {
|
if (room?.roomId === this.props.room.roomId) {
|
||||||
const userId = event.getSender();
|
const userId = event.getSender();
|
||||||
// remove user from usersTyping
|
// remove user from usersTyping
|
||||||
|
|
|
@ -45,7 +45,7 @@ export function setCursorPositionAtTheEnd(element: HTMLElement): void {
|
||||||
const range = document.createRange();
|
const range = document.createRange();
|
||||||
range.selectNodeContents(element);
|
range.selectNodeContents(element);
|
||||||
range.collapse(false);
|
range.collapse(false);
|
||||||
const selection = document.getSelection();
|
const selection = document.getSelection()!;
|
||||||
selection.removeAllRanges();
|
selection.removeAllRanges();
|
||||||
selection.addRange(range);
|
selection.addRange(range);
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React, { ReactNode } from "react";
|
||||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||||
import { Room } from "matrix-js-sdk/src/models/room";
|
import { Room } from "matrix-js-sdk/src/models/room";
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
|
@ -79,13 +79,13 @@ export default class BridgeTile extends React.PureComponent<IProps> {
|
||||||
`Bridge info event ${this.props.ev.getId()} does not provide a 'bridgebot' key which` +
|
`Bridge info event ${this.props.ev.getId()} does not provide a 'bridgebot' key which` +
|
||||||
"is deprecated behaviour. Using sender for now.",
|
"is deprecated behaviour. Using sender for now.",
|
||||||
);
|
);
|
||||||
content.bridgebot = this.props.ev.getSender();
|
content.bridgebot = this.props.ev.getSender()!;
|
||||||
}
|
}
|
||||||
const { channel, network, protocol } = content;
|
const { channel, network, protocol } = content;
|
||||||
const protocolName = protocol.displayname || protocol.id;
|
const protocolName = protocol.displayname || protocol.id;
|
||||||
const channelName = channel.displayname || channel.id;
|
const channelName = channel.displayname || channel.id;
|
||||||
|
|
||||||
let creator = null;
|
let creator: JSX.Element | undefined;
|
||||||
if (content.creator) {
|
if (content.creator) {
|
||||||
creator = (
|
creator = (
|
||||||
<li>
|
<li>
|
||||||
|
@ -129,7 +129,7 @@ export default class BridgeTile extends React.PureComponent<IProps> {
|
||||||
let networkIcon;
|
let networkIcon;
|
||||||
|
|
||||||
if (protocol.avatar_url) {
|
if (protocol.avatar_url) {
|
||||||
const avatarUrl = mediaFromMxc(protocol.avatar_url).getSquareThumbnailHttp(64);
|
const avatarUrl = mediaFromMxc(protocol.avatar_url).getSquareThumbnailHttp(64) ?? undefined;
|
||||||
|
|
||||||
networkIcon = (
|
networkIcon = (
|
||||||
<BaseAvatar
|
<BaseAvatar
|
||||||
|
@ -145,7 +145,7 @@ export default class BridgeTile extends React.PureComponent<IProps> {
|
||||||
} else {
|
} else {
|
||||||
networkIcon = <div className="mx_RoomSettingsDialog_noProtocolIcon" />;
|
networkIcon = <div className="mx_RoomSettingsDialog_noProtocolIcon" />;
|
||||||
}
|
}
|
||||||
let networkItem = null;
|
let networkItem: ReactNode | undefined;
|
||||||
if (network) {
|
if (network) {
|
||||||
const networkName = network.displayname || network.id;
|
const networkName = network.displayname || network.id;
|
||||||
let networkLink = <span>{networkName}</span>;
|
let networkLink = <span>{networkName}</span>;
|
||||||
|
|
|
@ -24,8 +24,8 @@ export default class ChangeDisplayName extends React.Component {
|
||||||
private getDisplayName = async (): Promise<string> => {
|
private getDisplayName = async (): Promise<string> => {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
try {
|
try {
|
||||||
const res = await cli.getProfileInfo(cli.getUserId());
|
const res = await cli.getProfileInfo(cli.getUserId()!);
|
||||||
return res.displayname;
|
return res.displayname ?? "";
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error("Failed to fetch display name");
|
throw new Error("Failed to fetch display name");
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,12 +42,12 @@ enum Phase {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
onFinished?: (outcome: {
|
onFinished: (outcome: {
|
||||||
didSetEmail?: boolean;
|
didSetEmail?: boolean;
|
||||||
/** Was one or more other devices logged out whilst changing the password */
|
/** Was one or more other devices logged out whilst changing the password */
|
||||||
didLogoutOutOtherDevices: boolean;
|
didLogoutOutOtherDevices: boolean;
|
||||||
}) => void;
|
}) => void;
|
||||||
onError?: (error: { error: string }) => void;
|
onError: (error: { error: string }) => void;
|
||||||
rowClassName?: string;
|
rowClassName?: string;
|
||||||
buttonClassName?: string;
|
buttonClassName?: string;
|
||||||
buttonKind?: string;
|
buttonKind?: string;
|
||||||
|
@ -68,9 +68,9 @@ interface IState {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class ChangePassword extends React.Component<IProps, IState> {
|
export default class ChangePassword extends React.Component<IProps, IState> {
|
||||||
private [FIELD_OLD_PASSWORD]: Field;
|
private [FIELD_OLD_PASSWORD]: Field | null;
|
||||||
private [FIELD_NEW_PASSWORD]: Field;
|
private [FIELD_NEW_PASSWORD]: Field | null;
|
||||||
private [FIELD_NEW_PASSWORD_CONFIRM]: Field;
|
private [FIELD_NEW_PASSWORD_CONFIRM]: Field | null;
|
||||||
|
|
||||||
public static defaultProps: Partial<IProps> = {
|
public static defaultProps: Partial<IProps> = {
|
||||||
onFinished() {},
|
onFinished() {},
|
||||||
|
@ -154,7 +154,7 @@ export default class ChangePassword extends React.Component<IProps, IState> {
|
||||||
},
|
},
|
||||||
// TODO: Remove `user` once servers support proper UIA
|
// TODO: Remove `user` once servers support proper UIA
|
||||||
// See https://github.com/matrix-org/synapse/issues/5665
|
// See https://github.com/matrix-org/synapse/issues/5665
|
||||||
user: cli.credentials.userId,
|
user: cli.credentials.userId ?? undefined,
|
||||||
password: oldPassword,
|
password: oldPassword,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -195,7 +195,7 @@ export default class ChangePassword extends React.Component<IProps, IState> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private checkPassword(oldPass: string, newPass: string, confirmPass: string): { error: string } {
|
private checkPassword(oldPass: string, newPass: string, confirmPass: string): { error: string } | undefined {
|
||||||
if (newPass !== confirmPass) {
|
if (newPass !== confirmPass) {
|
||||||
return {
|
return {
|
||||||
error: _t("New passwords don't match"),
|
error: _t("New passwords don't match"),
|
||||||
|
@ -226,7 +226,7 @@ export default class ChangePassword extends React.Component<IProps, IState> {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
private markFieldValid(fieldID: FieldType, valid: boolean): void {
|
private markFieldValid(fieldID: FieldType, valid?: boolean): void {
|
||||||
const { fieldValid } = this.state;
|
const { fieldValid } = this.state;
|
||||||
fieldValid[fieldID] = valid;
|
fieldValid[fieldID] = valid;
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -367,7 +367,7 @@ export default class ChangePassword extends React.Component<IProps, IState> {
|
||||||
return Object.values(this.state.fieldValid).every(Boolean);
|
return Object.values(this.state.fieldValid).every(Boolean);
|
||||||
}
|
}
|
||||||
|
|
||||||
private findFirstInvalidField(fieldIDs: FieldType[]): Field {
|
private findFirstInvalidField(fieldIDs: FieldType[]): Field | null {
|
||||||
for (const fieldID of fieldIDs) {
|
for (const fieldID of fieldIDs) {
|
||||||
if (!this.state.fieldValid[fieldID] && this[fieldID]) {
|
if (!this.state.fieldValid[fieldID] && this[fieldID]) {
|
||||||
return this[fieldID];
|
return this[fieldID];
|
||||||
|
|
|
@ -75,7 +75,7 @@ export default class CrossSigningPanel extends React.PureComponent<{}, IState> {
|
||||||
|
|
||||||
private onBootstrapClick = (): void => {
|
private onBootstrapClick = (): void => {
|
||||||
if (this.state.crossSigningPrivateKeysInStorage) {
|
if (this.state.crossSigningPrivateKeysInStorage) {
|
||||||
Modal.createDialog(SetupEncryptionDialog, {}, null, /* priority = */ false, /* static = */ true);
|
Modal.createDialog(SetupEncryptionDialog, {}, undefined, /* priority = */ false, /* static = */ true);
|
||||||
} else {
|
} else {
|
||||||
// Trigger the flow to set up secure backup, which is what this will do when in
|
// Trigger the flow to set up secure backup, which is what this will do when in
|
||||||
// the appropriate state.
|
// the appropriate state.
|
||||||
|
@ -90,8 +90,8 @@ export default class CrossSigningPanel extends React.PureComponent<{}, IState> {
|
||||||
private async getUpdatedStatus(): Promise<void> {
|
private async getUpdatedStatus(): Promise<void> {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
const pkCache = cli.getCrossSigningCacheCallbacks();
|
const pkCache = cli.getCrossSigningCacheCallbacks();
|
||||||
const crossSigning = cli.crypto.crossSigningInfo;
|
const crossSigning = cli.crypto!.crossSigningInfo;
|
||||||
const secretStorage = cli.crypto.secretStorage;
|
const secretStorage = cli.crypto!.secretStorage;
|
||||||
const crossSigningPublicKeysOnDevice = Boolean(crossSigning.getId());
|
const crossSigningPublicKeysOnDevice = Boolean(crossSigning.getId());
|
||||||
const crossSigningPrivateKeysInStorage = Boolean(await crossSigning.isStoredInSecretStorage(secretStorage));
|
const crossSigningPrivateKeysInStorage = Boolean(await crossSigning.isStoredInSecretStorage(secretStorage));
|
||||||
const masterPrivateKeyCached = !!(pkCache && (await pkCache.getCrossSigningKeyCache("master")));
|
const masterPrivateKeyCached = !!(pkCache && (await pkCache.getCrossSigningKeyCache("master")));
|
||||||
|
@ -209,7 +209,7 @@ export default class CrossSigningPanel extends React.PureComponent<{}, IState> {
|
||||||
selfSigningPrivateKeyCached &&
|
selfSigningPrivateKeyCached &&
|
||||||
userSigningPrivateKeyCached;
|
userSigningPrivateKeyCached;
|
||||||
|
|
||||||
const actions = [];
|
const actions: JSX.Element[] = [];
|
||||||
|
|
||||||
// TODO: determine how better to expose this to users in addition to prompts at login/toast
|
// TODO: determine how better to expose this to users in addition to prompts at login/toast
|
||||||
if (!keysExistEverywhere && homeserverSupportsCrossSigning) {
|
if (!keysExistEverywhere && homeserverSupportsCrossSigning) {
|
||||||
|
|
|
@ -64,7 +64,7 @@ export default class DevicesPanel extends React.Component<IProps, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private onDevicesUpdated = (users: string[]): void => {
|
private onDevicesUpdated = (users: string[]): void => {
|
||||||
if (!users.includes(this.context.getUserId())) return;
|
if (!users.includes(this.context.getUserId()!)) return;
|
||||||
this.loadDevices();
|
this.loadDevices();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -252,9 +252,9 @@ export default class DevicesPanel extends React.Component<IProps, IState> {
|
||||||
const otherDevices = devices.filter((device) => device.device_id !== myDeviceId);
|
const otherDevices = devices.filter((device) => device.device_id !== myDeviceId);
|
||||||
otherDevices.sort(this.deviceCompare);
|
otherDevices.sort(this.deviceCompare);
|
||||||
|
|
||||||
const verifiedDevices = [];
|
const verifiedDevices: IMyDevice[] = [];
|
||||||
const unverifiedDevices = [];
|
const unverifiedDevices: IMyDevice[] = [];
|
||||||
const nonCryptoDevices = [];
|
const nonCryptoDevices: IMyDevice[] = [];
|
||||||
for (const device of otherDevices) {
|
for (const device of otherDevices) {
|
||||||
const verified = this.isDeviceVerified(device);
|
const verified = this.isDeviceVerified(device);
|
||||||
if (verified === true) {
|
if (verified === true) {
|
||||||
|
@ -271,7 +271,7 @@ export default class DevicesPanel extends React.Component<IProps, IState> {
|
||||||
return <React.Fragment />;
|
return <React.Fragment />;
|
||||||
}
|
}
|
||||||
|
|
||||||
let selectButton: JSX.Element;
|
let selectButton: JSX.Element | undefined;
|
||||||
if (deviceList.length > 1) {
|
if (deviceList.length > 1) {
|
||||||
const anySelected = deviceList.some((device) => this.state.selectedDevices.includes(device.device_id));
|
const anySelected = deviceList.some((device) => this.state.selectedDevices.includes(device.device_id));
|
||||||
const buttonAction = anySelected
|
const buttonAction = anySelected
|
||||||
|
|
|
@ -103,7 +103,7 @@ export default class DevicesPanelEntry extends React.Component<IProps, IState> {
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
const userId = cli.getUserId();
|
const userId = cli.getUserId()!;
|
||||||
const verificationRequestPromise = cli.requestVerification(userId, [this.props.device.device_id]);
|
const verificationRequestPromise = cli.requestVerification(userId, [this.props.device.device_id]);
|
||||||
Modal.createDialog(VerificationRequestDialog, {
|
Modal.createDialog(VerificationRequestDialog, {
|
||||||
verificationRequestPromise,
|
verificationRequestPromise,
|
||||||
|
@ -119,7 +119,7 @@ export default class DevicesPanelEntry extends React.Component<IProps, IState> {
|
||||||
|
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
let iconClass = "";
|
let iconClass = "";
|
||||||
let verifyButton: JSX.Element;
|
let verifyButton: JSX.Element | undefined;
|
||||||
if (this.props.verified !== null) {
|
if (this.props.verified !== null) {
|
||||||
iconClass = this.props.verified ? "mx_E2EIcon_verified" : "mx_E2EIcon_warning";
|
iconClass = this.props.verified ? "mx_E2EIcon_verified" : "mx_E2EIcon_warning";
|
||||||
if (!this.props.verified && this.props.canBeVerified) {
|
if (!this.props.verified && this.props.canBeVerified) {
|
||||||
|
@ -131,7 +131,7 @@ export default class DevicesPanelEntry extends React.Component<IProps, IState> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let signOutButton: JSX.Element;
|
let signOutButton: JSX.Element | undefined;
|
||||||
if (this.props.isOwnDevice) {
|
if (this.props.isOwnDevice) {
|
||||||
signOutButton = (
|
signOutButton = (
|
||||||
<AccessibleButton kind="danger_outline" onClick={this.onOwnDeviceSignOut}>
|
<AccessibleButton kind="danger_outline" onClick={this.onOwnDeviceSignOut}>
|
||||||
|
|
|
@ -26,6 +26,7 @@ import EventIndexPeg from "../../../indexing/EventIndexPeg";
|
||||||
import { SettingLevel } from "../../../settings/SettingLevel";
|
import { SettingLevel } from "../../../settings/SettingLevel";
|
||||||
import SeshatResetDialog from "../dialogs/SeshatResetDialog";
|
import SeshatResetDialog from "../dialogs/SeshatResetDialog";
|
||||||
import InlineSpinner from "../elements/InlineSpinner";
|
import InlineSpinner from "../elements/InlineSpinner";
|
||||||
|
import { IIndexStats } from "../../../indexing/BaseEventIndexManager";
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
enabling: boolean;
|
enabling: boolean;
|
||||||
|
@ -48,10 +49,10 @@ export default class EventIndexPanel extends React.Component<{}, IState> {
|
||||||
|
|
||||||
public updateCurrentRoom = async (): Promise<void> => {
|
public updateCurrentRoom = async (): Promise<void> => {
|
||||||
const eventIndex = EventIndexPeg.get();
|
const eventIndex = EventIndexPeg.get();
|
||||||
let stats;
|
let stats: IIndexStats | undefined;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
stats = await eventIndex.getStats();
|
stats = await eventIndex?.getStats();
|
||||||
} catch {
|
} catch {
|
||||||
// This call may fail if sporadically, not a huge issue as we will
|
// This call may fail if sporadically, not a huge issue as we will
|
||||||
// try later again and probably succeed.
|
// try later again and probably succeed.
|
||||||
|
@ -126,8 +127,8 @@ export default class EventIndexPanel extends React.Component<{}, IState> {
|
||||||
});
|
});
|
||||||
|
|
||||||
await EventIndexPeg.initEventIndex();
|
await EventIndexPeg.initEventIndex();
|
||||||
await EventIndexPeg.get().addInitialCheckpoints();
|
await EventIndexPeg.get()?.addInitialCheckpoints();
|
||||||
EventIndexPeg.get().startCrawler();
|
EventIndexPeg.get()?.startCrawler();
|
||||||
await SettingsStore.setValue("enableEventIndexing", null, SettingLevel.DEVICE, true);
|
await SettingsStore.setValue("enableEventIndexing", null, SettingLevel.DEVICE, true);
|
||||||
await this.updateState();
|
await this.updateState();
|
||||||
};
|
};
|
||||||
|
@ -146,7 +147,7 @@ export default class EventIndexPanel extends React.Component<{}, IState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
let eventIndexingSettings = null;
|
let eventIndexingSettings: JSX.Element | undefined;
|
||||||
const brand = SdkConfig.get().brand;
|
const brand = SdkConfig.get().brand;
|
||||||
|
|
||||||
if (EventIndexPeg.get() !== null) {
|
if (EventIndexPeg.get() !== null) {
|
||||||
|
|
|
@ -39,8 +39,8 @@ interface IState {
|
||||||
layout: Layout;
|
layout: Layout;
|
||||||
// User profile data for the message preview
|
// User profile data for the message preview
|
||||||
userId?: string;
|
userId?: string;
|
||||||
displayName: string;
|
displayName?: string;
|
||||||
avatarUrl: string;
|
avatarUrl?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class FontScalingPanel extends React.Component<IProps, IState> {
|
export default class FontScalingPanel extends React.Component<IProps, IState> {
|
||||||
|
@ -55,16 +55,13 @@ export default class FontScalingPanel extends React.Component<IProps, IState> {
|
||||||
fontSize: (SettingsStore.getValue("baseFontSize", null) + FontWatcher.SIZE_DIFF).toString(),
|
fontSize: (SettingsStore.getValue("baseFontSize", null) + FontWatcher.SIZE_DIFF).toString(),
|
||||||
useCustomFontSize: SettingsStore.getValue("useCustomFontSize"),
|
useCustomFontSize: SettingsStore.getValue("useCustomFontSize"),
|
||||||
layout: SettingsStore.getValue("layout"),
|
layout: SettingsStore.getValue("layout"),
|
||||||
userId: null,
|
|
||||||
displayName: null,
|
|
||||||
avatarUrl: null,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public async componentDidMount(): Promise<void> {
|
public async componentDidMount(): Promise<void> {
|
||||||
// Fetch the current user profile for the message preview
|
// Fetch the current user profile for the message preview
|
||||||
const client = MatrixClientPeg.get();
|
const client = MatrixClientPeg.get();
|
||||||
const userId = client.getUserId();
|
const userId = client.getUserId()!;
|
||||||
const profileInfo = await client.getProfileInfo(userId);
|
const profileInfo = await client.getProfileInfo(userId);
|
||||||
if (this.unmounted) return;
|
if (this.unmounted) return;
|
||||||
|
|
||||||
|
|
|
@ -74,14 +74,14 @@ const JoinRuleSettings: React.FC<IProps> = ({
|
||||||
? content.allow?.filter((o) => o.type === RestrictedAllowType.RoomMembership).map((o) => o.room_id)
|
? content.allow?.filter((o) => o.type === RestrictedAllowType.RoomMembership).map((o) => o.room_id)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
const editRestrictedRoomIds = async (): Promise<string[] | undefined> => {
|
const editRestrictedRoomIds = async (): Promise<string[]> => {
|
||||||
let selected = restrictedAllowRoomIds;
|
let selected = restrictedAllowRoomIds;
|
||||||
if (!selected?.length && SpaceStore.instance.activeSpaceRoom) {
|
if (!selected?.length && SpaceStore.instance.activeSpaceRoom) {
|
||||||
selected = [SpaceStore.instance.activeSpaceRoom.roomId];
|
selected = [SpaceStore.instance.activeSpaceRoom.roomId];
|
||||||
}
|
}
|
||||||
|
|
||||||
const matrixClient = MatrixClientPeg.get();
|
const matrixClient = MatrixClientPeg.get();
|
||||||
const { finished } = Modal.createDialog(
|
const { finished } = Modal.createDialog<[string[]]>(
|
||||||
ManageRestrictedJoinRuleDialog,
|
ManageRestrictedJoinRuleDialog,
|
||||||
{
|
{
|
||||||
matrixClient,
|
matrixClient,
|
||||||
|
@ -127,7 +127,7 @@ const JoinRuleSettings: React.FC<IProps> = ({
|
||||||
const shownSpaces = restrictedAllowRoomIds
|
const shownSpaces = restrictedAllowRoomIds
|
||||||
.map((roomId) => cli.getRoom(roomId))
|
.map((roomId) => cli.getRoom(roomId))
|
||||||
.filter((room) => room?.isSpaceRoom())
|
.filter((room) => room?.isSpaceRoom())
|
||||||
.slice(0, 4);
|
.slice(0, 4) as Room[];
|
||||||
|
|
||||||
let moreText;
|
let moreText;
|
||||||
if (shownSpaces.length < restrictedAllowRoomIds.length) {
|
if (shownSpaces.length < restrictedAllowRoomIds.length) {
|
||||||
|
@ -234,7 +234,7 @@ const JoinRuleSettings: React.FC<IProps> = ({
|
||||||
const onChange = async (joinRule: JoinRule): Promise<void> => {
|
const onChange = async (joinRule: JoinRule): Promise<void> => {
|
||||||
const beforeJoinRule = content.join_rule;
|
const beforeJoinRule = content.join_rule;
|
||||||
|
|
||||||
let restrictedAllowRoomIds: string[];
|
let restrictedAllowRoomIds: string[] | undefined;
|
||||||
if (joinRule === JoinRule.Restricted) {
|
if (joinRule === JoinRule.Restricted) {
|
||||||
if (beforeJoinRule === JoinRule.Restricted || roomSupportsRestricted) {
|
if (beforeJoinRule === JoinRule.Restricted || roomSupportsRestricted) {
|
||||||
// Have the user pick which spaces to allow joins from
|
// Have the user pick which spaces to allow joins from
|
||||||
|
@ -244,8 +244,8 @@ const JoinRuleSettings: React.FC<IProps> = ({
|
||||||
// Block this action on a room upgrade otherwise it'd make their room unjoinable
|
// Block this action on a room upgrade otherwise it'd make their room unjoinable
|
||||||
const targetVersion = preferredRestrictionVersion;
|
const targetVersion = preferredRestrictionVersion;
|
||||||
|
|
||||||
let warning: JSX.Element;
|
let warning: JSX.Element | undefined;
|
||||||
const userId = cli.getUserId();
|
const userId = cli.getUserId()!;
|
||||||
const unableToUpdateSomeParents = Array.from(SpaceStore.instance.getKnownParents(room.roomId)).some(
|
const unableToUpdateSomeParents = Array.from(SpaceStore.instance.getKnownParents(room.roomId)).some(
|
||||||
(roomId) => !cli.getRoom(roomId)?.currentState.maySendStateEvent(EventType.SpaceChild, userId),
|
(roomId) => !cli.getRoom(roomId)?.currentState.maySendStateEvent(EventType.SpaceChild, userId),
|
||||||
);
|
);
|
||||||
|
@ -332,7 +332,7 @@ const JoinRuleSettings: React.FC<IProps> = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
// when setting to 0 allowed rooms/spaces set to invite only instead as per the note
|
// when setting to 0 allowed rooms/spaces set to invite only instead as per the note
|
||||||
if (!restrictedAllowRoomIds.length) {
|
if (!restrictedAllowRoomIds?.length) {
|
||||||
joinRule = JoinRule.Invite;
|
joinRule = JoinRule.Invite;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -346,7 +346,7 @@ const JoinRuleSettings: React.FC<IProps> = ({
|
||||||
|
|
||||||
// pre-set the accepted spaces with the currently viewed one as per the microcopy
|
// pre-set the accepted spaces with the currently viewed one as per the microcopy
|
||||||
if (joinRule === JoinRule.Restricted) {
|
if (joinRule === JoinRule.Restricted) {
|
||||||
newContent.allow = restrictedAllowRoomIds.map((roomId) => ({
|
newContent.allow = restrictedAllowRoomIds?.map((roomId) => ({
|
||||||
type: RestrictedAllowType.RoomMembership,
|
type: RestrictedAllowType.RoomMembership,
|
||||||
room_id: roomId,
|
room_id: roomId,
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -328,7 +328,7 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
|
||||||
this.setState({ phase: Phase.Persisting });
|
this.setState({ phase: Phase.Persisting });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const masterRule = this.state.masterPushRule;
|
const masterRule = this.state.masterPushRule!;
|
||||||
await MatrixClientPeg.get().setPushRuleEnabled("global", masterRule.kind, masterRule.rule_id, !checked);
|
await MatrixClientPeg.get().setPushRuleEnabled("global", masterRule.kind, masterRule.rule_id, !checked);
|
||||||
await this.refreshFromServer();
|
await this.refreshFromServer();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -396,8 +396,8 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
|
||||||
if (rule.ruleId === KEYWORD_RULE_ID) {
|
if (rule.ruleId === KEYWORD_RULE_ID) {
|
||||||
// Update all the keywords
|
// Update all the keywords
|
||||||
for (const rule of this.state.vectorKeywordRuleInfo.rules) {
|
for (const rule of this.state.vectorKeywordRuleInfo.rules) {
|
||||||
let enabled: boolean;
|
let enabled: boolean | undefined;
|
||||||
let actions: PushRuleAction[];
|
let actions: PushRuleAction[] | undefined;
|
||||||
if (checkedState === VectorState.On) {
|
if (checkedState === VectorState.On) {
|
||||||
if (rule.actions.length !== 1) {
|
if (rule.actions.length !== 1) {
|
||||||
// XXX: Magic number
|
// XXX: Magic number
|
||||||
|
|
|
@ -33,14 +33,14 @@ import { accessSecretStorage } from "../../../SecurityManager";
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
error: null;
|
error: Error | null;
|
||||||
backupKeyStored: boolean;
|
backupKeyStored: boolean | null;
|
||||||
backupKeyCached: boolean;
|
backupKeyCached: boolean | null;
|
||||||
backupKeyWellFormed: boolean;
|
backupKeyWellFormed: boolean | null;
|
||||||
secretStorageKeyInAccount: boolean;
|
secretStorageKeyInAccount: boolean | null;
|
||||||
secretStorageReady: boolean;
|
secretStorageReady: boolean | null;
|
||||||
backupInfo: IKeyBackupInfo;
|
backupInfo: IKeyBackupInfo | null;
|
||||||
backupSigStatus: TrustInfo;
|
backupSigStatus: TrustInfo | null;
|
||||||
sessionsRemaining: number;
|
sessionsRemaining: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,10 +144,10 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> {
|
||||||
|
|
||||||
private async getUpdatedDiagnostics(): Promise<void> {
|
private async getUpdatedDiagnostics(): Promise<void> {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
const secretStorage = cli.crypto.secretStorage;
|
const secretStorage = cli.crypto!.secretStorage;
|
||||||
|
|
||||||
const backupKeyStored = !!(await cli.isKeyBackupKeyStored());
|
const backupKeyStored = !!(await cli.isKeyBackupKeyStored());
|
||||||
const backupKeyFromCache = await cli.crypto.getSessionBackupPrivateKey();
|
const backupKeyFromCache = await cli.crypto!.getSessionBackupPrivateKey();
|
||||||
const backupKeyCached = !!backupKeyFromCache;
|
const backupKeyCached = !!backupKeyFromCache;
|
||||||
const backupKeyWellFormed = backupKeyFromCache instanceof Uint8Array;
|
const backupKeyWellFormed = backupKeyFromCache instanceof Uint8Array;
|
||||||
const secretStorageKeyInAccount = await secretStorage.hasKey();
|
const secretStorageKeyInAccount = await secretStorage.hasKey();
|
||||||
|
@ -173,7 +173,7 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> {
|
||||||
this.loadBackupStatus();
|
this.loadBackupStatus();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
null,
|
undefined,
|
||||||
/* priority = */ false,
|
/* priority = */ false,
|
||||||
/* static = */ true,
|
/* static = */ true,
|
||||||
);
|
);
|
||||||
|
@ -200,7 +200,7 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
private restoreBackup = async (): Promise<void> => {
|
private restoreBackup = async (): Promise<void> => {
|
||||||
Modal.createDialog(RestoreKeyBackupDialog, null, null, /* priority = */ false, /* static = */ true);
|
Modal.createDialog(RestoreKeyBackupDialog, undefined, undefined, /* priority = */ false, /* static = */ true);
|
||||||
};
|
};
|
||||||
|
|
||||||
private resetSecretStorage = async (): Promise<void> => {
|
private resetSecretStorage = async (): Promise<void> => {
|
||||||
|
@ -233,7 +233,7 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> {
|
||||||
let statusDescription;
|
let statusDescription;
|
||||||
let extraDetailsTableRows;
|
let extraDetailsTableRows;
|
||||||
let extraDetails;
|
let extraDetails;
|
||||||
const actions = [];
|
const actions: JSX.Element[] = [];
|
||||||
if (error) {
|
if (error) {
|
||||||
statusDescription = <div className="error">{_t("Unable to load key backup status")}</div>;
|
statusDescription = <div className="error">{_t("Unable to load key backup status")}</div>;
|
||||||
} else if (loading) {
|
} else if (loading) {
|
||||||
|
|
|
@ -97,7 +97,6 @@ export default class SetIdServer extends React.Component<IProps, IState> {
|
||||||
defaultIdServer,
|
defaultIdServer,
|
||||||
currentClientIdServer: MatrixClientPeg.get().getIdentityServerUrl(),
|
currentClientIdServer: MatrixClientPeg.get().getIdentityServerUrl(),
|
||||||
idServer: "",
|
idServer: "",
|
||||||
error: null,
|
|
||||||
busy: false,
|
busy: false,
|
||||||
disconnectBusy: false,
|
disconnectBusy: false,
|
||||||
checking: false,
|
checking: false,
|
||||||
|
|
|
@ -133,7 +133,7 @@ interface IProps {
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
verifying: boolean;
|
verifying: boolean;
|
||||||
addTask: AddThreepid;
|
addTask: AddThreepid | null;
|
||||||
continueDisabled: boolean;
|
continueDisabled: boolean;
|
||||||
newEmailAddress: string;
|
newEmailAddress: string;
|
||||||
}
|
}
|
||||||
|
@ -201,7 +201,7 @@ export default class EmailAddresses extends React.Component<IProps, IState> {
|
||||||
|
|
||||||
this.setState({ continueDisabled: true });
|
this.setState({ continueDisabled: true });
|
||||||
this.state.addTask
|
this.state.addTask
|
||||||
.checkEmailLinkClicked()
|
?.checkEmailLinkClicked()
|
||||||
.then(([finished]) => {
|
.then(([finished]) => {
|
||||||
let newEmailAddress = this.state.newEmailAddress;
|
let newEmailAddress = this.state.newEmailAddress;
|
||||||
if (finished) {
|
if (finished) {
|
||||||
|
|
|
@ -33,11 +33,11 @@ interface Props {
|
||||||
device?: ExtendedDevice;
|
device?: ExtendedDevice;
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
isSigningOut: boolean;
|
isSigningOut: boolean;
|
||||||
localNotificationSettings?: LocalNotificationSettings | undefined;
|
localNotificationSettings?: LocalNotificationSettings;
|
||||||
// number of other sessions the user has
|
// number of other sessions the user has
|
||||||
// excludes current session
|
// excludes current session
|
||||||
otherSessionsCount: number;
|
otherSessionsCount: number;
|
||||||
setPushNotifications?: (deviceId: string, enabled: boolean) => Promise<void> | undefined;
|
setPushNotifications: (deviceId: string, enabled: boolean) => Promise<void>;
|
||||||
onVerifyCurrentDevice: () => void;
|
onVerifyCurrentDevice: () => void;
|
||||||
onSignOutCurrentDevice: () => void;
|
onSignOutCurrentDevice: () => void;
|
||||||
signOutAllOtherSessions?: () => void;
|
signOutAllOtherSessions?: () => void;
|
||||||
|
|
|
@ -32,7 +32,7 @@ const makeDeleteRequest =
|
||||||
export const deleteDevicesWithInteractiveAuth = async (
|
export const deleteDevicesWithInteractiveAuth = async (
|
||||||
matrixClient: MatrixClient,
|
matrixClient: MatrixClient,
|
||||||
deviceIds: string[],
|
deviceIds: string[],
|
||||||
onFinished?: InteractiveAuthCallback,
|
onFinished: InteractiveAuthCallback,
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
if (!deviceIds.length) {
|
if (!deviceIds.length) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -48,9 +48,9 @@ interface IEmailAddressProps {
|
||||||
|
|
||||||
interface IEmailAddressState {
|
interface IEmailAddressState {
|
||||||
verifying: boolean;
|
verifying: boolean;
|
||||||
addTask: any; // FIXME: When AddThreepid is TSfied
|
addTask: AddThreepid | null;
|
||||||
continueDisabled: boolean;
|
continueDisabled: boolean;
|
||||||
bound: boolean;
|
bound?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class EmailAddress extends React.Component<IEmailAddressProps, IEmailAddressState> {
|
export class EmailAddress extends React.Component<IEmailAddressProps, IEmailAddressState> {
|
||||||
|
@ -172,7 +172,7 @@ export class EmailAddress extends React.Component<IEmailAddressProps, IEmailAddr
|
||||||
|
|
||||||
this.setState({ continueDisabled: true });
|
this.setState({ continueDisabled: true });
|
||||||
try {
|
try {
|
||||||
await this.state.addTask.checkEmailLinkClicked();
|
await this.state.addTask?.checkEmailLinkClicked();
|
||||||
this.setState({
|
this.setState({
|
||||||
addTask: null,
|
addTask: null,
|
||||||
continueDisabled: false,
|
continueDisabled: false,
|
||||||
|
|
|
@ -41,10 +41,10 @@ interface IPhoneNumberProps {
|
||||||
interface IPhoneNumberState {
|
interface IPhoneNumberState {
|
||||||
verifying: boolean;
|
verifying: boolean;
|
||||||
verificationCode: string;
|
verificationCode: string;
|
||||||
addTask: any; // FIXME: When AddThreepid is TSfied
|
addTask: AddThreepid | null;
|
||||||
continueDisabled: boolean;
|
continueDisabled: boolean;
|
||||||
bound: boolean;
|
bound?: boolean;
|
||||||
verifyError: string;
|
verifyError: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PhoneNumber extends React.Component<IPhoneNumberProps, IPhoneNumberState> {
|
export class PhoneNumber extends React.Component<IPhoneNumberProps, IPhoneNumberState> {
|
||||||
|
@ -89,6 +89,7 @@ export class PhoneNumber extends React.Component<IPhoneNumberProps, IPhoneNumber
|
||||||
// a leading plus sign to a number in E.164 format (which the 3PID
|
// a leading plus sign to a number in E.164 format (which the 3PID
|
||||||
// address is), but this goes against the spec.
|
// address is), but this goes against the spec.
|
||||||
// See https://github.com/matrix-org/matrix-doc/issues/2222
|
// See https://github.com/matrix-org/matrix-doc/issues/2222
|
||||||
|
// @ts-ignore
|
||||||
await task.bindMsisdn(null, `+${address}`);
|
await task.bindMsisdn(null, `+${address}`);
|
||||||
this.setState({
|
this.setState({
|
||||||
continueDisabled: false,
|
continueDisabled: false,
|
||||||
|
@ -128,8 +129,10 @@ export class PhoneNumber extends React.Component<IPhoneNumberProps, IPhoneNumber
|
||||||
// address is), but this goes against the spec.
|
// address is), but this goes against the spec.
|
||||||
// See https://github.com/matrix-org/matrix-doc/issues/2222
|
// See https://github.com/matrix-org/matrix-doc/issues/2222
|
||||||
if (bind) {
|
if (bind) {
|
||||||
|
// @ts-ignore
|
||||||
await task.bindMsisdn(null, `+${address}`);
|
await task.bindMsisdn(null, `+${address}`);
|
||||||
} else {
|
} else {
|
||||||
|
// @ts-ignore
|
||||||
await task.addMsisdn(null, `+${address}`);
|
await task.addMsisdn(null, `+${address}`);
|
||||||
}
|
}
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -183,7 +186,7 @@ export class PhoneNumber extends React.Component<IPhoneNumberProps, IPhoneNumber
|
||||||
this.setState({ continueDisabled: true });
|
this.setState({ continueDisabled: true });
|
||||||
const token = this.state.verificationCode;
|
const token = this.state.verificationCode;
|
||||||
try {
|
try {
|
||||||
await this.state.addTask.haveMsisdnToken(token);
|
await this.state.addTask?.haveMsisdnToken(token);
|
||||||
this.setState({
|
this.setState({
|
||||||
addTask: null,
|
addTask: null,
|
||||||
continueDisabled: false,
|
continueDisabled: false,
|
||||||
|
|
|
@ -16,7 +16,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React, { ReactNode } from "react";
|
||||||
import { SERVICE_TYPES } from "matrix-js-sdk/src/service-types";
|
import { SERVICE_TYPES } from "matrix-js-sdk/src/service-types";
|
||||||
import { IThreepid } from "matrix-js-sdk/src/@types/threepids";
|
import { IThreepid } from "matrix-js-sdk/src/@types/threepids";
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
|
@ -64,20 +64,20 @@ interface IState {
|
||||||
spellCheckEnabled: boolean;
|
spellCheckEnabled: boolean;
|
||||||
spellCheckLanguages: string[];
|
spellCheckLanguages: string[];
|
||||||
haveIdServer: boolean;
|
haveIdServer: boolean;
|
||||||
serverSupportsSeparateAddAndBind: boolean;
|
serverSupportsSeparateAddAndBind?: boolean;
|
||||||
idServerHasUnsignedTerms: boolean;
|
idServerHasUnsignedTerms: boolean;
|
||||||
requiredPolicyInfo: {
|
requiredPolicyInfo: {
|
||||||
// This object is passed along to a component for handling
|
// This object is passed along to a component for handling
|
||||||
hasTerms: boolean;
|
hasTerms: boolean;
|
||||||
policiesAndServices: ServicePolicyPair[]; // From the startTermsFlow callback
|
policiesAndServices: ServicePolicyPair[] | null; // From the startTermsFlow callback
|
||||||
agreedUrls: string[]; // From the startTermsFlow callback
|
agreedUrls: string[] | null; // From the startTermsFlow callback
|
||||||
resolve: (values: string[]) => void; // Promise resolve function for startTermsFlow callback
|
resolve: ((values: string[]) => void) | null; // Promise resolve function for startTermsFlow callback
|
||||||
};
|
};
|
||||||
emails: IThreepid[];
|
emails: IThreepid[];
|
||||||
msisdns: IThreepid[];
|
msisdns: IThreepid[];
|
||||||
loading3pids: boolean; // whether or not the emails and msisdns have been loaded
|
loading3pids: boolean; // whether or not the emails and msisdns have been loaded
|
||||||
canChangePassword: boolean;
|
canChangePassword: boolean;
|
||||||
idServerName: string;
|
idServerName?: string;
|
||||||
externalAccountManagementUrl?: string;
|
externalAccountManagementUrl?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,7 +92,6 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta
|
||||||
spellCheckEnabled: false,
|
spellCheckEnabled: false,
|
||||||
spellCheckLanguages: [],
|
spellCheckLanguages: [],
|
||||||
haveIdServer: Boolean(MatrixClientPeg.get().getIdentityServerUrl()),
|
haveIdServer: Boolean(MatrixClientPeg.get().getIdentityServerUrl()),
|
||||||
serverSupportsSeparateAddAndBind: null,
|
|
||||||
idServerHasUnsignedTerms: false,
|
idServerHasUnsignedTerms: false,
|
||||||
requiredPolicyInfo: {
|
requiredPolicyInfo: {
|
||||||
// This object is passed along to a component for handling
|
// This object is passed along to a component for handling
|
||||||
|
@ -105,8 +104,6 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta
|
||||||
msisdns: [],
|
msisdns: [],
|
||||||
loading3pids: true, // whether or not the emails and msisdns have been loaded
|
loading3pids: true, // whether or not the emails and msisdns have been loaded
|
||||||
canChangePassword: false,
|
canChangePassword: false,
|
||||||
idServerName: null,
|
|
||||||
externalAccountManagementUrl: undefined,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.dispatcherRef = dis.register(this.onAction);
|
this.dispatcherRef = dis.register(this.onAction);
|
||||||
|
@ -118,8 +115,8 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta
|
||||||
public async componentDidMount(): Promise<void> {
|
public async componentDidMount(): Promise<void> {
|
||||||
const plat = PlatformPeg.get();
|
const plat = PlatformPeg.get();
|
||||||
const [spellCheckEnabled, spellCheckLanguages] = await Promise.all([
|
const [spellCheckEnabled, spellCheckLanguages] = await Promise.all([
|
||||||
plat.getSpellCheckEnabled(),
|
plat?.getSpellCheckEnabled(),
|
||||||
plat.getSpellCheckLanguages(),
|
plat?.getSpellCheckLanguages(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (spellCheckLanguages) {
|
if (spellCheckLanguages) {
|
||||||
|
@ -301,7 +298,7 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderAccountSection(): JSX.Element {
|
private renderAccountSection(): JSX.Element {
|
||||||
let passwordChangeForm = (
|
let passwordChangeForm: ReactNode = (
|
||||||
<ChangePassword
|
<ChangePassword
|
||||||
className="mx_GeneralUserSettingsTab_changePassword"
|
className="mx_GeneralUserSettingsTab_changePassword"
|
||||||
rowClassName=""
|
rowClassName=""
|
||||||
|
@ -311,7 +308,7 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
let threepidSection = null;
|
let threepidSection: ReactNode = null;
|
||||||
|
|
||||||
// For older homeservers without separate 3PID add and bind methods (MSC2290),
|
// For older homeservers without separate 3PID add and bind methods (MSC2290),
|
||||||
// we use a combo add with bind option API which requires an identity server to
|
// we use a combo add with bind option API which requires an identity server to
|
||||||
|
@ -345,7 +342,7 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta
|
||||||
threepidSection = <Spinner />;
|
threepidSection = <Spinner />;
|
||||||
}
|
}
|
||||||
|
|
||||||
let passwordChangeText = _t("Set a new account password…");
|
let passwordChangeText: ReactNode = _t("Set a new account password…");
|
||||||
if (!this.state.canChangePassword) {
|
if (!this.state.canChangePassword) {
|
||||||
// Just don't show anything if you can't do anything.
|
// Just don't show anything if you can't do anything.
|
||||||
passwordChangeText = null;
|
passwordChangeText = null;
|
||||||
|
@ -483,7 +480,7 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderIntegrationManagerSection(): JSX.Element {
|
private renderIntegrationManagerSection(): ReactNode {
|
||||||
if (!SettingsStore.getValue(UIFeature.Widgets)) return null;
|
if (!SettingsStore.getValue(UIFeature.Widgets)) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -496,7 +493,7 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta
|
||||||
|
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
const plaf = PlatformPeg.get();
|
const plaf = PlatformPeg.get();
|
||||||
const supportsMultiLanguageSpellCheck = plaf.supportsSpellCheckSettings();
|
const supportsMultiLanguageSpellCheck = plaf?.supportsSpellCheckSettings();
|
||||||
|
|
||||||
const discoWarning = this.state.requiredPolicyInfo.hasTerms ? (
|
const discoWarning = this.state.requiredPolicyInfo.hasTerms ? (
|
||||||
<img
|
<img
|
||||||
|
@ -508,7 +505,7 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta
|
||||||
/>
|
/>
|
||||||
) : null;
|
) : null;
|
||||||
|
|
||||||
let accountManagementSection;
|
let accountManagementSection: JSX.Element | undefined;
|
||||||
if (SettingsStore.getValue(UIFeature.Deactivate)) {
|
if (SettingsStore.getValue(UIFeature.Deactivate)) {
|
||||||
accountManagementSection = (
|
accountManagementSection = (
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -23,6 +23,7 @@ import QueryMatcher from "../../../autocomplete/QueryMatcher";
|
||||||
import SearchBox from "../../structures/SearchBox";
|
import SearchBox from "../../structures/SearchBox";
|
||||||
import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
|
import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
|
||||||
import { Entry } from "../dialogs/AddExistingToSpaceDialog";
|
import { Entry } from "../dialogs/AddExistingToSpaceDialog";
|
||||||
|
import { filterBoolean } from "../../../utils/arrays";
|
||||||
|
|
||||||
enum Target {
|
enum Target {
|
||||||
All = "All",
|
All = "All",
|
||||||
|
@ -53,7 +54,7 @@ const SpecificChildrenPicker: React.FC<ISpecificChildrenPickerProps> = ({
|
||||||
|
|
||||||
const matcher = new QueryMatcher<Room>(rooms, {
|
const matcher = new QueryMatcher<Room>(rooms, {
|
||||||
keys: ["name"],
|
keys: ["name"],
|
||||||
funcs: [(r) => [r.getCanonicalAlias(), ...r.getAltAliases()].filter(Boolean) as string[]],
|
funcs: [(r) => filterBoolean([r.getCanonicalAlias(), ...r.getAltAliases()])],
|
||||||
shouldMatchWordsOnly: false,
|
shouldMatchWordsOnly: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,7 @@ export const createSpace = async (
|
||||||
avatar?: string | File,
|
avatar?: string | File,
|
||||||
createOpts: Partial<ICreateRoomOpts> = {},
|
createOpts: Partial<ICreateRoomOpts> = {},
|
||||||
otherOpts: Partial<Omit<ICreateOpts, "createOpts">> = {},
|
otherOpts: Partial<Omit<ICreateOpts, "createOpts">> = {},
|
||||||
): Promise<string> => {
|
): Promise<string | null> => {
|
||||||
return createRoom({
|
return createRoom({
|
||||||
createOpts: {
|
createOpts: {
|
||||||
name,
|
name,
|
||||||
|
@ -179,7 +179,7 @@ export const SpaceCreateForm: React.FC<ISpaceCreateFormProps> = ({
|
||||||
children,
|
children,
|
||||||
}) => {
|
}) => {
|
||||||
const cli = useContext(MatrixClientContext);
|
const cli = useContext(MatrixClientContext);
|
||||||
const domain = cli.getDomain();
|
const domain = cli.getDomain() ?? undefined;
|
||||||
|
|
||||||
const onKeyDown = (ev: KeyboardEvent): void => {
|
const onKeyDown = (ev: KeyboardEvent): void => {
|
||||||
const action = getKeyBindingsManager().getAccessibilityAction(ev);
|
const action = getKeyBindingsManager().getAccessibilityAction(ev);
|
||||||
|
@ -231,7 +231,7 @@ export const SpaceCreateForm: React.FC<ISpaceCreateFormProps> = ({
|
||||||
name="spaceTopic"
|
name="spaceTopic"
|
||||||
element="textarea"
|
element="textarea"
|
||||||
label={_t("Description")}
|
label={_t("Description")}
|
||||||
value={topic}
|
value={topic ?? ""}
|
||||||
onChange={(ev) => setTopic(ev.target.value)}
|
onChange={(ev) => setTopic(ev.target.value)}
|
||||||
rows={3}
|
rows={3}
|
||||||
disabled={busy}
|
disabled={busy}
|
||||||
|
@ -261,14 +261,18 @@ const SpaceCreateMenu: React.FC<{
|
||||||
|
|
||||||
setBusy(true);
|
setBusy(true);
|
||||||
// require & validate the space name field
|
// require & validate the space name field
|
||||||
if (!(await spaceNameField.current.validate({ allowEmpty: false }))) {
|
if (spaceNameField.current && !(await spaceNameField.current.validate({ allowEmpty: false }))) {
|
||||||
spaceNameField.current.focus();
|
spaceNameField.current.focus();
|
||||||
spaceNameField.current.validate({ allowEmpty: false, focused: true });
|
spaceNameField.current.validate({ allowEmpty: false, focused: true });
|
||||||
setBusy(false);
|
setBusy(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (visibility === Visibility.Public && !(await spaceAliasField.current.validate({ allowEmpty: false }))) {
|
if (
|
||||||
|
spaceAliasField.current &&
|
||||||
|
visibility === Visibility.Public &&
|
||||||
|
!(await spaceAliasField.current.validate({ allowEmpty: false }))
|
||||||
|
) {
|
||||||
spaceAliasField.current.focus();
|
spaceAliasField.current.focus();
|
||||||
spaceAliasField.current.validate({ allowEmpty: false, focused: true });
|
spaceAliasField.current.validate({ allowEmpty: false, focused: true });
|
||||||
setBusy(false);
|
setBusy(false);
|
||||||
|
|
|
@ -23,5 +23,5 @@ export interface JoinRoomErrorPayload extends Pick<ActionPayload, "action"> {
|
||||||
action: Action.JoinRoomError;
|
action: Action.JoinRoomError;
|
||||||
|
|
||||||
roomId: string;
|
roomId: string;
|
||||||
err?: MatrixError;
|
err: MatrixError;
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,7 @@ export function getSlashCommand(model: EditorModel): [Command | undefined, strin
|
||||||
|
|
||||||
export async function runSlashCommand(
|
export async function runSlashCommand(
|
||||||
cmd: Command,
|
cmd: Command,
|
||||||
args: string,
|
args: string | undefined,
|
||||||
roomId: string,
|
roomId: string,
|
||||||
threadId: string | null,
|
threadId: string | null,
|
||||||
): Promise<[content: IContent | null, success: boolean]> {
|
): Promise<[content: IContent | null, success: boolean]> {
|
||||||
|
|
|
@ -45,7 +45,7 @@ import { Caret } from "./caret";
|
||||||
*/
|
*/
|
||||||
|
|
||||||
type TransformCallback = (caretPosition: DocumentPosition, inputType: string, diff: IDiff) => number | void;
|
type TransformCallback = (caretPosition: DocumentPosition, inputType: string, diff: IDiff) => number | void;
|
||||||
type UpdateCallback = (caret: Caret, inputType?: string, diff?: IDiff) => void;
|
type UpdateCallback = (caret?: Caret, inputType?: string, diff?: IDiff) => void;
|
||||||
type ManualTransformCallback = () => Caret;
|
type ManualTransformCallback = () => Caret;
|
||||||
|
|
||||||
export default class EditorModel {
|
export default class EditorModel {
|
||||||
|
@ -252,7 +252,7 @@ export default class EditorModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
private onAutoComplete = ({ replaceParts, close }: ICallback): void => {
|
private onAutoComplete = ({ replaceParts, close }: ICallback): void => {
|
||||||
let pos;
|
let pos: DocumentPosition | undefined;
|
||||||
if (replaceParts) {
|
if (replaceParts) {
|
||||||
this._parts.splice(this.autoCompletePartIdx, this.autoCompletePartCount, ...replaceParts);
|
this._parts.splice(this.autoCompletePartIdx, this.autoCompletePartCount, ...replaceParts);
|
||||||
this.autoCompletePartCount = replaceParts.length;
|
this.autoCompletePartCount = replaceParts.length;
|
||||||
|
@ -380,13 +380,15 @@ export default class EditorModel {
|
||||||
// reset it to insert as first part
|
// reset it to insert as first part
|
||||||
index = 0;
|
index = 0;
|
||||||
}
|
}
|
||||||
while (str) {
|
|
||||||
const newPart = this._partCreator.createPartForInput(str, index, inputType);
|
let it: string | undefined = str;
|
||||||
const oldStr = str;
|
while (it) {
|
||||||
str = newPart.appendUntilRejected(str, inputType);
|
const newPart = this._partCreator.createPartForInput(it, index, inputType);
|
||||||
if (str === oldStr) {
|
const oldStr = it;
|
||||||
|
it = newPart.appendUntilRejected(it, inputType);
|
||||||
|
if (it === oldStr) {
|
||||||
// nothing changed, break out of this infinite loop and log an error
|
// nothing changed, break out of this infinite loop and log an error
|
||||||
console.error(`Failed to update model for input (str ${str}) (type ${inputType})`);
|
console.error(`Failed to update model for input (str ${it}) (type ${inputType})`);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
this.insertPart(index, newPart);
|
this.insertPart(index, newPart);
|
||||||
|
|
|
@ -20,12 +20,13 @@ import { Room } from "matrix-js-sdk/src/matrix";
|
||||||
import { MatrixClientPeg } from "../../MatrixClientPeg";
|
import { MatrixClientPeg } from "../../MatrixClientPeg";
|
||||||
import { SettingLevel } from "../../settings/SettingLevel";
|
import { SettingLevel } from "../../settings/SettingLevel";
|
||||||
import SettingsStore from "../../settings/SettingsStore";
|
import SettingsStore from "../../settings/SettingsStore";
|
||||||
|
import { filterBoolean } from "../../utils/arrays";
|
||||||
|
|
||||||
export const useRecentSearches = (): [Room[], () => void] => {
|
export const useRecentSearches = (): [Room[], () => void] => {
|
||||||
const [rooms, setRooms] = useState(() => {
|
const [rooms, setRooms] = useState(() => {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
const recents = SettingsStore.getValue<string[]>("SpotlightSearch.recentSearches", null);
|
const recents = SettingsStore.getValue<string[]>("SpotlightSearch.recentSearches", null);
|
||||||
return recents.map((r) => cli.getRoom(r)).filter(Boolean) as Room[];
|
return filterBoolean(recents.map((r) => cli.getRoom(r)));
|
||||||
});
|
});
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
|
@ -67,7 +67,7 @@ type Mapper<T> = (...args: any[]) => T;
|
||||||
* {@link useEventEmitterState}
|
* {@link useEventEmitterState}
|
||||||
*/
|
*/
|
||||||
export function useTypedEventEmitterState<T, Events extends string, Arguments extends ListenerMap<Events>>(
|
export function useTypedEventEmitterState<T, Events extends string, Arguments extends ListenerMap<Events>>(
|
||||||
emitter: TypedEventEmitter<Events, Arguments>,
|
emitter: TypedEventEmitter<Events, Arguments> | undefined,
|
||||||
eventName: Events,
|
eventName: Events,
|
||||||
fn: Mapper<T>,
|
fn: Mapper<T>,
|
||||||
): T {
|
): T {
|
||||||
|
|
|
@ -107,7 +107,7 @@ export const usePublicRoomDirectory = (): {
|
||||||
"org.matrix.msc3827.stable",
|
"org.matrix.msc3827.stable",
|
||||||
))
|
))
|
||||||
? Array.from<RoomType | null>(roomTypes)
|
? Array.from<RoomType | null>(roomTypes)
|
||||||
: null,
|
: undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -84,7 +84,7 @@ function useUserOnboardingContextValue<T>(defaultValue: T, callback: (cli: Matri
|
||||||
|
|
||||||
export function useUserOnboardingContext(): UserOnboardingContext {
|
export function useUserOnboardingContext(): UserOnboardingContext {
|
||||||
const hasAvatar = useUserOnboardingContextValue(false, async (cli) => {
|
const hasAvatar = useUserOnboardingContextValue(false, async (cli) => {
|
||||||
const profile = await cli.getProfileInfo(cli.getUserId());
|
const profile = await cli.getProfileInfo(cli.getUserId()!);
|
||||||
return Boolean(profile?.avatar_url);
|
return Boolean(profile?.avatar_url);
|
||||||
});
|
});
|
||||||
const hasDevices = useUserOnboardingContextValue(false, async (cli) => {
|
const hasDevices = useUserOnboardingContextValue(false, async (cli) => {
|
||||||
|
|
|
@ -21,7 +21,7 @@ import { Direction } from "matrix-js-sdk/src/matrix";
|
||||||
/* eslint-disable camelcase */
|
/* eslint-disable camelcase */
|
||||||
export interface ICrawlerCheckpoint {
|
export interface ICrawlerCheckpoint {
|
||||||
roomId: string;
|
roomId: string;
|
||||||
token: string;
|
token: string | null;
|
||||||
fullCrawl?: boolean;
|
fullCrawl?: boolean;
|
||||||
direction: Direction;
|
direction: Direction;
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,7 @@ export default class EventIndex extends EventEmitter {
|
||||||
|
|
||||||
public async init(): Promise<void> {
|
public async init(): Promise<void> {
|
||||||
const indexManager = PlatformPeg.get()?.getEventIndexingManager();
|
const indexManager = PlatformPeg.get()?.getEventIndexingManager();
|
||||||
|
if (!indexManager) return;
|
||||||
|
|
||||||
this.crawlerCheckpoints = await indexManager.loadCheckpoints();
|
this.crawlerCheckpoints = await indexManager.loadCheckpoints();
|
||||||
logger.log("EventIndex: Loaded checkpoints", this.crawlerCheckpoints);
|
logger.log("EventIndex: Loaded checkpoints", this.crawlerCheckpoints);
|
||||||
|
@ -93,6 +94,7 @@ export default class EventIndex extends EventEmitter {
|
||||||
*/
|
*/
|
||||||
public async addInitialCheckpoints(): Promise<void> {
|
public async addInitialCheckpoints(): Promise<void> {
|
||||||
const indexManager = PlatformPeg.get()?.getEventIndexingManager();
|
const indexManager = PlatformPeg.get()?.getEventIndexingManager();
|
||||||
|
if (!indexManager) return;
|
||||||
const client = MatrixClientPeg.get();
|
const client = MatrixClientPeg.get();
|
||||||
const rooms = client.getRooms();
|
const rooms = client.getRooms();
|
||||||
|
|
||||||
|
@ -160,6 +162,7 @@ export default class EventIndex extends EventEmitter {
|
||||||
*/
|
*/
|
||||||
private onSync = async (state: SyncState, prevState: SyncState | null, data?: ISyncStateData): Promise<void> => {
|
private onSync = async (state: SyncState, prevState: SyncState | null, data?: ISyncStateData): Promise<void> => {
|
||||||
const indexManager = PlatformPeg.get()?.getEventIndexingManager();
|
const indexManager = PlatformPeg.get()?.getEventIndexingManager();
|
||||||
|
if (!indexManager) return;
|
||||||
|
|
||||||
if (prevState === "PREPARED" && state === "SYNCING") {
|
if (prevState === "PREPARED" && state === "SYNCING") {
|
||||||
// If our indexer is empty we're most likely running Element the
|
// If our indexer is empty we're most likely running Element the
|
||||||
|
@ -230,6 +233,7 @@ export default class EventIndex extends EventEmitter {
|
||||||
*/
|
*/
|
||||||
private redactEvent = async (ev: MatrixEvent): Promise<void> => {
|
private redactEvent = async (ev: MatrixEvent): Promise<void> => {
|
||||||
const indexManager = PlatformPeg.get()?.getEventIndexingManager();
|
const indexManager = PlatformPeg.get()?.getEventIndexingManager();
|
||||||
|
if (!indexManager) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await indexManager.deleteEvent(ev.getAssociatedId());
|
await indexManager.deleteEvent(ev.getAssociatedId());
|
||||||
|
@ -355,6 +359,7 @@ export default class EventIndex extends EventEmitter {
|
||||||
|
|
||||||
private async addRoomCheckpoint(roomId: string, fullCrawl = false): Promise<void> {
|
private async addRoomCheckpoint(roomId: string, fullCrawl = false): Promise<void> {
|
||||||
const indexManager = PlatformPeg.get()?.getEventIndexingManager();
|
const indexManager = PlatformPeg.get()?.getEventIndexingManager();
|
||||||
|
if (!indexManager) return;
|
||||||
const client = MatrixClientPeg.get();
|
const client = MatrixClientPeg.get();
|
||||||
const room = client.getRoom(roomId);
|
const room = client.getRoom(roomId);
|
||||||
|
|
||||||
|
@ -403,6 +408,7 @@ export default class EventIndex extends EventEmitter {
|
||||||
|
|
||||||
const client = MatrixClientPeg.get();
|
const client = MatrixClientPeg.get();
|
||||||
const indexManager = PlatformPeg.get()?.getEventIndexingManager();
|
const indexManager = PlatformPeg.get()?.getEventIndexingManager();
|
||||||
|
if (!indexManager) return;
|
||||||
|
|
||||||
this.crawler = {
|
this.crawler = {
|
||||||
cancel: () => {
|
cancel: () => {
|
||||||
|
@ -653,7 +659,7 @@ export default class EventIndex extends EventEmitter {
|
||||||
const indexManager = PlatformPeg.get()?.getEventIndexingManager();
|
const indexManager = PlatformPeg.get()?.getEventIndexingManager();
|
||||||
this.removeListeners();
|
this.removeListeners();
|
||||||
this.stopCrawler();
|
this.stopCrawler();
|
||||||
await indexManager.closeEventIndex();
|
await indexManager?.closeEventIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -665,9 +671,9 @@ export default class EventIndex extends EventEmitter {
|
||||||
* @return {Promise<IResultRoomEvents[]>} A promise that will resolve to an array
|
* @return {Promise<IResultRoomEvents[]>} A promise that will resolve to an array
|
||||||
* of search results once the search is done.
|
* of search results once the search is done.
|
||||||
*/
|
*/
|
||||||
public async search(searchArgs: ISearchArgs): Promise<IResultRoomEvents> {
|
public async search(searchArgs: ISearchArgs): Promise<IResultRoomEvents | undefined> {
|
||||||
const indexManager = PlatformPeg.get()?.getEventIndexingManager();
|
const indexManager = PlatformPeg.get()?.getEventIndexingManager();
|
||||||
return indexManager.searchEventIndex(searchArgs);
|
return indexManager?.searchEventIndex(searchArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -699,6 +705,7 @@ export default class EventIndex extends EventEmitter {
|
||||||
): Promise<MatrixEvent[]> {
|
): Promise<MatrixEvent[]> {
|
||||||
const client = MatrixClientPeg.get();
|
const client = MatrixClientPeg.get();
|
||||||
const indexManager = PlatformPeg.get()?.getEventIndexingManager();
|
const indexManager = PlatformPeg.get()?.getEventIndexingManager();
|
||||||
|
if (!indexManager) return [];
|
||||||
|
|
||||||
const loadArgs: ILoadArgs = {
|
const loadArgs: ILoadArgs = {
|
||||||
roomId: room.roomId,
|
roomId: room.roomId,
|
||||||
|
|
|
@ -74,8 +74,8 @@ export class IntegrationManagers {
|
||||||
}
|
}
|
||||||
|
|
||||||
private setupConfiguredManager(): void {
|
private setupConfiguredManager(): void {
|
||||||
const apiUrl: string = SdkConfig.get("integrations_rest_url");
|
const apiUrl = SdkConfig.get("integrations_rest_url");
|
||||||
const uiUrl: string = SdkConfig.get("integrations_ui_url");
|
const uiUrl = SdkConfig.get("integrations_ui_url");
|
||||||
|
|
||||||
if (apiUrl && uiUrl) {
|
if (apiUrl && uiUrl) {
|
||||||
this.managers.push(new IntegrationManagerInstance(Kind.Config, apiUrl, uiUrl));
|
this.managers.push(new IntegrationManagerInstance(Kind.Config, apiUrl, uiUrl));
|
||||||
|
|
|
@ -172,16 +172,16 @@ export const options: Opts = {
|
||||||
return {
|
return {
|
||||||
// @ts-ignore see https://linkify.js.org/docs/options.html
|
// @ts-ignore see https://linkify.js.org/docs/options.html
|
||||||
click: function (e: MouseEvent) {
|
click: function (e: MouseEvent) {
|
||||||
const userId = parsePermalink(href).userId;
|
const userId = parsePermalink(href)?.userId;
|
||||||
onUserClick(e, userId);
|
if (userId) onUserClick(e, userId);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
case Type.RoomAlias:
|
case Type.RoomAlias:
|
||||||
return {
|
return {
|
||||||
// @ts-ignore see https://linkify.js.org/docs/options.html
|
// @ts-ignore see https://linkify.js.org/docs/options.html
|
||||||
click: function (e: MouseEvent) {
|
click: function (e: MouseEvent) {
|
||||||
const alias = parsePermalink(href).roomIdOrAlias;
|
const alias = parsePermalink(href)?.roomIdOrAlias;
|
||||||
onAliasClick(e, alias);
|
if (alias) onAliasClick(e, alias);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ export const ROOM_RULE_TYPES = [RULE_ROOM, "m.room.rule.room", "org.matrix.mjoln
|
||||||
export const SERVER_RULE_TYPES = [RULE_SERVER, "m.room.rule.server", "org.matrix.mjolnir.rule.server"];
|
export const SERVER_RULE_TYPES = [RULE_SERVER, "m.room.rule.server", "org.matrix.mjolnir.rule.server"];
|
||||||
export const ALL_RULE_TYPES = [...USER_RULE_TYPES, ...ROOM_RULE_TYPES, ...SERVER_RULE_TYPES];
|
export const ALL_RULE_TYPES = [...USER_RULE_TYPES, ...ROOM_RULE_TYPES, ...SERVER_RULE_TYPES];
|
||||||
|
|
||||||
export function ruleTypeToStable(rule: string): string {
|
export function ruleTypeToStable(rule: string): string | null {
|
||||||
if (USER_RULE_TYPES.includes(rule)) {
|
if (USER_RULE_TYPES.includes(rule)) {
|
||||||
return RULE_USER;
|
return RULE_USER;
|
||||||
}
|
}
|
||||||
|
@ -68,9 +68,11 @@ export class BanList {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async banEntity(kind: string, entity: string, reason: string): Promise<any> {
|
public async banEntity(kind: string, entity: string, reason: string): Promise<any> {
|
||||||
|
const type = ruleTypeToStable(kind);
|
||||||
|
if (!type) return; // unknown rule type
|
||||||
await MatrixClientPeg.get().sendStateEvent(
|
await MatrixClientPeg.get().sendStateEvent(
|
||||||
this._roomId,
|
this._roomId,
|
||||||
ruleTypeToStable(kind),
|
type,
|
||||||
{
|
{
|
||||||
entity: entity,
|
entity: entity,
|
||||||
reason: reason,
|
reason: reason,
|
||||||
|
@ -78,12 +80,14 @@ export class BanList {
|
||||||
},
|
},
|
||||||
"rule:" + entity,
|
"rule:" + entity,
|
||||||
);
|
);
|
||||||
this._rules.push(new ListRule(entity, RECOMMENDATION_BAN, reason, ruleTypeToStable(kind)));
|
this._rules.push(new ListRule(entity, RECOMMENDATION_BAN, reason, type));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async unbanEntity(kind: string, entity: string): Promise<any> {
|
public async unbanEntity(kind: string, entity: string): Promise<any> {
|
||||||
|
const type = ruleTypeToStable(kind);
|
||||||
|
if (!type) return; // unknown rule type
|
||||||
// Empty state event is effectively deleting it.
|
// Empty state event is effectively deleting it.
|
||||||
await MatrixClientPeg.get().sendStateEvent(this._roomId, ruleTypeToStable(kind), {}, "rule:" + entity);
|
await MatrixClientPeg.get().sendStateEvent(this._roomId, type, {}, "rule:" + entity);
|
||||||
this._rules = this._rules.filter((r) => {
|
this._rules = this._rules.filter((r) => {
|
||||||
if (r.kind !== ruleTypeToStable(kind)) return true;
|
if (r.kind !== ruleTypeToStable(kind)) return true;
|
||||||
if (r.entity !== entity) return true;
|
if (r.entity !== entity) return true;
|
||||||
|
@ -103,6 +107,7 @@ export class BanList {
|
||||||
if (!ev.getStateKey()) continue;
|
if (!ev.getStateKey()) continue;
|
||||||
|
|
||||||
const kind = ruleTypeToStable(eventType);
|
const kind = ruleTypeToStable(eventType);
|
||||||
|
if (!kind) continue; // unknown type
|
||||||
|
|
||||||
const entity = ev.getContent()["entity"];
|
const entity = ev.getContent()["entity"];
|
||||||
const recommendation = ev.getContent()["recommendation"];
|
const recommendation = ev.getContent()["recommendation"];
|
||||||
|
|
|
@ -40,15 +40,12 @@ async function collectBugReport(opts: IOpts = {}, gzipLogs = true): Promise<Form
|
||||||
const progressCallback = opts.progressCallback || ((): void => {});
|
const progressCallback = opts.progressCallback || ((): void => {});
|
||||||
|
|
||||||
progressCallback(_t("Collecting app version information"));
|
progressCallback(_t("Collecting app version information"));
|
||||||
let version = "UNKNOWN";
|
let version: string | undefined;
|
||||||
try {
|
try {
|
||||||
version = await PlatformPeg.get().getAppVersion();
|
version = await PlatformPeg.get()?.getAppVersion();
|
||||||
} catch (err) {} // PlatformPeg already logs this.
|
} catch (err) {} // PlatformPeg already logs this.
|
||||||
|
|
||||||
let userAgent = "UNKNOWN";
|
const userAgent = window.navigator?.userAgent ?? "UNKNOWN";
|
||||||
if (window.navigator && window.navigator.userAgent) {
|
|
||||||
userAgent = window.navigator.userAgent;
|
|
||||||
}
|
|
||||||
|
|
||||||
let installedPWA = "UNKNOWN";
|
let installedPWA = "UNKNOWN";
|
||||||
try {
|
try {
|
||||||
|
@ -69,7 +66,7 @@ async function collectBugReport(opts: IOpts = {}, gzipLogs = true): Promise<Form
|
||||||
const body = new FormData();
|
const body = new FormData();
|
||||||
body.append("text", opts.userText || "User did not supply any additional text.");
|
body.append("text", opts.userText || "User did not supply any additional text.");
|
||||||
body.append("app", opts.customApp || "element-web");
|
body.append("app", opts.customApp || "element-web");
|
||||||
body.append("version", version);
|
body.append("version", version ?? "UNKNOWN");
|
||||||
body.append("user_agent", userAgent);
|
body.append("user_agent", userAgent);
|
||||||
body.append("installed_pwa", installedPWA);
|
body.append("installed_pwa", installedPWA);
|
||||||
body.append("touch_input", touchInput);
|
body.append("touch_input", touchInput);
|
||||||
|
|
|
@ -19,7 +19,7 @@ import Sizer from "./sizer";
|
||||||
|
|
||||||
export default class ResizeItem<C extends IConfig = IConfig> {
|
export default class ResizeItem<C extends IConfig = IConfig> {
|
||||||
public readonly domNode: HTMLElement;
|
public readonly domNode: HTMLElement;
|
||||||
protected readonly id: string;
|
protected readonly id: string | null;
|
||||||
protected reverse: boolean;
|
protected reverse: boolean;
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
|
|
|
@ -34,7 +34,7 @@ interface IClassNames {
|
||||||
export interface IConfig {
|
export interface IConfig {
|
||||||
onResizeStart?(): void;
|
onResizeStart?(): void;
|
||||||
onResizeStop?(): void;
|
onResizeStop?(): void;
|
||||||
onResized?(size: number, id: string, element: HTMLElement): void;
|
onResized?(size: number | null, id: string | null, element: HTMLElement): void;
|
||||||
handler?: HTMLDivElement;
|
handler?: HTMLDivElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ export default class Resizer<C extends IConfig = IConfig> {
|
||||||
resizeHandle: HTMLDivElement,
|
resizeHandle: HTMLDivElement,
|
||||||
resizer: Resizer,
|
resizer: Resizer,
|
||||||
sizer: Sizer,
|
sizer: Sizer,
|
||||||
container: HTMLElement,
|
container?: HTMLElement,
|
||||||
): ResizeItem;
|
): ResizeItem;
|
||||||
createSizer(containerElement: HTMLElement, vertical: boolean, reverse: boolean): Sizer;
|
createSizer(containerElement: HTMLElement, vertical: boolean, reverse: boolean): Sizer;
|
||||||
},
|
},
|
||||||
|
|
|
@ -53,8 +53,8 @@ type CryptoContext = {
|
||||||
};
|
};
|
||||||
|
|
||||||
type DeviceContext = {
|
type DeviceContext = {
|
||||||
device_id: string;
|
device_id?: string;
|
||||||
mx_local_settings: string;
|
mx_local_settings: string | null;
|
||||||
modernizr_missing_features?: string;
|
modernizr_missing_features?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -82,7 +82,7 @@ export default class ThreepidInviteStore extends EventEmitter {
|
||||||
const results: IPersistedThreepidInvite[] = [];
|
const results: IPersistedThreepidInvite[] = [];
|
||||||
for (let i = 0; i < localStorage.length; i++) {
|
for (let i = 0; i < localStorage.length; i++) {
|
||||||
const keyName = localStorage.key(i);
|
const keyName = localStorage.key(i);
|
||||||
if (!keyName.startsWith(STORAGE_PREFIX)) continue;
|
if (!keyName?.startsWith(STORAGE_PREFIX)) continue;
|
||||||
results.push(JSON.parse(localStorage.getItem(keyName)) as IPersistedThreepidInvite);
|
results.push(JSON.parse(localStorage.getItem(keyName)) as IPersistedThreepidInvite);
|
||||||
}
|
}
|
||||||
return results;
|
return results;
|
||||||
|
|
|
@ -160,7 +160,7 @@ export default class RightPanelStore extends ReadyWatchingStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public setCards(cards: IRightPanelCard[], allowClose = true, roomId: string = null): void {
|
public setCards(cards: IRightPanelCard[], allowClose = true, roomId: string | null = null): void {
|
||||||
// This function sets the history of the right panel and shows the right panel if not already visible.
|
// This function sets the history of the right panel and shows the right panel if not already visible.
|
||||||
const rId = roomId ?? this.viewedRoomId;
|
const rId = roomId ?? this.viewedRoomId;
|
||||||
const history = cards.map((c) => ({ phase: c.phase, state: c.state ?? {} }));
|
const history = cards.map((c) => ({ phase: c.phase, state: c.state ?? {} }));
|
||||||
|
@ -170,7 +170,7 @@ export default class RightPanelStore extends ReadyWatchingStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Appends a card to the history and shows the right panel if not already visible
|
// Appends a card to the history and shows the right panel if not already visible
|
||||||
public pushCard(card: IRightPanelCard, allowClose = true, roomId: string = null): void {
|
public pushCard(card: IRightPanelCard, allowClose = true, roomId: string | null = null): void {
|
||||||
const rId = roomId ?? this.viewedRoomId;
|
const rId = roomId ?? this.viewedRoomId;
|
||||||
const redirect = this.getVerificationRedirect(card);
|
const redirect = this.getVerificationRedirect(card);
|
||||||
const targetPhase = redirect?.phase ?? card.phase;
|
const targetPhase = redirect?.phase ?? card.phase;
|
||||||
|
@ -196,7 +196,7 @@ export default class RightPanelStore extends ReadyWatchingStore {
|
||||||
this.emitAndUpdateSettings();
|
this.emitAndUpdateSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
public popCard(roomId: string = null): IRightPanelCard {
|
public popCard(roomId: string | null = null): IRightPanelCard | undefined {
|
||||||
const rId = roomId ?? this.viewedRoomId;
|
const rId = roomId ?? this.viewedRoomId;
|
||||||
if (!this.byRoom[rId]) return;
|
if (!this.byRoom[rId]) return;
|
||||||
|
|
||||||
|
@ -304,7 +304,7 @@ export default class RightPanelStore extends ReadyWatchingStore {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private getVerificationRedirect(card: IRightPanelCard): IRightPanelCard {
|
private getVerificationRedirect(card: IRightPanelCard): IRightPanelCard | null {
|
||||||
if (card.phase === RightPanelPhases.RoomMemberInfo && card.state) {
|
if (card.phase === RightPanelPhases.RoomMemberInfo && card.state) {
|
||||||
// RightPanelPhases.RoomMemberInfo -> needs to be changed to RightPanelPhases.EncryptionPanel if there is a pending verification request
|
// RightPanelPhases.RoomMemberInfo -> needs to be changed to RightPanelPhases.EncryptionPanel if there is a pending verification request
|
||||||
const { member } = card.state;
|
const { member } = card.state;
|
||||||
|
@ -322,8 +322,8 @@ export default class RightPanelStore extends ReadyWatchingStore {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private isPhaseValid(targetPhase: RightPanelPhases, isViewingRoom: boolean): boolean {
|
private isPhaseValid(targetPhase: RightPanelPhases | null, isViewingRoom: boolean): boolean {
|
||||||
if (!RightPanelPhases[targetPhase]) {
|
if (!targetPhase || !RightPanelPhases[targetPhase]) {
|
||||||
logger.warn(`Tried to switch right panel to unknown phase: ${targetPhase}`);
|
logger.warn(`Tried to switch right panel to unknown phase: ${targetPhase}`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ export interface IRightPanelCardStateStored {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IRightPanelCard {
|
export interface IRightPanelCard {
|
||||||
phase: RightPanelPhases;
|
phase: RightPanelPhases | null;
|
||||||
state?: IRightPanelCardState;
|
state?: IRightPanelCardState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,10 +90,10 @@ export function convertCardToStore(panelState: IRightPanelCard): IRightPanelCard
|
||||||
spaceId: state.spaceId,
|
spaceId: state.spaceId,
|
||||||
isInitialEventHighlighted: state.isInitialEventHighlighted,
|
isInitialEventHighlighted: state.isInitialEventHighlighted,
|
||||||
initialEventScrollIntoView: state.initialEventScrollIntoView,
|
initialEventScrollIntoView: state.initialEventScrollIntoView,
|
||||||
threadHeadEventId: !!state?.threadHeadEvent?.getId() ? panelState.state.threadHeadEvent.getId() : undefined,
|
threadHeadEventId: !!state?.threadHeadEvent?.getId() ? state.threadHeadEvent.getId() : undefined,
|
||||||
memberInfoEventId: !!state?.memberInfoEvent?.getId() ? panelState.state.memberInfoEvent.getId() : undefined,
|
memberInfoEventId: !!state?.memberInfoEvent?.getId() ? state.memberInfoEvent.getId() : undefined,
|
||||||
initialEventId: !!state?.initialEvent?.getId() ? panelState.state.initialEvent.getId() : undefined,
|
initialEventId: !!state?.initialEvent?.getId() ? state.initialEvent.getId() : undefined,
|
||||||
memberId: !!state?.member?.userId ? panelState.state.member.userId : undefined,
|
memberId: !!state?.member?.userId ? state.member.userId : undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
return { state: stateStored, phase: panelState.phase };
|
return { state: stateStored, phase: panelState.phase };
|
||||||
|
@ -113,7 +113,7 @@ function convertStoreToCard(panelStateStore: IRightPanelCardStored, room: Room):
|
||||||
? room.findEventById(stateStored.memberInfoEventId)
|
? room.findEventById(stateStored.memberInfoEventId)
|
||||||
: undefined,
|
: undefined,
|
||||||
initialEvent: !!stateStored?.initialEventId ? room.findEventById(stateStored.initialEventId) : undefined,
|
initialEvent: !!stateStored?.initialEventId ? room.findEventById(stateStored.initialEventId) : undefined,
|
||||||
member: !!stateStored?.memberId ? room.getMember(stateStored.memberId) : undefined,
|
member: (!!stateStored?.memberId && room.getMember(stateStored.memberId)) || undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
return { state: state, phase: panelStateStore.phase };
|
return { state: state, phase: panelStateStore.phase };
|
||||||
|
|
|
@ -57,7 +57,7 @@ export interface RoomListStore extends EventEmitter {
|
||||||
* @param tagId tag to get the sort algorithm for
|
* @param tagId tag to get the sort algorithm for
|
||||||
* @returns the sort algorithm
|
* @returns the sort algorithm
|
||||||
*/
|
*/
|
||||||
getTagSorting(tagId: TagID): SortAlgorithm;
|
getTagSorting(tagId: TagID): SortAlgorithm | null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the list algorithm for the specified tag.
|
* Set the list algorithm for the specified tag.
|
||||||
|
@ -71,7 +71,7 @@ export interface RoomListStore extends EventEmitter {
|
||||||
* @param tagId tag to get the list algorithm for
|
* @param tagId tag to get the list algorithm for
|
||||||
* @returns the list algorithm
|
* @returns the list algorithm
|
||||||
*/
|
*/
|
||||||
getListOrder(tagId: TagID): ListAlgorithm;
|
getListOrder(tagId: TagID): ListAlgorithm | null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Regenerates the room whole room list, discarding any previous results.
|
* Regenerates the room whole room list, discarding any previous results.
|
||||||
|
|
|
@ -51,7 +51,7 @@ export default class RoomListLayoutStore extends AsyncStoreWithClient<IState> {
|
||||||
if (!this.layoutMap.has(tagId)) {
|
if (!this.layoutMap.has(tagId)) {
|
||||||
this.layoutMap.set(tagId, new ListLayout(tagId));
|
this.layoutMap.set(tagId, new ListLayout(tagId));
|
||||||
}
|
}
|
||||||
return this.layoutMap.get(tagId);
|
return this.layoutMap.get(tagId)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: this primarily exists for debugging, and isn't really intended to be used by anything.
|
// Note: this primarily exists for debugging, and isn't really intended to be used by anything.
|
||||||
|
|
|
@ -404,7 +404,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> implements
|
||||||
localStorage.setItem(`mx_tagSort_${tagId}`, sort);
|
localStorage.setItem(`mx_tagSort_${tagId}`, sort);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getTagSorting(tagId: TagID): SortAlgorithm {
|
public getTagSorting(tagId: TagID): SortAlgorithm | null {
|
||||||
return this.algorithm.getTagSorting(tagId);
|
return this.algorithm.getTagSorting(tagId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -443,7 +443,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> implements
|
||||||
localStorage.setItem(`mx_listOrder_${tagId}`, order);
|
localStorage.setItem(`mx_listOrder_${tagId}`, order);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getListOrder(tagId: TagID): ListAlgorithm {
|
public getListOrder(tagId: TagID): ListAlgorithm | null {
|
||||||
return this.algorithm.getListOrdering(tagId);
|
return this.algorithm.getListOrdering(tagId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,7 @@ export const sortRooms = (rooms: Room[]): Room[] => {
|
||||||
// See https://github.com/vector-im/element-web/issues/14458
|
// See https://github.com/vector-im/element-web/issues/14458
|
||||||
let myUserId = "";
|
let myUserId = "";
|
||||||
if (MatrixClientPeg.get()) {
|
if (MatrixClientPeg.get()) {
|
||||||
myUserId = MatrixClientPeg.get().getUserId();
|
myUserId = MatrixClientPeg.get().getUserId()!;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tsCache: { [roomId: string]: number } = {};
|
const tsCache: { [roomId: string]: number } = {};
|
||||||
|
|
|
@ -23,7 +23,7 @@ import { _t } from "../../../languageHandler";
|
||||||
|
|
||||||
export class LegacyCallAnswerEventPreview implements IPreview {
|
export class LegacyCallAnswerEventPreview implements IPreview {
|
||||||
public getTextFor(event: MatrixEvent, tagId?: TagID): string {
|
public getTextFor(event: MatrixEvent, tagId?: TagID): string {
|
||||||
if (shouldPrefixMessagesIn(event.getRoomId(), tagId)) {
|
if (shouldPrefixMessagesIn(event.getRoomId()!, tagId)) {
|
||||||
if (isSelf(event)) {
|
if (isSelf(event)) {
|
||||||
return _t("You joined the call");
|
return _t("You joined the call");
|
||||||
} else {
|
} else {
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue