mirror of
https://github.com/vector-im/element-web.git
synced 2024-11-15 12:45:11 +08:00
Enable @typescript-eslint/explicit-function-return-type
in /src (#9788)
* Enable `@typescript-eslint/explicit-member-accessibility` on /src * Prettier * Enable `@typescript-eslint/explicit-function-return-type` in /src * Fix types * tsc strict fixes * Delint * Fix test * Fix bad merge
This commit is contained in:
parent
7a36ba0fde
commit
030b7e90bf
@ -100,8 +100,12 @@ module.exports = {
|
|||||||
files: ["src/**/*.{ts,tsx}", "test/**/*.{ts,tsx}", "cypress/**/*.ts"],
|
files: ["src/**/*.{ts,tsx}", "test/**/*.{ts,tsx}", "cypress/**/*.ts"],
|
||||||
extends: ["plugin:matrix-org/typescript", "plugin:matrix-org/react"],
|
extends: ["plugin:matrix-org/typescript", "plugin:matrix-org/react"],
|
||||||
rules: {
|
rules: {
|
||||||
// temporary disabled
|
"@typescript-eslint/explicit-function-return-type": [
|
||||||
"@typescript-eslint/explicit-function-return-type": "off",
|
"error",
|
||||||
|
{
|
||||||
|
allowExpressions: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
// Things we do that break the ideal style
|
// Things we do that break the ideal style
|
||||||
"prefer-promise-reject-errors": "off",
|
"prefer-promise-reject-errors": "off",
|
||||||
|
8
src/@types/diff-dom.d.ts
vendored
8
src/@types/diff-dom.d.ts
vendored
@ -20,10 +20,10 @@ declare module "diff-dom" {
|
|||||||
name: string;
|
name: string;
|
||||||
text?: string;
|
text?: string;
|
||||||
route: number[];
|
route: number[];
|
||||||
value: string;
|
value: HTMLElement | string;
|
||||||
element: unknown;
|
element: HTMLElement | string;
|
||||||
oldValue: string;
|
oldValue: HTMLElement | string;
|
||||||
newValue: string;
|
newValue: HTMLElement | string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IOpts {}
|
interface IOpts {}
|
||||||
|
@ -15,7 +15,7 @@ limitations under the License.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// This is intended to fix re-resizer because of its unguarded `instanceof TouchEvent` checks.
|
// This is intended to fix re-resizer because of its unguarded `instanceof TouchEvent` checks.
|
||||||
export function polyfillTouchEvent() {
|
export function polyfillTouchEvent(): void {
|
||||||
// Firefox doesn't have touch events without touch devices being present, so create a fake
|
// Firefox doesn't have touch events without touch devices being present, so create a fake
|
||||||
// one we can rely on lying about.
|
// one we can rely on lying about.
|
||||||
if (!window.TouchEvent) {
|
if (!window.TouchEvent) {
|
||||||
|
@ -47,7 +47,7 @@ export default class AsyncWrapper extends React.Component<IProps, IState> {
|
|||||||
error: null,
|
error: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
public componentDidMount() {
|
public componentDidMount(): void {
|
||||||
// XXX: temporary logging to try to diagnose
|
// XXX: temporary logging to try to diagnose
|
||||||
// https://github.com/vector-im/element-web/issues/3148
|
// https://github.com/vector-im/element-web/issues/3148
|
||||||
logger.log("Starting load of AsyncWrapper for modal");
|
logger.log("Starting load of AsyncWrapper for modal");
|
||||||
@ -69,15 +69,15 @@ export default class AsyncWrapper extends React.Component<IProps, IState> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentWillUnmount() {
|
public componentWillUnmount(): void {
|
||||||
this.unmounted = true;
|
this.unmounted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private onWrapperCancelClick = () => {
|
private onWrapperCancelClick = (): void => {
|
||||||
this.props.onFinished(false);
|
this.props.onFinished(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
public render() {
|
public render(): JSX.Element {
|
||||||
if (this.state.component) {
|
if (this.state.component) {
|
||||||
const Component = this.state.component;
|
const Component = this.state.component;
|
||||||
return <Component {...this.props} />;
|
return <Component {...this.props} />;
|
||||||
|
@ -137,7 +137,12 @@ export function getInitialLetter(name: string): string {
|
|||||||
return split(name, "", 1)[0].toUpperCase();
|
return split(name, "", 1)[0].toUpperCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function avatarUrlForRoom(room: Room, width: number, height: number, resizeMethod?: ResizeMethod) {
|
export function avatarUrlForRoom(
|
||||||
|
room: Room,
|
||||||
|
width: number,
|
||||||
|
height: number,
|
||||||
|
resizeMethod?: ResizeMethod,
|
||||||
|
): string | null {
|
||||||
if (!room) return null; // null-guard
|
if (!room) return null; // null-guard
|
||||||
|
|
||||||
if (room.getMxcAvatarUrl()) {
|
if (room.getMxcAvatarUrl()) {
|
||||||
|
@ -272,7 +272,7 @@ export default abstract class BasePlatform {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public setLanguage(preferredLangs: string[]) {}
|
public setLanguage(preferredLangs: string[]): void {}
|
||||||
|
|
||||||
public setSpellCheckEnabled(enabled: boolean): void {}
|
public setSpellCheckEnabled(enabled: boolean): void {}
|
||||||
|
|
||||||
@ -280,7 +280,7 @@ export default abstract class BasePlatform {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public setSpellCheckLanguages(preferredLangs: string[]) {}
|
public setSpellCheckLanguages(preferredLangs: string[]): void {}
|
||||||
|
|
||||||
public getSpellCheckLanguages(): Promise<string[]> | null {
|
public getSpellCheckLanguages(): Promise<string[]> | null {
|
||||||
return null;
|
return null;
|
||||||
|
@ -40,7 +40,7 @@ export class BlurhashEncoder {
|
|||||||
this.worker.onmessage = this.onMessage;
|
this.worker.onmessage = this.onMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
private onMessage = (ev: MessageEvent<IBlurhashWorkerResponse>) => {
|
private onMessage = (ev: MessageEvent<IBlurhashWorkerResponse>): void => {
|
||||||
const { seq, blurhash } = ev.data;
|
const { seq, blurhash } = ev.data;
|
||||||
const deferred = this.pendingDeferredMap.get(seq);
|
const deferred = this.pendingDeferredMap.get(seq);
|
||||||
if (deferred) {
|
if (deferred) {
|
||||||
|
@ -68,16 +68,20 @@ interface IMediaConfig {
|
|||||||
* @param {File} imageFile The file to load in an image element.
|
* @param {File} imageFile The file to load in an image element.
|
||||||
* @return {Promise} A promise that resolves with the html image element.
|
* @return {Promise} A promise that resolves with the html image element.
|
||||||
*/
|
*/
|
||||||
async function loadImageElement(imageFile: File) {
|
async function loadImageElement(imageFile: File): Promise<{
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
img: HTMLImageElement;
|
||||||
|
}> {
|
||||||
// Load the file into an html element
|
// Load the file into an html element
|
||||||
const img = new Image();
|
const img = new Image();
|
||||||
const objectUrl = URL.createObjectURL(imageFile);
|
const objectUrl = URL.createObjectURL(imageFile);
|
||||||
const imgPromise = new Promise((resolve, reject) => {
|
const imgPromise = new Promise((resolve, reject) => {
|
||||||
img.onload = function () {
|
img.onload = function (): void {
|
||||||
URL.revokeObjectURL(objectUrl);
|
URL.revokeObjectURL(objectUrl);
|
||||||
resolve(img);
|
resolve(img);
|
||||||
};
|
};
|
||||||
img.onerror = function (e) {
|
img.onerror = function (e): void {
|
||||||
reject(e);
|
reject(e);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@ -185,13 +189,13 @@ function loadVideoElement(videoFile: File): Promise<HTMLVideoElement> {
|
|||||||
|
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
|
|
||||||
reader.onload = function (ev) {
|
reader.onload = function (ev): void {
|
||||||
// Wait until we have enough data to thumbnail the first frame.
|
// Wait until we have enough data to thumbnail the first frame.
|
||||||
video.onloadeddata = async function () {
|
video.onloadeddata = async function (): Promise<void> {
|
||||||
resolve(video);
|
resolve(video);
|
||||||
video.pause();
|
video.pause();
|
||||||
};
|
};
|
||||||
video.onerror = function (e) {
|
video.onerror = function (e): void {
|
||||||
reject(e);
|
reject(e);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -206,7 +210,7 @@ function loadVideoElement(videoFile: File): Promise<HTMLVideoElement> {
|
|||||||
video.load();
|
video.load();
|
||||||
video.play();
|
video.play();
|
||||||
};
|
};
|
||||||
reader.onerror = function (e) {
|
reader.onerror = function (e): void {
|
||||||
reject(e);
|
reject(e);
|
||||||
};
|
};
|
||||||
reader.readAsDataURL(videoFile);
|
reader.readAsDataURL(videoFile);
|
||||||
@ -253,10 +257,10 @@ function infoForVideoFile(
|
|||||||
function readFileAsArrayBuffer(file: File | Blob): Promise<ArrayBuffer> {
|
function readFileAsArrayBuffer(file: File | Blob): Promise<ArrayBuffer> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.onload = function (e) {
|
reader.onload = function (e): void {
|
||||||
resolve(e.target.result as ArrayBuffer);
|
resolve(e.target.result as ArrayBuffer);
|
||||||
};
|
};
|
||||||
reader.onerror = function (e) {
|
reader.onerror = function (e): void {
|
||||||
reject(e);
|
reject(e);
|
||||||
};
|
};
|
||||||
reader.readAsArrayBuffer(file);
|
reader.readAsArrayBuffer(file);
|
||||||
@ -461,7 +465,7 @@ export default class ContentMessages {
|
|||||||
matrixClient: MatrixClient,
|
matrixClient: MatrixClient,
|
||||||
replyToEvent: MatrixEvent | undefined,
|
replyToEvent: MatrixEvent | undefined,
|
||||||
promBefore?: Promise<any>,
|
promBefore?: Promise<any>,
|
||||||
) {
|
): Promise<void> {
|
||||||
const fileName = file.name || _t("Attachment");
|
const fileName = file.name || _t("Attachment");
|
||||||
const content: Omit<IMediaEventContent, "info"> & { info: Partial<IMediaEventInfo> } = {
|
const content: Omit<IMediaEventContent, "info"> & { info: Partial<IMediaEventInfo> } = {
|
||||||
body: fileName,
|
body: fileName,
|
||||||
@ -491,7 +495,7 @@ export default class ContentMessages {
|
|||||||
this.inprogress.push(upload);
|
this.inprogress.push(upload);
|
||||||
dis.dispatch<UploadStartedPayload>({ action: Action.UploadStarted, upload });
|
dis.dispatch<UploadStartedPayload>({ action: Action.UploadStarted, upload });
|
||||||
|
|
||||||
function onProgress(progress: UploadProgress) {
|
function onProgress(progress: UploadProgress): void {
|
||||||
upload.onProgress(progress);
|
upload.onProgress(progress);
|
||||||
dis.dispatch<UploadProgressPayload>({ action: Action.UploadProgress, upload });
|
dis.dispatch<UploadProgressPayload>({ action: Action.UploadProgress, upload });
|
||||||
}
|
}
|
||||||
@ -568,7 +572,7 @@ export default class ContentMessages {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private isFileSizeAcceptable(file: File) {
|
private isFileSizeAcceptable(file: File): boolean {
|
||||||
if (
|
if (
|
||||||
this.mediaConfig !== null &&
|
this.mediaConfig !== null &&
|
||||||
this.mediaConfig["m.upload.size"] !== undefined &&
|
this.mediaConfig["m.upload.size"] !== undefined &&
|
||||||
@ -599,7 +603,7 @@ export default class ContentMessages {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static sharedInstance() {
|
public static sharedInstance(): ContentMessages {
|
||||||
if (window.mxContentMessages === undefined) {
|
if (window.mxContentMessages === undefined) {
|
||||||
window.mxContentMessages = new ContentMessages();
|
window.mxContentMessages = new ContentMessages();
|
||||||
}
|
}
|
||||||
|
@ -188,7 +188,7 @@ export function wantsDateSeparator(prevEventDate: Date, nextEventDate: Date): bo
|
|||||||
return prevEventDate.getDay() !== nextEventDate.getDay();
|
return prevEventDate.getDay() !== nextEventDate.getDay();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function formatFullDateNoDay(date: Date) {
|
export function formatFullDateNoDay(date: Date): string {
|
||||||
return _t("%(date)s at %(time)s", {
|
return _t("%(date)s at %(time)s", {
|
||||||
date: date.toLocaleDateString().replace(/\//g, "-"),
|
date: date.toLocaleDateString().replace(/\//g, "-"),
|
||||||
time: date.toLocaleTimeString().replace(/:/g, "-"),
|
time: date.toLocaleTimeString().replace(/:/g, "-"),
|
||||||
@ -205,7 +205,7 @@ export function formatFullDateNoDayISO(date: Date): string {
|
|||||||
return date.toISOString();
|
return date.toISOString();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function formatFullDateNoDayNoTime(date: Date) {
|
export function formatFullDateNoDayNoTime(date: Date): string {
|
||||||
return date.getFullYear() + "/" + pad(date.getMonth() + 1) + "/" + pad(date.getDate());
|
return date.getFullYear() + "/" + pad(date.getMonth() + 1) + "/" + pad(date.getDate());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ import { logger } from "matrix-js-sdk/src/logger";
|
|||||||
import { CryptoEvent } from "matrix-js-sdk/src/crypto";
|
import { CryptoEvent } from "matrix-js-sdk/src/crypto";
|
||||||
import { ClientEvent, EventType, RoomStateEvent } from "matrix-js-sdk/src/matrix";
|
import { ClientEvent, EventType, RoomStateEvent } from "matrix-js-sdk/src/matrix";
|
||||||
import { SyncState } from "matrix-js-sdk/src/sync";
|
import { SyncState } from "matrix-js-sdk/src/sync";
|
||||||
|
import { IKeyBackupInfo } from "matrix-js-sdk/src/crypto/keybackup";
|
||||||
|
|
||||||
import { MatrixClientPeg } from "./MatrixClientPeg";
|
import { MatrixClientPeg } from "./MatrixClientPeg";
|
||||||
import dis from "./dispatcher/dispatcher";
|
import dis from "./dispatcher/dispatcher";
|
||||||
@ -56,7 +57,7 @@ export default class DeviceListener {
|
|||||||
// has the user dismissed any of the various nag toasts to setup encryption on this device?
|
// has the user dismissed any of the various nag toasts to setup encryption on this device?
|
||||||
private dismissedThisDeviceToast = false;
|
private dismissedThisDeviceToast = false;
|
||||||
// cache of the key backup info
|
// cache of the key backup info
|
||||||
private keyBackupInfo: object = null;
|
private keyBackupInfo: IKeyBackupInfo | null = null;
|
||||||
private keyBackupFetchedAt: number = null;
|
private keyBackupFetchedAt: number = null;
|
||||||
private keyBackupStatusChecked = false;
|
private keyBackupStatusChecked = false;
|
||||||
// We keep a list of our own device IDs so we can batch ones that were already
|
// We keep a list of our own device IDs so we can batch ones that were already
|
||||||
@ -70,12 +71,12 @@ export default class DeviceListener {
|
|||||||
private enableBulkUnverifiedSessionsReminder = true;
|
private enableBulkUnverifiedSessionsReminder = true;
|
||||||
private deviceClientInformationSettingWatcherRef: string | undefined;
|
private deviceClientInformationSettingWatcherRef: string | undefined;
|
||||||
|
|
||||||
public static sharedInstance() {
|
public static sharedInstance(): DeviceListener {
|
||||||
if (!window.mxDeviceListener) window.mxDeviceListener = new DeviceListener();
|
if (!window.mxDeviceListener) window.mxDeviceListener = new DeviceListener();
|
||||||
return window.mxDeviceListener;
|
return window.mxDeviceListener;
|
||||||
}
|
}
|
||||||
|
|
||||||
public start() {
|
public start(): void {
|
||||||
this.running = true;
|
this.running = true;
|
||||||
MatrixClientPeg.get().on(CryptoEvent.WillUpdateDevices, this.onWillUpdateDevices);
|
MatrixClientPeg.get().on(CryptoEvent.WillUpdateDevices, this.onWillUpdateDevices);
|
||||||
MatrixClientPeg.get().on(CryptoEvent.DevicesUpdated, this.onDevicesUpdated);
|
MatrixClientPeg.get().on(CryptoEvent.DevicesUpdated, this.onDevicesUpdated);
|
||||||
@ -98,7 +99,7 @@ export default class DeviceListener {
|
|||||||
this.updateClientInformation();
|
this.updateClientInformation();
|
||||||
}
|
}
|
||||||
|
|
||||||
public stop() {
|
public stop(): void {
|
||||||
this.running = false;
|
this.running = false;
|
||||||
if (MatrixClientPeg.get()) {
|
if (MatrixClientPeg.get()) {
|
||||||
MatrixClientPeg.get().removeListener(CryptoEvent.WillUpdateDevices, this.onWillUpdateDevices);
|
MatrixClientPeg.get().removeListener(CryptoEvent.WillUpdateDevices, this.onWillUpdateDevices);
|
||||||
@ -134,7 +135,7 @@ export default class DeviceListener {
|
|||||||
*
|
*
|
||||||
* @param {String[]} deviceIds List of device IDs to dismiss notifications for
|
* @param {String[]} deviceIds List of device IDs to dismiss notifications for
|
||||||
*/
|
*/
|
||||||
public async dismissUnverifiedSessions(deviceIds: Iterable<string>) {
|
public async dismissUnverifiedSessions(deviceIds: Iterable<string>): Promise<void> {
|
||||||
logger.log("Dismissing unverified sessions: " + Array.from(deviceIds).join(","));
|
logger.log("Dismissing unverified sessions: " + Array.from(deviceIds).join(","));
|
||||||
for (const d of deviceIds) {
|
for (const d of deviceIds) {
|
||||||
this.dismissed.add(d);
|
this.dismissed.add(d);
|
||||||
@ -143,19 +144,19 @@ export default class DeviceListener {
|
|||||||
this.recheck();
|
this.recheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
public dismissEncryptionSetup() {
|
public dismissEncryptionSetup(): void {
|
||||||
this.dismissedThisDeviceToast = true;
|
this.dismissedThisDeviceToast = true;
|
||||||
this.recheck();
|
this.recheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ensureDeviceIdsAtStartPopulated() {
|
private ensureDeviceIdsAtStartPopulated(): void {
|
||||||
if (this.ourDeviceIdsAtStart === null) {
|
if (this.ourDeviceIdsAtStart === null) {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
this.ourDeviceIdsAtStart = new Set(cli.getStoredDevicesForUser(cli.getUserId()).map((d) => d.deviceId));
|
this.ourDeviceIdsAtStart = new Set(cli.getStoredDevicesForUser(cli.getUserId()).map((d) => d.deviceId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private onWillUpdateDevices = async (users: string[], initialFetch?: boolean) => {
|
private onWillUpdateDevices = async (users: string[], initialFetch?: boolean): Promise<void> => {
|
||||||
// If we didn't know about *any* devices before (ie. it's fresh login),
|
// If we didn't know about *any* devices before (ie. it's fresh login),
|
||||||
// then they are all pre-existing devices, so ignore this and set the
|
// then they are all pre-existing devices, so ignore this and set the
|
||||||
// devicesAtStart list to the devices that we see after the fetch.
|
// devicesAtStart list to the devices that we see after the fetch.
|
||||||
@ -168,26 +169,26 @@ export default class DeviceListener {
|
|||||||
// before we download any new ones.
|
// before we download any new ones.
|
||||||
};
|
};
|
||||||
|
|
||||||
private onDevicesUpdated = (users: string[]) => {
|
private onDevicesUpdated = (users: string[]): void => {
|
||||||
if (!users.includes(MatrixClientPeg.get().getUserId())) return;
|
if (!users.includes(MatrixClientPeg.get().getUserId())) return;
|
||||||
this.recheck();
|
this.recheck();
|
||||||
};
|
};
|
||||||
|
|
||||||
private onDeviceVerificationChanged = (userId: string) => {
|
private onDeviceVerificationChanged = (userId: string): void => {
|
||||||
if (userId !== MatrixClientPeg.get().getUserId()) return;
|
if (userId !== MatrixClientPeg.get().getUserId()) return;
|
||||||
this.recheck();
|
this.recheck();
|
||||||
};
|
};
|
||||||
|
|
||||||
private onUserTrustStatusChanged = (userId: string) => {
|
private onUserTrustStatusChanged = (userId: string): void => {
|
||||||
if (userId !== MatrixClientPeg.get().getUserId()) return;
|
if (userId !== MatrixClientPeg.get().getUserId()) return;
|
||||||
this.recheck();
|
this.recheck();
|
||||||
};
|
};
|
||||||
|
|
||||||
private onCrossSingingKeysChanged = () => {
|
private onCrossSingingKeysChanged = (): void => {
|
||||||
this.recheck();
|
this.recheck();
|
||||||
};
|
};
|
||||||
|
|
||||||
private onAccountData = (ev: MatrixEvent) => {
|
private onAccountData = (ev: MatrixEvent): void => {
|
||||||
// User may have:
|
// User may have:
|
||||||
// * migrated SSSS to symmetric
|
// * migrated SSSS to symmetric
|
||||||
// * uploaded keys to secret storage
|
// * uploaded keys to secret storage
|
||||||
@ -202,13 +203,13 @@ export default class DeviceListener {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private onSync = (state: SyncState, prevState?: SyncState) => {
|
private onSync = (state: SyncState, prevState?: SyncState): void => {
|
||||||
if (state === "PREPARED" && prevState === null) {
|
if (state === "PREPARED" && prevState === null) {
|
||||||
this.recheck();
|
this.recheck();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private onRoomStateEvents = (ev: MatrixEvent) => {
|
private onRoomStateEvents = (ev: MatrixEvent): void => {
|
||||||
if (ev.getType() !== EventType.RoomEncryption) return;
|
if (ev.getType() !== EventType.RoomEncryption) return;
|
||||||
|
|
||||||
// If a room changes to encrypted, re-check as it may be our first
|
// If a room changes to encrypted, re-check as it may be our first
|
||||||
@ -216,7 +217,7 @@ export default class DeviceListener {
|
|||||||
this.recheck();
|
this.recheck();
|
||||||
};
|
};
|
||||||
|
|
||||||
private onAction = ({ action }: ActionPayload) => {
|
private onAction = ({ action }: ActionPayload): void => {
|
||||||
if (action !== Action.OnLoggedIn) return;
|
if (action !== Action.OnLoggedIn) return;
|
||||||
this.recheck();
|
this.recheck();
|
||||||
this.updateClientInformation();
|
this.updateClientInformation();
|
||||||
@ -224,7 +225,7 @@ export default class DeviceListener {
|
|||||||
|
|
||||||
// The server doesn't tell us when key backup is set up, so we poll
|
// The server doesn't tell us when key backup is set up, so we poll
|
||||||
// & cache the result
|
// & cache the result
|
||||||
private async getKeyBackupInfo() {
|
private async getKeyBackupInfo(): Promise<IKeyBackupInfo> {
|
||||||
const now = new Date().getTime();
|
const now = new Date().getTime();
|
||||||
if (!this.keyBackupInfo || this.keyBackupFetchedAt < now - KEY_BACKUP_POLL_INTERVAL) {
|
if (!this.keyBackupInfo || this.keyBackupFetchedAt < now - KEY_BACKUP_POLL_INTERVAL) {
|
||||||
this.keyBackupInfo = await MatrixClientPeg.get().getKeyBackupVersion();
|
this.keyBackupInfo = await MatrixClientPeg.get().getKeyBackupVersion();
|
||||||
@ -233,7 +234,7 @@ export default class DeviceListener {
|
|||||||
return this.keyBackupInfo;
|
return this.keyBackupInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
private shouldShowSetupEncryptionToast() {
|
private shouldShowSetupEncryptionToast(): boolean {
|
||||||
// If we're in the middle of a secret storage operation, we're likely
|
// If we're in the middle of a secret storage operation, we're likely
|
||||||
// modifying the state involved here, so don't add new toasts to setup.
|
// modifying the state involved here, so don't add new toasts to setup.
|
||||||
if (isSecretStorageBeingAccessed()) return false;
|
if (isSecretStorageBeingAccessed()) return false;
|
||||||
@ -242,7 +243,7 @@ export default class DeviceListener {
|
|||||||
return cli && cli.getRooms().some((r) => cli.isRoomEncrypted(r.roomId));
|
return cli && cli.getRooms().some((r) => cli.isRoomEncrypted(r.roomId));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async recheck() {
|
private async recheck(): Promise<void> {
|
||||||
if (!this.running) return; // we have been stopped
|
if (!this.running) return; // we have been stopped
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
|
|
||||||
@ -359,7 +360,7 @@ export default class DeviceListener {
|
|||||||
this.displayingToastsForDeviceIds = newUnverifiedDeviceIds;
|
this.displayingToastsForDeviceIds = newUnverifiedDeviceIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
private checkKeyBackupStatus = async () => {
|
private checkKeyBackupStatus = async (): Promise<void> => {
|
||||||
if (this.keyBackupStatusChecked) {
|
if (this.keyBackupStatusChecked) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -388,7 +389,7 @@ export default class DeviceListener {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private updateClientInformation = async () => {
|
private updateClientInformation = async (): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
if (this.shouldRecordClientInformation) {
|
if (this.shouldRecordClientInformation) {
|
||||||
await recordClientInformation(MatrixClientPeg.get(), SdkConfig.get(), PlatformPeg.get());
|
await recordClientInformation(MatrixClientPeg.get(), SdkConfig.get(), PlatformPeg.get());
|
||||||
|
@ -16,5 +16,6 @@ limitations under the License.
|
|||||||
|
|
||||||
import { TimelineRenderingType } from "./contexts/RoomContext";
|
import { TimelineRenderingType } from "./contexts/RoomContext";
|
||||||
|
|
||||||
export const editorRoomKey = (roomId: string, context: TimelineRenderingType) => `mx_edit_room_${roomId}_${context}`;
|
export const editorRoomKey = (roomId: string, context: TimelineRenderingType): string =>
|
||||||
export const editorStateKey = (eventId: string) => `mx_edit_state_${eventId}`;
|
`mx_edit_room_${roomId}_${context}`;
|
||||||
|
export const editorStateKey = (eventId: string): string => `mx_edit_state_${eventId}`;
|
||||||
|
@ -449,9 +449,9 @@ export interface IOptsReturnString extends IOpts {
|
|||||||
returnString: true;
|
returnString: true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const emojiToHtmlSpan = (emoji: string) =>
|
const emojiToHtmlSpan = (emoji: string): string =>
|
||||||
`<span class='mx_Emoji' title='${unicodeToShortcode(emoji)}'>${emoji}</span>`;
|
`<span class='mx_Emoji' title='${unicodeToShortcode(emoji)}'>${emoji}</span>`;
|
||||||
const emojiToJsxSpan = (emoji: string, key: number) => (
|
const emojiToJsxSpan = (emoji: string, key: number): JSX.Element => (
|
||||||
<span key={key} className="mx_Emoji" title={unicodeToShortcode(emoji)}>
|
<span key={key} className="mx_Emoji" title={unicodeToShortcode(emoji)}>
|
||||||
{emoji}
|
{emoji}
|
||||||
</span>
|
</span>
|
||||||
@ -505,7 +505,7 @@ function formatEmojis(message: string, isHtmlMessage: boolean): (JSX.Element | s
|
|||||||
*/
|
*/
|
||||||
export function bodyToHtml(content: IContent, highlights: Optional<string[]>, opts: IOptsReturnString): string;
|
export function bodyToHtml(content: IContent, highlights: Optional<string[]>, opts: IOptsReturnString): string;
|
||||||
export function bodyToHtml(content: IContent, highlights: Optional<string[]>, opts: IOptsReturnNode): ReactNode;
|
export function bodyToHtml(content: IContent, highlights: Optional<string[]>, opts: IOptsReturnNode): ReactNode;
|
||||||
export function bodyToHtml(content: IContent, highlights: Optional<string[]>, opts: IOpts = {}) {
|
export function bodyToHtml(content: IContent, highlights: Optional<string[]>, opts: IOpts = {}): ReactNode | string {
|
||||||
const isFormattedBody = content.format === "org.matrix.custom.html" && !!content.formatted_body;
|
const isFormattedBody = content.format === "org.matrix.custom.html" && !!content.formatted_body;
|
||||||
let bodyHasEmoji = false;
|
let bodyHasEmoji = false;
|
||||||
let isHtmlMessage = false;
|
let isHtmlMessage = false;
|
||||||
|
@ -28,7 +28,7 @@ limitations under the License.
|
|||||||
* consume in the timeline, when performing scroll offset calculations
|
* consume in the timeline, when performing scroll offset calculations
|
||||||
* (e.g. scroll locking)
|
* (e.g. scroll locking)
|
||||||
*/
|
*/
|
||||||
export function thumbHeight(fullWidth: number, fullHeight: number, thumbWidth: number, thumbHeight: number) {
|
export function thumbHeight(fullWidth: number, fullHeight: number, thumbWidth: number, thumbHeight: number): number {
|
||||||
if (!fullWidth || !fullHeight) {
|
if (!fullWidth || !fullHeight) {
|
||||||
// Cannot calculate thumbnail height for image: missing w/h in metadata. We can't even
|
// Cannot calculate thumbnail height for image: missing w/h in metadata. We can't even
|
||||||
// log this because it's spammy
|
// log this because it's spammy
|
||||||
|
@ -76,7 +76,7 @@ export const Key = {
|
|||||||
|
|
||||||
export const IS_MAC = navigator.platform.toUpperCase().includes("MAC");
|
export const IS_MAC = navigator.platform.toUpperCase().includes("MAC");
|
||||||
|
|
||||||
export function isOnlyCtrlOrCmdKeyEvent(ev) {
|
export function isOnlyCtrlOrCmdKeyEvent(ev: KeyboardEvent): boolean {
|
||||||
if (IS_MAC) {
|
if (IS_MAC) {
|
||||||
return ev.metaKey && !ev.altKey && !ev.ctrlKey && !ev.shiftKey;
|
return ev.metaKey && !ev.altKey && !ev.ctrlKey && !ev.shiftKey;
|
||||||
} else {
|
} else {
|
||||||
|
@ -169,7 +169,7 @@ export default class LegacyCallHandler extends EventEmitter {
|
|||||||
|
|
||||||
private silencedCalls = new Set<string>(); // callIds
|
private silencedCalls = new Set<string>(); // callIds
|
||||||
|
|
||||||
public static get instance() {
|
public static get instance(): LegacyCallHandler {
|
||||||
if (!window.mxLegacyCallHandler) {
|
if (!window.mxLegacyCallHandler) {
|
||||||
window.mxLegacyCallHandler = new LegacyCallHandler();
|
window.mxLegacyCallHandler = new LegacyCallHandler();
|
||||||
}
|
}
|
||||||
@ -456,7 +456,7 @@ export default class LegacyCallHandler extends EventEmitter {
|
|||||||
return callsNotInThatRoom;
|
return callsNotInThatRoom;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getAllActiveCallsForPip(roomId: string) {
|
public getAllActiveCallsForPip(roomId: string): MatrixCall[] {
|
||||||
const room = MatrixClientPeg.get().getRoom(roomId);
|
const room = MatrixClientPeg.get().getRoom(roomId);
|
||||||
if (WidgetLayoutStore.instance.hasMaximisedWidget(room)) {
|
if (WidgetLayoutStore.instance.hasMaximisedWidget(room)) {
|
||||||
// This checks if there is space for the call view in the aux panel
|
// This checks if there is space for the call view in the aux panel
|
||||||
@ -478,7 +478,7 @@ export default class LegacyCallHandler extends EventEmitter {
|
|||||||
const audio = document.getElementById(audioId) as HTMLMediaElement;
|
const audio = document.getElementById(audioId) as HTMLMediaElement;
|
||||||
if (audio) {
|
if (audio) {
|
||||||
this.addEventListenersForAudioElement(audio);
|
this.addEventListenersForAudioElement(audio);
|
||||||
const playAudio = async () => {
|
const playAudio = async (): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
if (audio.muted) {
|
if (audio.muted) {
|
||||||
logger.error(
|
logger.error(
|
||||||
@ -524,7 +524,7 @@ export default class LegacyCallHandler extends EventEmitter {
|
|||||||
// TODO: Attach an invisible element for this instead
|
// TODO: Attach an invisible element for this instead
|
||||||
// which listens?
|
// which listens?
|
||||||
const audio = document.getElementById(audioId) as HTMLMediaElement;
|
const audio = document.getElementById(audioId) as HTMLMediaElement;
|
||||||
const pauseAudio = () => {
|
const pauseAudio = (): void => {
|
||||||
logger.debug(`${logPrefix} pausing audio`);
|
logger.debug(`${logPrefix} pausing audio`);
|
||||||
// pause doesn't return a promise, so just do it
|
// pause doesn't return a promise, so just do it
|
||||||
audio.pause();
|
audio.pause();
|
||||||
@ -600,7 +600,7 @@ export default class LegacyCallHandler extends EventEmitter {
|
|||||||
this.setCallListeners(newCall);
|
this.setCallListeners(newCall);
|
||||||
this.setCallState(newCall, newCall.state);
|
this.setCallState(newCall, newCall.state);
|
||||||
});
|
});
|
||||||
call.on(CallEvent.AssertedIdentityChanged, async () => {
|
call.on(CallEvent.AssertedIdentityChanged, async (): Promise<void> => {
|
||||||
if (!this.matchesCallForThisRoom(call)) return;
|
if (!this.matchesCallForThisRoom(call)) return;
|
||||||
|
|
||||||
logger.log(`Call ID ${call.callId} got new asserted identity:`, call.getRemoteAssertedIdentity());
|
logger.log(`Call ID ${call.callId} got new asserted identity:`, call.getRemoteAssertedIdentity());
|
||||||
@ -808,7 +808,7 @@ export default class LegacyCallHandler extends EventEmitter {
|
|||||||
|
|
||||||
private showICEFallbackPrompt(): void {
|
private showICEFallbackPrompt(): void {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
const code = (sub) => <code>{sub}</code>;
|
const code = (sub: string): JSX.Element => <code>{sub}</code>;
|
||||||
Modal.createDialog(
|
Modal.createDialog(
|
||||||
QuestionDialog,
|
QuestionDialog,
|
||||||
{
|
{
|
||||||
|
@ -219,7 +219,7 @@ export function attemptTokenLogin(
|
|||||||
})
|
})
|
||||||
.then(function (creds) {
|
.then(function (creds) {
|
||||||
logger.log("Logged in with token");
|
logger.log("Logged in with token");
|
||||||
return clearStorage().then(async () => {
|
return clearStorage().then(async (): Promise<boolean> => {
|
||||||
await persistCredentials(creds);
|
await persistCredentials(creds);
|
||||||
// remember that we just logged in
|
// remember that we just logged in
|
||||||
sessionStorage.setItem("mx_fresh_login", String(true));
|
sessionStorage.setItem("mx_fresh_login", String(true));
|
||||||
@ -406,7 +406,7 @@ async function pickleKeyToAesKey(pickleKey: string): Promise<Uint8Array> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function abortLogin() {
|
async function abortLogin(): Promise<void> {
|
||||||
const signOut = await showStorageEvictedDialog();
|
const signOut = await showStorageEvictedDialog();
|
||||||
if (signOut) {
|
if (signOut) {
|
||||||
await clearStorage();
|
await clearStorage();
|
||||||
|
@ -20,14 +20,14 @@ import { MatrixClientPeg } from "./MatrixClientPeg";
|
|||||||
import SdkConfig from "./SdkConfig";
|
import SdkConfig from "./SdkConfig";
|
||||||
import { ElementWidgetActions } from "./stores/widgets/ElementWidgetActions";
|
import { ElementWidgetActions } from "./stores/widgets/ElementWidgetActions";
|
||||||
|
|
||||||
export function getConfigLivestreamUrl() {
|
export function getConfigLivestreamUrl(): string | undefined {
|
||||||
return SdkConfig.get("audio_stream_url");
|
return SdkConfig.get("audio_stream_url");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dummy rtmp URL used to signal that we want a special audio-only stream
|
// Dummy rtmp URL used to signal that we want a special audio-only stream
|
||||||
const AUDIOSTREAM_DUMMY_URL = "rtmp://audiostream.dummy/";
|
const AUDIOSTREAM_DUMMY_URL = "rtmp://audiostream.dummy/";
|
||||||
|
|
||||||
async function createLiveStream(roomId: string) {
|
async function createLiveStream(roomId: string): Promise<void> {
|
||||||
const openIdToken = await MatrixClientPeg.get().getOpenIdToken();
|
const openIdToken = await MatrixClientPeg.get().getOpenIdToken();
|
||||||
|
|
||||||
const url = getConfigLivestreamUrl() + "/createStream";
|
const url = getConfigLivestreamUrl() + "/createStream";
|
||||||
@ -47,7 +47,7 @@ async function createLiveStream(roomId: string) {
|
|||||||
return respBody["stream_id"];
|
return respBody["stream_id"];
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function startJitsiAudioLivestream(widgetMessaging: ClientWidgetApi, roomId: string) {
|
export async function startJitsiAudioLivestream(widgetMessaging: ClientWidgetApi, roomId: string): Promise<void> {
|
||||||
const streamId = await createLiveStream(roomId);
|
const streamId = await createLiveStream(roomId);
|
||||||
|
|
||||||
await widgetMessaging.transport.send(ElementWidgetActions.StartLiveStream, {
|
await widgetMessaging.transport.send(ElementWidgetActions.StartLiveStream, {
|
||||||
|
@ -122,7 +122,7 @@ export default class Login {
|
|||||||
initial_device_display_name: this.defaultDeviceDisplayName,
|
initial_device_display_name: this.defaultDeviceDisplayName,
|
||||||
};
|
};
|
||||||
|
|
||||||
const tryFallbackHs = (originalError) => {
|
const tryFallbackHs = (originalError: Error): Promise<IMatrixClientCreds> => {
|
||||||
return sendLoginRequest(this.fallbackHsUrl, this.isUrl, "m.login.password", loginParams).catch(
|
return sendLoginRequest(this.fallbackHsUrl, this.isUrl, "m.login.password", loginParams).catch(
|
||||||
(fallbackError) => {
|
(fallbackError) => {
|
||||||
logger.log("fallback HS login failed", fallbackError);
|
logger.log("fallback HS login failed", fallbackError);
|
||||||
|
@ -56,7 +56,7 @@ function isMultiLine(node: commonmark.Node): boolean {
|
|||||||
return par.firstChild != par.lastChild;
|
return par.firstChild != par.lastChild;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTextUntilEndOrLinebreak(node: commonmark.Node) {
|
function getTextUntilEndOrLinebreak(node: commonmark.Node): string {
|
||||||
let currentNode = node;
|
let currentNode = node;
|
||||||
let text = "";
|
let text = "";
|
||||||
while (currentNode !== null && currentNode.type !== "softbreak" && currentNode.type !== "linebreak") {
|
while (currentNode !== null && currentNode.type !== "softbreak" && currentNode.type !== "linebreak") {
|
||||||
@ -137,7 +137,7 @@ export default class Markdown {
|
|||||||
* See: https://github.com/vector-im/element-web/issues/4674
|
* See: https://github.com/vector-im/element-web/issues/4674
|
||||||
* @param parsed
|
* @param parsed
|
||||||
*/
|
*/
|
||||||
private repairLinks(parsed: commonmark.Node) {
|
private repairLinks(parsed: commonmark.Node): commonmark.Node {
|
||||||
const walker = parsed.walker();
|
const walker = parsed.walker();
|
||||||
let event: commonmark.NodeWalkingStep = null;
|
let event: commonmark.NodeWalkingStep = null;
|
||||||
let text = "";
|
let text = "";
|
||||||
|
@ -77,7 +77,7 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
|
|||||||
// Neither the static nor priority modal will be in this list.
|
// Neither the static nor priority modal will be in this list.
|
||||||
private modals: IModal<any>[] = [];
|
private modals: IModal<any>[] = [];
|
||||||
|
|
||||||
private static getOrCreateContainer() {
|
private static getOrCreateContainer(): HTMLElement {
|
||||||
let container = document.getElementById(DIALOG_CONTAINER_ID);
|
let container = document.getElementById(DIALOG_CONTAINER_ID);
|
||||||
|
|
||||||
if (!container) {
|
if (!container) {
|
||||||
@ -89,7 +89,7 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
|
|||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static getOrCreateStaticContainer() {
|
private static getOrCreateStaticContainer(): HTMLElement {
|
||||||
let container = document.getElementById(STATIC_DIALOG_CONTAINER_ID);
|
let container = document.getElementById(STATIC_DIALOG_CONTAINER_ID);
|
||||||
|
|
||||||
if (!container) {
|
if (!container) {
|
||||||
@ -101,31 +101,31 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
|
|||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
|
|
||||||
public toggleCurrentDialogVisibility() {
|
public toggleCurrentDialogVisibility(): void {
|
||||||
const modal = this.getCurrentModal();
|
const modal = this.getCurrentModal();
|
||||||
if (!modal) return;
|
if (!modal) return;
|
||||||
modal.hidden = !modal.hidden;
|
modal.hidden = !modal.hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
public hasDialogs() {
|
public hasDialogs(): boolean {
|
||||||
return this.priorityModal || this.staticModal || this.modals.length > 0;
|
return !!this.priorityModal || !!this.staticModal || this.modals.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public createDialog<T extends any[]>(
|
public createDialog<T extends any[]>(
|
||||||
Element: React.ComponentType<any>,
|
Element: React.ComponentType<any>,
|
||||||
...rest: ParametersWithoutFirst<ModalManager["createDialogAsync"]>
|
...rest: ParametersWithoutFirst<ModalManager["createDialogAsync"]>
|
||||||
) {
|
): IHandle<T> {
|
||||||
return this.createDialogAsync<T>(Promise.resolve(Element), ...rest);
|
return this.createDialogAsync<T>(Promise.resolve(Element), ...rest);
|
||||||
}
|
}
|
||||||
|
|
||||||
public appendDialog<T extends any[]>(
|
public appendDialog<T extends any[]>(
|
||||||
Element: React.ComponentType,
|
Element: React.ComponentType,
|
||||||
...rest: ParametersWithoutFirst<ModalManager["appendDialogAsync"]>
|
...rest: ParametersWithoutFirst<ModalManager["appendDialogAsync"]>
|
||||||
) {
|
): IHandle<T> {
|
||||||
return this.appendDialogAsync<T>(Promise.resolve(Element), ...rest);
|
return this.appendDialogAsync<T>(Promise.resolve(Element), ...rest);
|
||||||
}
|
}
|
||||||
|
|
||||||
public closeCurrentModal(reason: string) {
|
public closeCurrentModal(reason: string): void {
|
||||||
const modal = this.getCurrentModal();
|
const modal = this.getCurrentModal();
|
||||||
if (!modal) {
|
if (!modal) {
|
||||||
return;
|
return;
|
||||||
@ -139,7 +139,11 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
|
|||||||
props?: IProps<T>,
|
props?: IProps<T>,
|
||||||
className?: string,
|
className?: string,
|
||||||
options?: IOptions<T>,
|
options?: IOptions<T>,
|
||||||
) {
|
): {
|
||||||
|
modal: IModal<T>;
|
||||||
|
closeDialog: IHandle<T>["close"];
|
||||||
|
onFinishedProm: IHandle<T>["finished"];
|
||||||
|
} {
|
||||||
const modal: IModal<T> = {
|
const modal: IModal<T> = {
|
||||||
onFinished: props ? props.onFinished : null,
|
onFinished: props ? props.onFinished : null,
|
||||||
onBeforeClose: options.onBeforeClose,
|
onBeforeClose: options.onBeforeClose,
|
||||||
@ -173,7 +177,7 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
|
|||||||
): [IHandle<T>["close"], IHandle<T>["finished"]] {
|
): [IHandle<T>["close"], IHandle<T>["finished"]] {
|
||||||
const deferred = defer<T>();
|
const deferred = defer<T>();
|
||||||
return [
|
return [
|
||||||
async (...args: T) => {
|
async (...args: T): Promise<void> => {
|
||||||
if (modal.beforeClosePromise) {
|
if (modal.beforeClosePromise) {
|
||||||
await modal.beforeClosePromise;
|
await modal.beforeClosePromise;
|
||||||
} else if (modal.onBeforeClose) {
|
} else if (modal.onBeforeClose) {
|
||||||
@ -302,7 +306,7 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private onBackgroundClick = () => {
|
private onBackgroundClick = (): void => {
|
||||||
const modal = this.getCurrentModal();
|
const modal = this.getCurrentModal();
|
||||||
if (!modal) {
|
if (!modal) {
|
||||||
return;
|
return;
|
||||||
@ -320,7 +324,7 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
|
|||||||
return this.priorityModal ? this.priorityModal : this.modals[0] || this.staticModal;
|
return this.priorityModal ? this.priorityModal : this.modals[0] || this.staticModal;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async reRender() {
|
private async reRender(): Promise<void> {
|
||||||
// await next tick because sometimes ReactDOM can race with itself and cause the modal to wrongly stick around
|
// await next tick because sometimes ReactDOM can race with itself and cause the modal to wrongly stick around
|
||||||
await sleep(0);
|
await sleep(0);
|
||||||
|
|
||||||
|
@ -32,13 +32,13 @@ import { PlatformSetPayload } from "./dispatcher/payloads/PlatformSetPayload";
|
|||||||
* object.
|
* object.
|
||||||
*/
|
*/
|
||||||
export class PlatformPeg {
|
export class PlatformPeg {
|
||||||
private platform: BasePlatform = null;
|
private platform: BasePlatform | null = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the current Platform object for the application.
|
* Returns the current Platform object for the application.
|
||||||
* This should be an instance of a class extending BasePlatform.
|
* This should be an instance of a class extending BasePlatform.
|
||||||
*/
|
*/
|
||||||
public get() {
|
public get(): BasePlatform | null {
|
||||||
return this.platform;
|
return this.platform;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ export class PlatformPeg {
|
|||||||
* Sets the current platform handler object to use for the application.
|
* Sets the current platform handler object to use for the application.
|
||||||
* @param {BasePlatform} platform an instance of a class extending BasePlatform.
|
* @param {BasePlatform} platform an instance of a class extending BasePlatform.
|
||||||
*/
|
*/
|
||||||
public set(platform: BasePlatform) {
|
public set(platform: BasePlatform): void {
|
||||||
this.platform = platform;
|
this.platform = platform;
|
||||||
defaultDispatcher.dispatch<PlatformSetPayload>({
|
defaultDispatcher.dispatch<PlatformSetPayload>({
|
||||||
action: Action.PlatformSet,
|
action: Action.PlatformSet,
|
||||||
|
@ -175,7 +175,7 @@ export class PosthogAnalytics {
|
|||||||
this.onLayoutUpdated();
|
this.onLayoutUpdated();
|
||||||
}
|
}
|
||||||
|
|
||||||
private onLayoutUpdated = () => {
|
private onLayoutUpdated = (): void => {
|
||||||
let layout: UserProperties["WebLayout"];
|
let layout: UserProperties["WebLayout"];
|
||||||
|
|
||||||
switch (SettingsStore.getValue("layout")) {
|
switch (SettingsStore.getValue("layout")) {
|
||||||
@ -195,7 +195,7 @@ export class PosthogAnalytics {
|
|||||||
this.setProperty("WebLayout", layout);
|
this.setProperty("WebLayout", layout);
|
||||||
};
|
};
|
||||||
|
|
||||||
private onAction = (payload: ActionPayload) => {
|
private onAction = (payload: ActionPayload): void => {
|
||||||
if (payload.action !== Action.SettingUpdated) return;
|
if (payload.action !== Action.SettingUpdated) return;
|
||||||
const settingsPayload = payload as SettingUpdatedPayload;
|
const settingsPayload = payload as SettingUpdatedPayload;
|
||||||
if (["layout", "useCompactLayout"].includes(settingsPayload.settingName)) {
|
if (["layout", "useCompactLayout"].includes(settingsPayload.settingName)) {
|
||||||
@ -232,7 +232,7 @@ export class PosthogAnalytics {
|
|||||||
return properties;
|
return properties;
|
||||||
};
|
};
|
||||||
|
|
||||||
private registerSuperProperties(properties: Properties) {
|
private registerSuperProperties(properties: Properties): void {
|
||||||
if (this.enabled) {
|
if (this.enabled) {
|
||||||
this.posthog.register(properties);
|
this.posthog.register(properties);
|
||||||
}
|
}
|
||||||
@ -255,7 +255,7 @@ export class PosthogAnalytics {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-nextline no-unused-varsx
|
// eslint-disable-nextline no-unused-varsx
|
||||||
private capture(eventName: string, properties: Properties, options?: IPostHogEventOptions) {
|
private capture(eventName: string, properties: Properties, options?: IPostHogEventOptions): void {
|
||||||
if (!this.enabled) {
|
if (!this.enabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -107,20 +107,20 @@ export default class PosthogTrackers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class PosthogScreenTracker extends PureComponent<{ screenName: ScreenName }> {
|
export class PosthogScreenTracker extends PureComponent<{ screenName: ScreenName }> {
|
||||||
public componentDidMount() {
|
public componentDidMount(): void {
|
||||||
PosthogTrackers.instance.trackOverride(this.props.screenName);
|
PosthogTrackers.instance.trackOverride(this.props.screenName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidUpdate() {
|
public componentDidUpdate(): void {
|
||||||
// We do not clear the old override here so that we do not send the non-override screen as a transition
|
// We do not clear the old override here so that we do not send the non-override screen as a transition
|
||||||
PosthogTrackers.instance.trackOverride(this.props.screenName);
|
PosthogTrackers.instance.trackOverride(this.props.screenName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentWillUnmount() {
|
public componentWillUnmount(): void {
|
||||||
PosthogTrackers.instance.clearOverride(this.props.screenName);
|
PosthogTrackers.instance.clearOverride(this.props.screenName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render(): JSX.Element {
|
||||||
return null; // no need to render anything, we just need to hook into the React lifecycle
|
return null; // no need to render anything, we just need to hook into the React lifecycle
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ class Presence {
|
|||||||
* Start listening the user activity to evaluate his presence state.
|
* Start listening the user activity to evaluate his presence state.
|
||||||
* Any state change will be sent to the homeserver.
|
* Any state change will be sent to the homeserver.
|
||||||
*/
|
*/
|
||||||
public async start() {
|
public async start(): Promise<void> {
|
||||||
this.unavailableTimer = new Timer(UNAVAILABLE_TIME_MS);
|
this.unavailableTimer = new Timer(UNAVAILABLE_TIME_MS);
|
||||||
// the user_activity_start action starts the timer
|
// the user_activity_start action starts the timer
|
||||||
this.dispatcherRef = dis.register(this.onAction);
|
this.dispatcherRef = dis.register(this.onAction);
|
||||||
@ -58,7 +58,7 @@ class Presence {
|
|||||||
/**
|
/**
|
||||||
* Stop tracking user activity
|
* Stop tracking user activity
|
||||||
*/
|
*/
|
||||||
public stop() {
|
public stop(): void {
|
||||||
if (this.dispatcherRef) {
|
if (this.dispatcherRef) {
|
||||||
dis.unregister(this.dispatcherRef);
|
dis.unregister(this.dispatcherRef);
|
||||||
this.dispatcherRef = null;
|
this.dispatcherRef = null;
|
||||||
@ -73,11 +73,11 @@ class Presence {
|
|||||||
* Get the current presence state.
|
* Get the current presence state.
|
||||||
* @returns {string} the presence state (see PRESENCE enum)
|
* @returns {string} the presence state (see PRESENCE enum)
|
||||||
*/
|
*/
|
||||||
public getState() {
|
public getState(): State {
|
||||||
return this.state;
|
return this.state;
|
||||||
}
|
}
|
||||||
|
|
||||||
private onAction = (payload: ActionPayload) => {
|
private onAction = (payload: ActionPayload): void => {
|
||||||
if (payload.action === "user_activity") {
|
if (payload.action === "user_activity") {
|
||||||
this.setState(State.Online);
|
this.setState(State.Online);
|
||||||
this.unavailableTimer.restart();
|
this.unavailableTimer.restart();
|
||||||
@ -89,7 +89,7 @@ class Presence {
|
|||||||
* If the state has changed, the homeserver will be notified.
|
* If the state has changed, the homeserver will be notified.
|
||||||
* @param {string} newState the new presence state (see PRESENCE enum)
|
* @param {string} newState the new presence state (see PRESENCE enum)
|
||||||
*/
|
*/
|
||||||
private async setState(newState: State) {
|
private async setState(newState: State): Promise<void> {
|
||||||
if (newState === this.state) {
|
if (newState === this.state) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ export default class ScalarAuthClient {
|
|||||||
this.isDefaultManager = apiUrl === configApiUrl && configUiUrl === uiUrl;
|
this.isDefaultManager = apiUrl === configApiUrl && configUiUrl === uiUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
private writeTokenToStore() {
|
private writeTokenToStore(): void {
|
||||||
window.localStorage.setItem("mx_scalar_token_at_" + this.apiUrl, this.scalarToken);
|
window.localStorage.setItem("mx_scalar_token_at_" + this.apiUrl, this.scalarToken);
|
||||||
if (this.isDefaultManager) {
|
if (this.isDefaultManager) {
|
||||||
// We remove the old token from storage to migrate upwards. This is safe
|
// We remove the old token from storage to migrate upwards. This is safe
|
||||||
@ -72,7 +72,7 @@ export default class ScalarAuthClient {
|
|||||||
return this.readTokenFromStore();
|
return this.readTokenFromStore();
|
||||||
}
|
}
|
||||||
|
|
||||||
public setTermsInteractionCallback(callback) {
|
public setTermsInteractionCallback(callback: TermsInteractionCallback): void {
|
||||||
this.termsInteractionCallback = callback;
|
this.termsInteractionCallback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -711,7 +711,7 @@ function returnStateEvent(event: MessageEvent<any>, roomId: string, eventType: s
|
|||||||
sendResponse(event, stateEvent.getContent());
|
sendResponse(event, stateEvent.getContent());
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getOpenIdToken(event: MessageEvent<any>) {
|
async function getOpenIdToken(event: MessageEvent<any>): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const tokenObject = await MatrixClientPeg.get().getOpenIdToken();
|
const tokenObject = await MatrixClientPeg.get().getOpenIdToken();
|
||||||
sendResponse(event, tokenObject);
|
sendResponse(event, tokenObject);
|
||||||
@ -728,7 +728,7 @@ async function sendEvent(
|
|||||||
content?: IContent;
|
content?: IContent;
|
||||||
}>,
|
}>,
|
||||||
roomId: string,
|
roomId: string,
|
||||||
) {
|
): Promise<void> {
|
||||||
const eventType = event.data.type;
|
const eventType = event.data.type;
|
||||||
const stateKey = event.data.state_key;
|
const stateKey = event.data.state_key;
|
||||||
const content = event.data.content;
|
const content = event.data.content;
|
||||||
@ -786,7 +786,7 @@ async function readEvents(
|
|||||||
limit?: number;
|
limit?: number;
|
||||||
}>,
|
}>,
|
||||||
roomId: string,
|
roomId: string,
|
||||||
) {
|
): Promise<void> {
|
||||||
const eventType = event.data.type;
|
const eventType = event.data.type;
|
||||||
const stateKey = event.data.state_key;
|
const stateKey = event.data.state_key;
|
||||||
const limit = event.data.limit;
|
const limit = event.data.limit;
|
||||||
|
@ -56,7 +56,7 @@ export default class SdkConfig {
|
|||||||
private static instance: IConfigOptions;
|
private static instance: IConfigOptions;
|
||||||
private static fallback: SnakedObject<IConfigOptions>;
|
private static fallback: SnakedObject<IConfigOptions>;
|
||||||
|
|
||||||
private static setInstance(i: IConfigOptions) {
|
private static setInstance(i: IConfigOptions): void {
|
||||||
SdkConfig.instance = i;
|
SdkConfig.instance = i;
|
||||||
SdkConfig.fallback = new SnakedObject(i);
|
SdkConfig.fallback = new SnakedObject(i);
|
||||||
|
|
||||||
@ -90,18 +90,18 @@ export default class SdkConfig {
|
|||||||
return val === undefined ? undefined : null;
|
return val === undefined ? undefined : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static put(cfg: Partial<IConfigOptions>) {
|
public static put(cfg: Partial<IConfigOptions>): void {
|
||||||
SdkConfig.setInstance({ ...DEFAULTS, ...cfg });
|
SdkConfig.setInstance({ ...DEFAULTS, ...cfg });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resets the config to be completely empty.
|
* Resets the config to be completely empty.
|
||||||
*/
|
*/
|
||||||
public static unset() {
|
public static unset(): void {
|
||||||
SdkConfig.setInstance(<IConfigOptions>{}); // safe to cast - defaults will be applied
|
SdkConfig.setInstance(<IConfigOptions>{}); // safe to cast - defaults will be applied
|
||||||
}
|
}
|
||||||
|
|
||||||
public static add(cfg: Partial<IConfigOptions>) {
|
public static add(cfg: Partial<IConfigOptions>): void {
|
||||||
SdkConfig.put({ ...SdkConfig.get(), ...cfg });
|
SdkConfig.put({ ...SdkConfig.get(), ...cfg });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ async function confirmToDismiss(): Promise<boolean> {
|
|||||||
type KeyParams = { passphrase: string; recoveryKey: string };
|
type KeyParams = { passphrase: string; recoveryKey: string };
|
||||||
|
|
||||||
function makeInputToKey(keyInfo: ISecretStorageKeyInfo): (keyParams: KeyParams) => Promise<Uint8Array> {
|
function makeInputToKey(keyInfo: ISecretStorageKeyInfo): (keyParams: KeyParams) => Promise<Uint8Array> {
|
||||||
return async ({ passphrase, recoveryKey }) => {
|
return async ({ passphrase, recoveryKey }): Promise<Uint8Array> => {
|
||||||
if (passphrase) {
|
if (passphrase) {
|
||||||
return deriveKey(passphrase, keyInfo.passphrase.salt, keyInfo.passphrase.iterations);
|
return deriveKey(passphrase, keyInfo.passphrase.salt, keyInfo.passphrase.iterations);
|
||||||
} else {
|
} else {
|
||||||
@ -151,7 +151,7 @@ async function getSecretStorageKey({
|
|||||||
/* props= */
|
/* props= */
|
||||||
{
|
{
|
||||||
keyInfo,
|
keyInfo,
|
||||||
checkPrivateKey: async (input: KeyParams) => {
|
checkPrivateKey: async (input: KeyParams): Promise<boolean> => {
|
||||||
const key = await inputToKey(input);
|
const key = await inputToKey(input);
|
||||||
return MatrixClientPeg.get().checkSecretStorageKey(key, keyInfo);
|
return MatrixClientPeg.get().checkSecretStorageKey(key, keyInfo);
|
||||||
},
|
},
|
||||||
@ -160,7 +160,7 @@ async function getSecretStorageKey({
|
|||||||
/* isPriorityModal= */ false,
|
/* isPriorityModal= */ false,
|
||||||
/* isStaticModal= */ false,
|
/* isStaticModal= */ false,
|
||||||
/* options= */ {
|
/* options= */ {
|
||||||
onBeforeClose: async (reason) => {
|
onBeforeClose: async (reason): Promise<boolean> => {
|
||||||
if (reason === "backgroundClick") {
|
if (reason === "backgroundClick") {
|
||||||
return confirmToDismiss();
|
return confirmToDismiss();
|
||||||
}
|
}
|
||||||
@ -196,7 +196,7 @@ export async function getDehydrationKey(
|
|||||||
/* props= */
|
/* props= */
|
||||||
{
|
{
|
||||||
keyInfo,
|
keyInfo,
|
||||||
checkPrivateKey: async (input) => {
|
checkPrivateKey: async (input): Promise<boolean> => {
|
||||||
const key = await inputToKey(input);
|
const key = await inputToKey(input);
|
||||||
try {
|
try {
|
||||||
checkFunc(key);
|
checkFunc(key);
|
||||||
@ -210,7 +210,7 @@ export async function getDehydrationKey(
|
|||||||
/* isPriorityModal= */ false,
|
/* isPriorityModal= */ false,
|
||||||
/* isStaticModal= */ false,
|
/* isStaticModal= */ false,
|
||||||
/* options= */ {
|
/* options= */ {
|
||||||
onBeforeClose: async (reason) => {
|
onBeforeClose: async (reason): Promise<boolean> => {
|
||||||
if (reason === "backgroundClick") {
|
if (reason === "backgroundClick") {
|
||||||
return confirmToDismiss();
|
return confirmToDismiss();
|
||||||
}
|
}
|
||||||
@ -324,7 +324,7 @@ export async function promptForBackupPassphrase(): Promise<Uint8Array> {
|
|||||||
* bootstrapped. Optional.
|
* bootstrapped. Optional.
|
||||||
* @param {bool} [forceReset] Reset secret storage even if it's already set up
|
* @param {bool} [forceReset] Reset secret storage even if it's already set up
|
||||||
*/
|
*/
|
||||||
export async function accessSecretStorage(func = async () => {}, forceReset = false) {
|
export async function accessSecretStorage(func = async (): Promise<void> => {}, forceReset = false): Promise<void> {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
secretStorageBeingAccessed = true;
|
secretStorageBeingAccessed = true;
|
||||||
try {
|
try {
|
||||||
@ -342,7 +342,7 @@ export async function accessSecretStorage(func = async () => {}, forceReset = fa
|
|||||||
/* priority = */ false,
|
/* priority = */ false,
|
||||||
/* static = */ true,
|
/* static = */ true,
|
||||||
/* options = */ {
|
/* options = */ {
|
||||||
onBeforeClose: async (reason) => {
|
onBeforeClose: async (reason): Promise<boolean> => {
|
||||||
// If Secure Backup is required, you cannot leave the modal.
|
// If Secure Backup is required, you cannot leave the modal.
|
||||||
if (reason === "backgroundClick") {
|
if (reason === "backgroundClick") {
|
||||||
return !isSecureBackupRequired();
|
return !isSecureBackupRequired();
|
||||||
@ -357,7 +357,7 @@ export async function accessSecretStorage(func = async () => {}, forceReset = fa
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await cli.bootstrapCrossSigning({
|
await cli.bootstrapCrossSigning({
|
||||||
authUploadDeviceSigningKeys: async (makeRequest) => {
|
authUploadDeviceSigningKeys: async (makeRequest): Promise<void> => {
|
||||||
const { finished } = Modal.createDialog(InteractiveAuthDialog, {
|
const { finished } = Modal.createDialog(InteractiveAuthDialog, {
|
||||||
title: _t("Setting up keys"),
|
title: _t("Setting up keys"),
|
||||||
matrixClient: cli,
|
matrixClient: cli,
|
||||||
|
@ -60,7 +60,7 @@ export default class SendHistoryManager {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public save(editorModel: EditorModel, replyEvent?: MatrixEvent) {
|
public save(editorModel: EditorModel, replyEvent?: MatrixEvent): void {
|
||||||
const item = SendHistoryManager.createItem(editorModel, replyEvent);
|
const item = SendHistoryManager.createItem(editorModel, replyEvent);
|
||||||
this.history.push(item);
|
this.history.push(item);
|
||||||
this.currentIndex = this.history.length;
|
this.currentIndex = this.history.length;
|
||||||
|
@ -85,7 +85,7 @@ const singleMxcUpload = async (): Promise<string | null> => {
|
|||||||
|
|
||||||
Modal.createDialog(UploadConfirmDialog, {
|
Modal.createDialog(UploadConfirmDialog, {
|
||||||
file,
|
file,
|
||||||
onFinished: async (shouldContinue) => {
|
onFinished: async (shouldContinue): Promise<void> => {
|
||||||
if (shouldContinue) {
|
if (shouldContinue) {
|
||||||
const { content_uri: uri } = await MatrixClientPeg.get().uploadContent(file);
|
const { content_uri: uri } = await MatrixClientPeg.get().uploadContent(file);
|
||||||
resolve(uri);
|
resolve(uri);
|
||||||
@ -151,11 +151,11 @@ export class Command {
|
|||||||
this.analyticsName = opts.analyticsName;
|
this.analyticsName = opts.analyticsName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getCommand() {
|
public getCommand(): string {
|
||||||
return `/${this.command}`;
|
return `/${this.command}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getCommandWithArgs() {
|
public getCommandWithArgs(): string {
|
||||||
return this.getCommand() + " " + this.args;
|
return this.getCommand() + " " + this.args;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,7 +184,7 @@ export class Command {
|
|||||||
return this.runFn(roomId, args);
|
return this.runFn(roomId, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getUsage() {
|
public getUsage(): string {
|
||||||
return _t("Usage") + ": " + this.getCommandWithArgs();
|
return _t("Usage") + ": " + this.getCommandWithArgs();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,15 +193,15 @@ export class Command {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function reject(error) {
|
function reject(error?: any): RunResult {
|
||||||
return { error };
|
return { error };
|
||||||
}
|
}
|
||||||
|
|
||||||
function success(promise?: Promise<any>) {
|
function success(promise?: Promise<any>): RunResult {
|
||||||
return { promise };
|
return { promise };
|
||||||
}
|
}
|
||||||
|
|
||||||
function successSync(value: any) {
|
function successSync(value: any): RunResult {
|
||||||
return success(Promise.resolve(value));
|
return success(Promise.resolve(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -319,7 +319,7 @@ export const Commands = [
|
|||||||
);
|
);
|
||||||
|
|
||||||
return success(
|
return success(
|
||||||
finished.then(async ([resp]) => {
|
finished.then(async ([resp]): Promise<void> => {
|
||||||
if (!resp?.continue) return;
|
if (!resp?.continue) return;
|
||||||
await upgradeRoom(room, args, resp.invite);
|
await upgradeRoom(room, args, resp.invite);
|
||||||
}),
|
}),
|
||||||
@ -338,7 +338,7 @@ export const Commands = [
|
|||||||
runFn: function (roomId, args) {
|
runFn: function (roomId, args) {
|
||||||
if (args) {
|
if (args) {
|
||||||
return success(
|
return success(
|
||||||
(async () => {
|
(async (): Promise<void> => {
|
||||||
const unixTimestamp = Date.parse(args);
|
const unixTimestamp = Date.parse(args);
|
||||||
if (!unixTimestamp) {
|
if (!unixTimestamp) {
|
||||||
throw newTranslatableError(
|
throw newTranslatableError(
|
||||||
@ -501,7 +501,9 @@ export const Commands = [
|
|||||||
? ContentHelpers.parseTopicContent(content)
|
? ContentHelpers.parseTopicContent(content)
|
||||||
: { text: _t("This room has no topic.") };
|
: { text: _t("This room has no topic.") };
|
||||||
|
|
||||||
const ref = (e) => e && linkifyElement(e);
|
const ref = (e): void => {
|
||||||
|
if (e) linkifyElement(e);
|
||||||
|
};
|
||||||
const body = topicToHtml(topic.text, topic.html, ref, true);
|
const body = topicToHtml(topic.text, topic.html, ref, true);
|
||||||
|
|
||||||
Modal.createDialog(InfoDialog, {
|
Modal.createDialog(InfoDialog, {
|
||||||
@ -1028,7 +1030,7 @@ export const Commands = [
|
|||||||
const fingerprint = matches[3];
|
const fingerprint = matches[3];
|
||||||
|
|
||||||
return success(
|
return success(
|
||||||
(async () => {
|
(async (): Promise<void> => {
|
||||||
const device = cli.getStoredDevice(userId, deviceId);
|
const device = cli.getStoredDevice(userId, deviceId);
|
||||||
if (!device) {
|
if (!device) {
|
||||||
throw newTranslatableError("Unknown (user, session) pair: (%(userId)s, %(deviceId)s)", {
|
throw newTranslatableError("Unknown (user, session) pair: (%(userId)s, %(deviceId)s)", {
|
||||||
@ -1205,7 +1207,7 @@ export const Commands = [
|
|||||||
},
|
},
|
||||||
runFn: (roomId) => {
|
runFn: (roomId) => {
|
||||||
return success(
|
return success(
|
||||||
(async () => {
|
(async (): Promise<void> => {
|
||||||
const room = await VoipUserMapper.sharedInstance().getVirtualRoomForRoom(roomId);
|
const room = await VoipUserMapper.sharedInstance().getVirtualRoomForRoom(roomId);
|
||||||
if (!room) throw newTranslatableError("No virtual room for this room");
|
if (!room) throw newTranslatableError("No virtual room for this room");
|
||||||
dis.dispatch<ViewRoomPayload>({
|
dis.dispatch<ViewRoomPayload>({
|
||||||
@ -1231,7 +1233,7 @@ export const Commands = [
|
|||||||
}
|
}
|
||||||
|
|
||||||
return success(
|
return success(
|
||||||
(async () => {
|
(async (): Promise<void> => {
|
||||||
if (isPhoneNumber) {
|
if (isPhoneNumber) {
|
||||||
const results = await LegacyCallHandler.instance.pstnLookup(userId);
|
const results = await LegacyCallHandler.instance.pstnLookup(userId);
|
||||||
if (!results || results.length === 0 || !results[0].userid) {
|
if (!results || results.length === 0 || !results[0].userid) {
|
||||||
@ -1265,7 +1267,7 @@ export const Commands = [
|
|||||||
const [userId, msg] = matches.slice(1);
|
const [userId, msg] = matches.slice(1);
|
||||||
if (userId && userId.startsWith("@") && userId.includes(":")) {
|
if (userId && userId.startsWith("@") && userId.includes(":")) {
|
||||||
return success(
|
return success(
|
||||||
(async () => {
|
(async (): Promise<void> => {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
const roomId = await ensureDMExists(cli, userId);
|
const roomId = await ensureDMExists(cli, userId);
|
||||||
dis.dispatch<ViewRoomPayload>({
|
dis.dispatch<ViewRoomPayload>({
|
||||||
|
@ -302,7 +302,7 @@ export class SlidingSyncManager {
|
|||||||
* @param batchSize The number of rooms to return in each request.
|
* @param batchSize The number of rooms to return in each request.
|
||||||
* @param gapBetweenRequestsMs The number of milliseconds to wait between requests.
|
* @param gapBetweenRequestsMs The number of milliseconds to wait between requests.
|
||||||
*/
|
*/
|
||||||
public async startSpidering(batchSize: number, gapBetweenRequestsMs: number) {
|
public async startSpidering(batchSize: number, gapBetweenRequestsMs: number): Promise<void> {
|
||||||
await sleep(gapBetweenRequestsMs); // wait a bit as this is called on first render so let's let things load
|
await sleep(gapBetweenRequestsMs); // wait a bit as this is called on first render so let's let things load
|
||||||
const listIndex = this.getOrAllocateListIndex(SlidingSyncManager.ListSearch);
|
const listIndex = this.getOrAllocateListIndex(SlidingSyncManager.ListSearch);
|
||||||
let startIndex = batchSize;
|
let startIndex = batchSize;
|
||||||
|
@ -75,7 +75,7 @@ export type TermsInteractionCallback = (
|
|||||||
export async function startTermsFlow(
|
export async function startTermsFlow(
|
||||||
services: Service[],
|
services: Service[],
|
||||||
interactionCallback: TermsInteractionCallback = dialogTermsInteractionCallback,
|
interactionCallback: TermsInteractionCallback = dialogTermsInteractionCallback,
|
||||||
) {
|
): Promise<void> {
|
||||||
const termsPromises = services.map((s) => MatrixClientPeg.get().getTerms(s.serviceType, s.baseUrl));
|
const termsPromises = services.map((s) => MatrixClientPeg.get().getTerms(s.serviceType, s.baseUrl));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -176,7 +176,7 @@ export async function startTermsFlow(
|
|||||||
urlsForService,
|
urlsForService,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
return Promise.all(agreePromises);
|
await Promise.all(agreePromises);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function dialogTermsInteractionCallback(
|
export async function dialogTermsInteractionCallback(
|
||||||
|
@ -228,7 +228,7 @@ function textForTombstoneEvent(ev: MatrixEvent): () => string | null {
|
|||||||
return () => _t("%(senderDisplayName)s upgraded this room.", { senderDisplayName });
|
return () => _t("%(senderDisplayName)s upgraded this room.", { senderDisplayName });
|
||||||
}
|
}
|
||||||
|
|
||||||
const onViewJoinRuleSettingsClick = () => {
|
const onViewJoinRuleSettingsClick = (): void => {
|
||||||
defaultDispatcher.dispatch({
|
defaultDispatcher.dispatch({
|
||||||
action: "open_room_settings",
|
action: "open_room_settings",
|
||||||
initial_tab_id: ROOM_SECURITY_TAB,
|
initial_tab_id: ROOM_SECURITY_TAB,
|
||||||
|
@ -50,7 +50,7 @@ export default class UserActivity {
|
|||||||
this.activeRecentlyTimeout = new Timer(RECENTLY_ACTIVE_THRESHOLD_MS);
|
this.activeRecentlyTimeout = new Timer(RECENTLY_ACTIVE_THRESHOLD_MS);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static sharedInstance() {
|
public static sharedInstance(): UserActivity {
|
||||||
if (window.mxUserActivity === undefined) {
|
if (window.mxUserActivity === undefined) {
|
||||||
window.mxUserActivity = new UserActivity(window, document);
|
window.mxUserActivity = new UserActivity(window, document);
|
||||||
}
|
}
|
||||||
@ -66,7 +66,7 @@ export default class UserActivity {
|
|||||||
* later on when the user does become active.
|
* later on when the user does become active.
|
||||||
* @param {Timer} timer the timer to use
|
* @param {Timer} timer the timer to use
|
||||||
*/
|
*/
|
||||||
public timeWhileActiveNow(timer: Timer) {
|
public timeWhileActiveNow(timer: Timer): void {
|
||||||
this.timeWhile(timer, this.attachedActiveNowTimers);
|
this.timeWhile(timer, this.attachedActiveNowTimers);
|
||||||
if (this.userActiveNow()) {
|
if (this.userActiveNow()) {
|
||||||
timer.start();
|
timer.start();
|
||||||
@ -82,14 +82,14 @@ export default class UserActivity {
|
|||||||
* later on when the user does become active.
|
* later on when the user does become active.
|
||||||
* @param {Timer} timer the timer to use
|
* @param {Timer} timer the timer to use
|
||||||
*/
|
*/
|
||||||
public timeWhileActiveRecently(timer: Timer) {
|
public timeWhileActiveRecently(timer: Timer): void {
|
||||||
this.timeWhile(timer, this.attachedActiveRecentlyTimers);
|
this.timeWhile(timer, this.attachedActiveRecentlyTimers);
|
||||||
if (this.userActiveRecently()) {
|
if (this.userActiveRecently()) {
|
||||||
timer.start();
|
timer.start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private timeWhile(timer: Timer, attachedTimers: Timer[]) {
|
private timeWhile(timer: Timer, attachedTimers: Timer[]): void {
|
||||||
// important this happens first
|
// important this happens first
|
||||||
const index = attachedTimers.indexOf(timer);
|
const index = attachedTimers.indexOf(timer);
|
||||||
if (index === -1) {
|
if (index === -1) {
|
||||||
@ -113,7 +113,7 @@ export default class UserActivity {
|
|||||||
/**
|
/**
|
||||||
* Start listening to user activity
|
* Start listening to user activity
|
||||||
*/
|
*/
|
||||||
public start() {
|
public start(): void {
|
||||||
this.document.addEventListener("mousedown", this.onUserActivity);
|
this.document.addEventListener("mousedown", this.onUserActivity);
|
||||||
this.document.addEventListener("mousemove", this.onUserActivity);
|
this.document.addEventListener("mousemove", this.onUserActivity);
|
||||||
this.document.addEventListener("keydown", this.onUserActivity);
|
this.document.addEventListener("keydown", this.onUserActivity);
|
||||||
@ -133,7 +133,7 @@ export default class UserActivity {
|
|||||||
/**
|
/**
|
||||||
* Stop tracking user activity
|
* Stop tracking user activity
|
||||||
*/
|
*/
|
||||||
public stop() {
|
public stop(): void {
|
||||||
this.document.removeEventListener("mousedown", this.onUserActivity);
|
this.document.removeEventListener("mousedown", this.onUserActivity);
|
||||||
this.document.removeEventListener("mousemove", this.onUserActivity);
|
this.document.removeEventListener("mousemove", this.onUserActivity);
|
||||||
this.document.removeEventListener("keydown", this.onUserActivity);
|
this.document.removeEventListener("keydown", this.onUserActivity);
|
||||||
@ -152,7 +152,7 @@ export default class UserActivity {
|
|||||||
* user's attention at any given moment.
|
* user's attention at any given moment.
|
||||||
* @returns {boolean} true if user is currently 'active'
|
* @returns {boolean} true if user is currently 'active'
|
||||||
*/
|
*/
|
||||||
public userActiveNow() {
|
public userActiveNow(): boolean {
|
||||||
return this.activeNowTimeout.isRunning();
|
return this.activeNowTimeout.isRunning();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,11 +164,11 @@ export default class UserActivity {
|
|||||||
* (or they may have gone to make tea and left the window focused).
|
* (or they may have gone to make tea and left the window focused).
|
||||||
* @returns {boolean} true if user has been active recently
|
* @returns {boolean} true if user has been active recently
|
||||||
*/
|
*/
|
||||||
public userActiveRecently() {
|
public userActiveRecently(): boolean {
|
||||||
return this.activeRecentlyTimeout.isRunning();
|
return this.activeRecentlyTimeout.isRunning();
|
||||||
}
|
}
|
||||||
|
|
||||||
private onPageVisibilityChanged = (e) => {
|
private onPageVisibilityChanged = (e): void => {
|
||||||
if (this.document.visibilityState === "hidden") {
|
if (this.document.visibilityState === "hidden") {
|
||||||
this.activeNowTimeout.abort();
|
this.activeNowTimeout.abort();
|
||||||
this.activeRecentlyTimeout.abort();
|
this.activeRecentlyTimeout.abort();
|
||||||
@ -177,12 +177,12 @@ export default class UserActivity {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private onWindowBlurred = () => {
|
private onWindowBlurred = (): void => {
|
||||||
this.activeNowTimeout.abort();
|
this.activeNowTimeout.abort();
|
||||||
this.activeRecentlyTimeout.abort();
|
this.activeRecentlyTimeout.abort();
|
||||||
};
|
};
|
||||||
|
|
||||||
private onUserActivity = (event: MouseEvent) => {
|
private onUserActivity = (event: MouseEvent): void => {
|
||||||
// ignore anything if the window isn't focused
|
// ignore anything if the window isn't focused
|
||||||
if (!this.document.hasFocus()) return;
|
if (!this.document.hasFocus()) return;
|
||||||
|
|
||||||
@ -214,7 +214,7 @@ export default class UserActivity {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private static async runTimersUntilTimeout(attachedTimers: Timer[], timeout: Timer) {
|
private static async runTimersUntilTimeout(attachedTimers: Timer[], timeout: Timer): Promise<void> {
|
||||||
attachedTimers.forEach((t) => t.start());
|
attachedTimers.forEach((t) => t.start());
|
||||||
try {
|
try {
|
||||||
await timeout.finished();
|
await timeout.finished();
|
||||||
|
@ -87,7 +87,7 @@ interface IAction {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const reducer = (state: IState, action: IAction) => {
|
export const reducer: Reducer<IState, IAction> = (state: IState, action: IAction) => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case Type.Register: {
|
case Type.Register: {
|
||||||
if (!state.activeRef) {
|
if (!state.activeRef) {
|
||||||
|
@ -26,7 +26,7 @@ interface IProps extends Omit<React.HTMLProps<HTMLDivElement>, "onKeyDown"> {}
|
|||||||
// https://www.w3.org/TR/wai-aria-practices-1.1/#toolbar
|
// https://www.w3.org/TR/wai-aria-practices-1.1/#toolbar
|
||||||
// All buttons passed in children must use RovingTabIndex to set `onFocus`, `isActive`, `ref`
|
// All buttons passed in children must use RovingTabIndex to set `onFocus`, `isActive`, `ref`
|
||||||
const Toolbar: React.FC<IProps> = ({ children, ...props }) => {
|
const Toolbar: React.FC<IProps> = ({ children, ...props }) => {
|
||||||
const onKeyDown = (ev: React.KeyboardEvent) => {
|
const onKeyDown = (ev: React.KeyboardEvent): void => {
|
||||||
const target = ev.target as HTMLElement;
|
const target = ev.target as HTMLElement;
|
||||||
// Don't interfere with input default keydown behaviour
|
// Don't interfere with input default keydown behaviour
|
||||||
if (target.tagName === "INPUT") return;
|
if (target.tagName === "INPUT") return;
|
||||||
|
@ -33,7 +33,7 @@ interface IProps extends React.ComponentProps<typeof StyledCheckbox> {
|
|||||||
export const StyledMenuItemCheckbox: React.FC<IProps> = ({ children, label, onChange, onClose, ...props }) => {
|
export const StyledMenuItemCheckbox: React.FC<IProps> = ({ children, label, onChange, onClose, ...props }) => {
|
||||||
const [onFocus, isActive, ref] = useRovingTabIndex<HTMLInputElement>();
|
const [onFocus, isActive, ref] = useRovingTabIndex<HTMLInputElement>();
|
||||||
|
|
||||||
const onKeyDown = (e: React.KeyboardEvent) => {
|
const onKeyDown = (e: React.KeyboardEvent): void => {
|
||||||
let handled = true;
|
let handled = true;
|
||||||
const action = getKeyBindingsManager().getAccessibilityAction(e);
|
const action = getKeyBindingsManager().getAccessibilityAction(e);
|
||||||
|
|
||||||
@ -55,7 +55,7 @@ export const StyledMenuItemCheckbox: React.FC<IProps> = ({ children, label, onCh
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const onKeyUp = (e: React.KeyboardEvent) => {
|
const onKeyUp = (e: React.KeyboardEvent): void => {
|
||||||
const action = getKeyBindingsManager().getAccessibilityAction(e);
|
const action = getKeyBindingsManager().getAccessibilityAction(e);
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case KeyBindingAction.Space:
|
case KeyBindingAction.Space:
|
||||||
|
@ -33,7 +33,7 @@ interface IProps extends React.ComponentProps<typeof StyledRadioButton> {
|
|||||||
export const StyledMenuItemRadio: React.FC<IProps> = ({ children, label, onChange, onClose, ...props }) => {
|
export const StyledMenuItemRadio: React.FC<IProps> = ({ children, label, onChange, onClose, ...props }) => {
|
||||||
const [onFocus, isActive, ref] = useRovingTabIndex<HTMLInputElement>();
|
const [onFocus, isActive, ref] = useRovingTabIndex<HTMLInputElement>();
|
||||||
|
|
||||||
const onKeyDown = (e: React.KeyboardEvent) => {
|
const onKeyDown = (e: React.KeyboardEvent): void => {
|
||||||
let handled = true;
|
let handled = true;
|
||||||
const action = getKeyBindingsManager().getAccessibilityAction(e);
|
const action = getKeyBindingsManager().getAccessibilityAction(e);
|
||||||
|
|
||||||
@ -55,7 +55,7 @@ export const StyledMenuItemRadio: React.FC<IProps> = ({ children, label, onChang
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const onKeyUp = (e: React.KeyboardEvent) => {
|
const onKeyUp = (e: React.KeyboardEvent): void => {
|
||||||
const action = getKeyBindingsManager().getAccessibilityAction(e);
|
const action = getKeyBindingsManager().getAccessibilityAction(e);
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case KeyBindingAction.Enter:
|
case KeyBindingAction.Enter:
|
||||||
|
@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { AsyncActionPayload } from "../dispatcher/payloads";
|
import { AsyncActionFn, AsyncActionPayload } from "../dispatcher/payloads";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an action thunk that will dispatch actions indicating the current
|
* Create an action thunk that will dispatch actions indicating the current
|
||||||
@ -45,7 +45,7 @@ import { AsyncActionPayload } from "../dispatcher/payloads";
|
|||||||
* `fn`.
|
* `fn`.
|
||||||
*/
|
*/
|
||||||
export function asyncAction(id: string, fn: () => Promise<any>, pendingFn: () => any | null): AsyncActionPayload {
|
export function asyncAction(id: string, fn: () => Promise<any>, pendingFn: () => any | null): AsyncActionPayload {
|
||||||
const helper = (dispatch) => {
|
const helper: AsyncActionFn = (dispatch) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
action: id + ".pending",
|
action: id + ".pending",
|
||||||
request: typeof pendingFn === "function" ? pendingFn() : undefined,
|
request: typeof pendingFn === "function" ? pendingFn() : undefined,
|
||||||
|
@ -22,7 +22,7 @@ import defaultDispatcher from "../../dispatcher/dispatcher";
|
|||||||
* Redirect to the correct device manager section
|
* Redirect to the correct device manager section
|
||||||
* Based on the labs setting
|
* Based on the labs setting
|
||||||
*/
|
*/
|
||||||
export const viewUserDeviceSettings = (isNewDeviceManagerEnabled: boolean) => {
|
export const viewUserDeviceSettings = (isNewDeviceManagerEnabled: boolean): void => {
|
||||||
defaultDispatcher.dispatch({
|
defaultDispatcher.dispatch({
|
||||||
action: Action.ViewUserSettings,
|
action: Action.ViewUserSettings,
|
||||||
initialTabId: isNewDeviceManagerEnabled ? UserTab.SessionManager : UserTab.Security,
|
initialTabId: isNewDeviceManagerEnabled ? UserTab.SessionManager : UserTab.Security,
|
||||||
|
@ -56,7 +56,7 @@ export default class ManageEventIndexDialog extends React.Component<IProps, ISta
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public updateCurrentRoom = async (room) => {
|
public updateCurrentRoom = async (room): Promise<void> => {
|
||||||
const eventIndex = EventIndexPeg.get();
|
const eventIndex = EventIndexPeg.get();
|
||||||
let stats;
|
let stats;
|
||||||
|
|
||||||
@ -131,17 +131,17 @@ export default class ManageEventIndexDialog extends React.Component<IProps, ISta
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private onDisable = async () => {
|
private onDisable = async (): Promise<void> => {
|
||||||
const DisableEventIndexDialog = (await import("./DisableEventIndexDialog")).default;
|
const DisableEventIndexDialog = (await import("./DisableEventIndexDialog")).default;
|
||||||
Modal.createDialog(DisableEventIndexDialog, null, null, /* priority = */ false, /* static = */ true);
|
Modal.createDialog(DisableEventIndexDialog, null, null, /* priority = */ false, /* static = */ true);
|
||||||
};
|
};
|
||||||
|
|
||||||
private onCrawlerSleepTimeChange = (e) => {
|
private onCrawlerSleepTimeChange = (e): void => {
|
||||||
this.setState({ crawlerSleepTime: e.target.value });
|
this.setState({ crawlerSleepTime: e.target.value });
|
||||||
SettingsStore.setValue("crawlerSleepTime", null, SettingLevel.DEVICE, e.target.value);
|
SettingsStore.setValue("crawlerSleepTime", null, SettingLevel.DEVICE, e.target.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
public render() {
|
public render(): JSX.Element {
|
||||||
const brand = SdkConfig.get().brand;
|
const brand = SdkConfig.get().brand;
|
||||||
|
|
||||||
let crawlerState;
|
let crawlerState;
|
||||||
|
@ -125,7 +125,7 @@ export default class CreateKeyBackupDialog extends React.PureComponent<IProps, I
|
|||||||
let info;
|
let info;
|
||||||
try {
|
try {
|
||||||
if (secureSecretStorage) {
|
if (secureSecretStorage) {
|
||||||
await accessSecretStorage(async () => {
|
await accessSecretStorage(async (): Promise<void> => {
|
||||||
info = await MatrixClientPeg.get().prepareKeyBackupVersion(null /* random key */, {
|
info = await MatrixClientPeg.get().prepareKeyBackupVersion(null /* random key */, {
|
||||||
secureSecretStorage: true,
|
secureSecretStorage: true,
|
||||||
});
|
});
|
||||||
|
@ -350,7 +350,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
|
|||||||
createSecretStorageKey: async () => this.recoveryKey,
|
createSecretStorageKey: async () => this.recoveryKey,
|
||||||
keyBackupInfo: this.state.backupInfo,
|
keyBackupInfo: this.state.backupInfo,
|
||||||
setupNewKeyBackup: !this.state.backupInfo,
|
setupNewKeyBackup: !this.state.backupInfo,
|
||||||
getKeyBackupPassphrase: async () => {
|
getKeyBackupPassphrase: async (): Promise<Uint8Array> => {
|
||||||
// We may already have the backup key if we earlier went
|
// We may already have the backup key if we earlier went
|
||||||
// through the restore backup path, so pass it along
|
// through the restore backup path, so pass it along
|
||||||
// rather than prompting again.
|
// rather than prompting again.
|
||||||
@ -383,7 +383,9 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
|
|||||||
private restoreBackup = async (): Promise<void> => {
|
private restoreBackup = async (): Promise<void> => {
|
||||||
// It's possible we'll need the backup key later on for bootstrapping,
|
// It's possible we'll need the backup key later on for bootstrapping,
|
||||||
// so let's stash it here, rather than prompting for it twice.
|
// so let's stash it here, rather than prompting for it twice.
|
||||||
const keyCallback = (k) => (this.backupKey = k);
|
const keyCallback = (k: Uint8Array): void => {
|
||||||
|
this.backupKey = k;
|
||||||
|
};
|
||||||
|
|
||||||
const { finished } = Modal.createDialog(
|
const { finished } = Modal.createDialog(
|
||||||
RestoreKeyBackupDialog,
|
RestoreKeyBackupDialog,
|
||||||
@ -420,7 +422,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
|
|||||||
this.setState({ phase: Phase.ChooseKeyPassphrase });
|
this.setState({ phase: Phase.ChooseKeyPassphrase });
|
||||||
};
|
};
|
||||||
|
|
||||||
private onPassPhraseNextClick = async (e: React.FormEvent) => {
|
private onPassPhraseNextClick = async (e: React.FormEvent): Promise<void> => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (!this.passphraseField.current) return; // unmounting
|
if (!this.passphraseField.current) return; // unmounting
|
||||||
|
|
||||||
@ -434,7 +436,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
|
|||||||
this.setState({ phase: Phase.PassphraseConfirm });
|
this.setState({ phase: Phase.PassphraseConfirm });
|
||||||
};
|
};
|
||||||
|
|
||||||
private onPassPhraseConfirmNextClick = async (e: React.FormEvent) => {
|
private onPassPhraseConfirmNextClick = async (e: React.FormEvent): Promise<void> => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
if (this.state.passPhrase !== this.state.passPhraseConfirm) return;
|
if (this.state.passPhrase !== this.state.passPhraseConfirm) return;
|
||||||
|
@ -121,7 +121,7 @@ export default class ExportE2eKeysDialog extends React.Component<IProps, IState>
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
private onPassphraseChange = (ev: React.ChangeEvent<HTMLInputElement>, phrase: AnyPassphrase) => {
|
private onPassphraseChange = (ev: React.ChangeEvent<HTMLInputElement>, phrase: AnyPassphrase): void => {
|
||||||
this.setState({
|
this.setState({
|
||||||
[phrase]: ev.target.value,
|
[phrase]: ev.target.value,
|
||||||
} as Pick<IState, AnyPassphrase>);
|
} as Pick<IState, AnyPassphrase>);
|
||||||
|
@ -91,7 +91,7 @@ export default class ImportE2eKeysDialog extends React.Component<IProps, IState>
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
private startImport(file: File, passphrase: string) {
|
private startImport(file: File, passphrase: string): Promise<void> {
|
||||||
this.setState({
|
this.setState({
|
||||||
errStr: null,
|
errStr: null,
|
||||||
phase: Phase.Importing,
|
phase: Phase.Importing,
|
||||||
|
@ -30,7 +30,7 @@ export class ManagedPlayback extends Playback {
|
|||||||
return super.play();
|
return super.play();
|
||||||
}
|
}
|
||||||
|
|
||||||
public destroy() {
|
public destroy(): void {
|
||||||
this.manager.destroyPlaybackInstance(this);
|
this.manager.destroyPlaybackInstance(this);
|
||||||
super.destroy();
|
super.destroy();
|
||||||
}
|
}
|
||||||
|
@ -145,7 +145,7 @@ export class Playback extends EventEmitter implements IDestroyable, PlaybackInte
|
|||||||
return true; // we don't ever care if the event had listeners, so just return "yes"
|
return true; // we don't ever care if the event had listeners, so just return "yes"
|
||||||
}
|
}
|
||||||
|
|
||||||
public destroy() {
|
public destroy(): void {
|
||||||
// Dev note: It's critical that we call stop() during cleanup to ensure that downstream callers
|
// Dev note: It's critical that we call stop() during cleanup to ensure that downstream callers
|
||||||
// are aware of the final clock position before the user triggered an unload.
|
// are aware of the final clock position before the user triggered an unload.
|
||||||
// noinspection JSIgnoredPromiseFromCall - not concerned about being called async here
|
// noinspection JSIgnoredPromiseFromCall - not concerned about being called async here
|
||||||
@ -159,7 +159,7 @@ export class Playback extends EventEmitter implements IDestroyable, PlaybackInte
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async prepare() {
|
public async prepare(): Promise<void> {
|
||||||
// don't attempt to decode the media again
|
// don't attempt to decode the media again
|
||||||
// AudioContext.decodeAudioData detaches the array buffer `this.buf`
|
// AudioContext.decodeAudioData detaches the array buffer `this.buf`
|
||||||
// meaning it cannot be re-read
|
// meaning it cannot be re-read
|
||||||
@ -190,7 +190,7 @@ export class Playback extends EventEmitter implements IDestroyable, PlaybackInte
|
|||||||
this.context.decodeAudioData(
|
this.context.decodeAudioData(
|
||||||
this.buf,
|
this.buf,
|
||||||
(b) => resolve(b),
|
(b) => resolve(b),
|
||||||
async (e) => {
|
async (e): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
// This error handler is largely for Safari as well, which doesn't support Opus/Ogg
|
// This error handler is largely for Safari as well, which doesn't support Opus/Ogg
|
||||||
// very well.
|
// very well.
|
||||||
@ -232,12 +232,12 @@ export class Playback extends EventEmitter implements IDestroyable, PlaybackInte
|
|||||||
this.emit(PlaybackState.Stopped); // signal that we're not decoding anymore
|
this.emit(PlaybackState.Stopped); // signal that we're not decoding anymore
|
||||||
}
|
}
|
||||||
|
|
||||||
private onPlaybackEnd = async () => {
|
private onPlaybackEnd = async (): Promise<void> => {
|
||||||
await this.context.suspend();
|
await this.context.suspend();
|
||||||
this.emit(PlaybackState.Stopped);
|
this.emit(PlaybackState.Stopped);
|
||||||
};
|
};
|
||||||
|
|
||||||
public async play() {
|
public async play(): Promise<void> {
|
||||||
// We can't restart a buffer source, so we need to create a new one if we hit the end
|
// We can't restart a buffer source, so we need to create a new one if we hit the end
|
||||||
if (this.state === PlaybackState.Stopped) {
|
if (this.state === PlaybackState.Stopped) {
|
||||||
this.disconnectSource();
|
this.disconnectSource();
|
||||||
@ -256,13 +256,13 @@ export class Playback extends EventEmitter implements IDestroyable, PlaybackInte
|
|||||||
this.emit(PlaybackState.Playing);
|
this.emit(PlaybackState.Playing);
|
||||||
}
|
}
|
||||||
|
|
||||||
private disconnectSource() {
|
private disconnectSource(): void {
|
||||||
if (this.element) return; // leave connected, we can (and must) re-use it
|
if (this.element) return; // leave connected, we can (and must) re-use it
|
||||||
this.source?.disconnect();
|
this.source?.disconnect();
|
||||||
this.source?.removeEventListener("ended", this.onPlaybackEnd);
|
this.source?.removeEventListener("ended", this.onPlaybackEnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
private makeNewSourceBuffer() {
|
private makeNewSourceBuffer(): void {
|
||||||
if (this.element && this.source) return; // leave connected, we can (and must) re-use it
|
if (this.element && this.source) return; // leave connected, we can (and must) re-use it
|
||||||
|
|
||||||
if (this.element) {
|
if (this.element) {
|
||||||
@ -276,22 +276,22 @@ export class Playback extends EventEmitter implements IDestroyable, PlaybackInte
|
|||||||
this.source.connect(this.context.destination);
|
this.source.connect(this.context.destination);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async pause() {
|
public async pause(): Promise<void> {
|
||||||
await this.context.suspend();
|
await this.context.suspend();
|
||||||
this.emit(PlaybackState.Paused);
|
this.emit(PlaybackState.Paused);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async stop() {
|
public async stop(): Promise<void> {
|
||||||
await this.onPlaybackEnd();
|
await this.onPlaybackEnd();
|
||||||
this.clock.flagStop();
|
this.clock.flagStop();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async toggle() {
|
public async toggle(): Promise<void> {
|
||||||
if (this.isPlaying) await this.pause();
|
if (this.isPlaying) await this.pause();
|
||||||
else await this.play();
|
else await this.play();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async skipTo(timeSeconds: number) {
|
public async skipTo(timeSeconds: number): Promise<void> {
|
||||||
// Dev note: this function talks a lot about clock desyncs. There is a clock running
|
// Dev note: this function talks a lot about clock desyncs. There is a clock running
|
||||||
// independently to the audio context and buffer so that accurate human-perceptible
|
// independently to the audio context and buffer so that accurate human-perceptible
|
||||||
// time can be exposed. The PlaybackClock class has more information, but the short
|
// time can be exposed. The PlaybackClock class has more information, but the short
|
||||||
|
@ -89,7 +89,7 @@ export class PlaybackClock implements IDestroyable {
|
|||||||
return this.observable;
|
return this.observable;
|
||||||
}
|
}
|
||||||
|
|
||||||
private checkTime = (force = false) => {
|
private checkTime = (force = false): void => {
|
||||||
const now = this.timeSeconds; // calculated dynamically
|
const now = this.timeSeconds; // calculated dynamically
|
||||||
if (this.lastCheck !== now || force) {
|
if (this.lastCheck !== now || force) {
|
||||||
this.observable.update([now, this.durationSeconds]);
|
this.observable.update([now, this.durationSeconds]);
|
||||||
@ -102,7 +102,7 @@ export class PlaybackClock implements IDestroyable {
|
|||||||
* The placeholders will be overridden once known.
|
* The placeholders will be overridden once known.
|
||||||
* @param {MatrixEvent} event The event to use for placeholders.
|
* @param {MatrixEvent} event The event to use for placeholders.
|
||||||
*/
|
*/
|
||||||
public populatePlaceholdersFrom(event: MatrixEvent) {
|
public populatePlaceholdersFrom(event: MatrixEvent): void {
|
||||||
const durationMs = Number(event.getContent()["info"]?.["duration"]);
|
const durationMs = Number(event.getContent()["info"]?.["duration"]);
|
||||||
if (Number.isFinite(durationMs)) this.placeholderDuration = durationMs / 1000;
|
if (Number.isFinite(durationMs)) this.placeholderDuration = durationMs / 1000;
|
||||||
}
|
}
|
||||||
@ -112,11 +112,11 @@ export class PlaybackClock implements IDestroyable {
|
|||||||
* This is to ensure the clock isn't skewed into thinking it is ~0.5s into
|
* This is to ensure the clock isn't skewed into thinking it is ~0.5s into
|
||||||
* a clip when the duration is set.
|
* a clip when the duration is set.
|
||||||
*/
|
*/
|
||||||
public flagLoadTime() {
|
public flagLoadTime(): void {
|
||||||
this.clipStart = this.context.currentTime;
|
this.clipStart = this.context.currentTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
public flagStart() {
|
public flagStart(): void {
|
||||||
if (this.stopped) {
|
if (this.stopped) {
|
||||||
this.clipStart = this.context.currentTime;
|
this.clipStart = this.context.currentTime;
|
||||||
this.stopped = false;
|
this.stopped = false;
|
||||||
@ -128,7 +128,7 @@ export class PlaybackClock implements IDestroyable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public flagStop() {
|
public flagStop(): void {
|
||||||
this.stopped = true;
|
this.stopped = true;
|
||||||
|
|
||||||
// Reset the clock time now so that the update going out will trigger components
|
// Reset the clock time now so that the update going out will trigger components
|
||||||
@ -136,13 +136,13 @@ export class PlaybackClock implements IDestroyable {
|
|||||||
this.clipStart = this.context.currentTime;
|
this.clipStart = this.context.currentTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
public syncTo(contextTime: number, clipTime: number) {
|
public syncTo(contextTime: number, clipTime: number): void {
|
||||||
this.clipStart = contextTime - clipTime;
|
this.clipStart = contextTime - clipTime;
|
||||||
this.stopped = false; // count as a mid-stream pause (if we were stopped)
|
this.stopped = false; // count as a mid-stream pause (if we were stopped)
|
||||||
this.checkTime(true);
|
this.checkTime(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public destroy() {
|
public destroy(): void {
|
||||||
this.observable.close();
|
this.observable.close();
|
||||||
if (this.timerId) clearInterval(this.timerId);
|
if (this.timerId) clearInterval(this.timerId);
|
||||||
}
|
}
|
||||||
|
@ -38,13 +38,13 @@ export class PlaybackManager {
|
|||||||
* instances are paused.
|
* instances are paused.
|
||||||
* @param playback Optional. The playback to leave untouched.
|
* @param playback Optional. The playback to leave untouched.
|
||||||
*/
|
*/
|
||||||
public pauseAllExcept(playback?: Playback) {
|
public pauseAllExcept(playback?: Playback): void {
|
||||||
this.instances
|
this.instances
|
||||||
.filter((p) => p !== playback && p.currentState === PlaybackState.Playing)
|
.filter((p) => p !== playback && p.currentState === PlaybackState.Playing)
|
||||||
.forEach((p) => p.pause());
|
.forEach((p) => p.pause());
|
||||||
}
|
}
|
||||||
|
|
||||||
public destroyPlaybackInstance(playback: ManagedPlayback) {
|
public destroyPlaybackInstance(playback: ManagedPlayback): void {
|
||||||
this.instances = this.instances.filter((p) => p !== playback);
|
this.instances = this.instances.filter((p) => p !== playback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,28 +75,28 @@ export class PlaybackQueue {
|
|||||||
return queue;
|
return queue;
|
||||||
}
|
}
|
||||||
|
|
||||||
private persistClocks() {
|
private persistClocks(): void {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
`mx_voice_message_clocks_${this.room.roomId}`,
|
`mx_voice_message_clocks_${this.room.roomId}`,
|
||||||
JSON.stringify(Array.from(this.clockStates.entries())),
|
JSON.stringify(Array.from(this.clockStates.entries())),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private loadClocks() {
|
private loadClocks(): void {
|
||||||
const val = localStorage.getItem(`mx_voice_message_clocks_${this.room.roomId}`);
|
const val = localStorage.getItem(`mx_voice_message_clocks_${this.room.roomId}`);
|
||||||
if (!!val) {
|
if (!!val) {
|
||||||
this.clockStates = new Map<string, number>(JSON.parse(val));
|
this.clockStates = new Map<string, number>(JSON.parse(val));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsortedEnqueue(mxEvent: MatrixEvent, playback: Playback) {
|
public unsortedEnqueue(mxEvent: MatrixEvent, playback: Playback): void {
|
||||||
// We don't ever detach our listeners: we expect the Playback to clean up for us
|
// We don't ever detach our listeners: we expect the Playback to clean up for us
|
||||||
this.playbacks.set(mxEvent.getId(), playback);
|
this.playbacks.set(mxEvent.getId(), playback);
|
||||||
playback.on(UPDATE_EVENT, (state) => this.onPlaybackStateChange(playback, mxEvent, state));
|
playback.on(UPDATE_EVENT, (state) => this.onPlaybackStateChange(playback, mxEvent, state));
|
||||||
playback.clockInfo.liveData.onUpdate((clock) => this.onPlaybackClock(playback, mxEvent, clock));
|
playback.clockInfo.liveData.onUpdate((clock) => this.onPlaybackClock(playback, mxEvent, clock));
|
||||||
}
|
}
|
||||||
|
|
||||||
private onPlaybackStateChange(playback: Playback, mxEvent: MatrixEvent, newState: PlaybackState) {
|
private onPlaybackStateChange(playback: Playback, mxEvent: MatrixEvent, newState: PlaybackState): void {
|
||||||
// Remember where the user got to in playback
|
// Remember where the user got to in playback
|
||||||
const wasLastPlaying = this.currentPlaybackId === mxEvent.getId();
|
const wasLastPlaying = this.currentPlaybackId === mxEvent.getId();
|
||||||
if (newState === PlaybackState.Stopped && this.clockStates.has(mxEvent.getId()) && !wasLastPlaying) {
|
if (newState === PlaybackState.Stopped && this.clockStates.has(mxEvent.getId()) && !wasLastPlaying) {
|
||||||
@ -210,7 +210,7 @@ export class PlaybackQueue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private onPlaybackClock(playback: Playback, mxEvent: MatrixEvent, clocks: number[]) {
|
private onPlaybackClock(playback: Playback, mxEvent: MatrixEvent, clocks: number[]): void {
|
||||||
if (playback.currentState === PlaybackState.Decoding) return; // ignore pre-ready values
|
if (playback.currentState === PlaybackState.Decoding) return; // ignore pre-ready values
|
||||||
|
|
||||||
if (playback.currentState !== PlaybackState.Stopped) {
|
if (playback.currentState !== PlaybackState.Stopped) {
|
||||||
|
@ -43,7 +43,7 @@ class MxVoiceWorklet extends AudioWorkletProcessor {
|
|||||||
private nextAmplitudeSecond = 0;
|
private nextAmplitudeSecond = 0;
|
||||||
private amplitudeIndex = 0;
|
private amplitudeIndex = 0;
|
||||||
|
|
||||||
public process(inputs, outputs, parameters) {
|
public process(inputs, outputs, parameters): boolean {
|
||||||
const currentSecond = roundTimeToTargetFreq(currentTime);
|
const currentSecond = roundTimeToTargetFreq(currentTime);
|
||||||
// We special case the first ping because there's a fairly good chance that we'll miss the zeroth
|
// We special case the first ping because there's a fairly good chance that we'll miss the zeroth
|
||||||
// update. Firefox for instance takes 0.06 seconds (roughly) to call this function for the first
|
// update. Firefox for instance takes 0.06 seconds (roughly) to call this function for the first
|
||||||
|
@ -141,7 +141,7 @@ export class VoiceMessageRecording implements IDestroyable {
|
|||||||
this.voiceRecording.destroy();
|
this.voiceRecording.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
private onDataAvailable = (data: ArrayBuffer) => {
|
private onDataAvailable = (data: ArrayBuffer): void => {
|
||||||
const buf = new Uint8Array(data);
|
const buf = new Uint8Array(data);
|
||||||
this.buffer = concat(this.buffer, buf);
|
this.buffer = concat(this.buffer, buf);
|
||||||
};
|
};
|
||||||
@ -153,6 +153,6 @@ export class VoiceMessageRecording implements IDestroyable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createVoiceMessageRecording = (matrixClient: MatrixClient) => {
|
export const createVoiceMessageRecording = (matrixClient: MatrixClient): VoiceMessageRecording => {
|
||||||
return new VoiceMessageRecording(matrixClient, new VoiceRecording());
|
return new VoiceMessageRecording(matrixClient, new VoiceRecording());
|
||||||
};
|
};
|
||||||
|
@ -110,7 +110,7 @@ export class VoiceRecording extends EventEmitter implements IDestroyable {
|
|||||||
return !MediaDeviceHandler.getAudioNoiseSuppression();
|
return !MediaDeviceHandler.getAudioNoiseSuppression();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async makeRecorder() {
|
private async makeRecorder(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
this.recorderStream = await navigator.mediaDevices.getUserMedia({
|
this.recorderStream = await navigator.mediaDevices.getUserMedia({
|
||||||
audio: {
|
audio: {
|
||||||
@ -212,14 +212,14 @@ export class VoiceRecording extends EventEmitter implements IDestroyable {
|
|||||||
return !!Recorder.isRecordingSupported();
|
return !!Recorder.isRecordingSupported();
|
||||||
}
|
}
|
||||||
|
|
||||||
private onAudioProcess = (ev: AudioProcessingEvent) => {
|
private onAudioProcess = (ev: AudioProcessingEvent): void => {
|
||||||
this.processAudioUpdate(ev.playbackTime);
|
this.processAudioUpdate(ev.playbackTime);
|
||||||
|
|
||||||
// We skip the functionality of the worklet regarding waveform calculations: we
|
// We skip the functionality of the worklet regarding waveform calculations: we
|
||||||
// should get that information pretty quick during the playback info.
|
// should get that information pretty quick during the playback info.
|
||||||
};
|
};
|
||||||
|
|
||||||
private processAudioUpdate = (timeSeconds: number) => {
|
private processAudioUpdate = (timeSeconds: number): void => {
|
||||||
if (!this.recording) return;
|
if (!this.recording) return;
|
||||||
|
|
||||||
this.observable.update({
|
this.observable.update({
|
||||||
@ -260,7 +260,7 @@ export class VoiceRecording extends EventEmitter implements IDestroyable {
|
|||||||
/**
|
/**
|
||||||
* {@link https://github.com/chris-rudmin/opus-recorder#instance-fields ref for recorderSeconds}
|
* {@link https://github.com/chris-rudmin/opus-recorder#instance-fields ref for recorderSeconds}
|
||||||
*/
|
*/
|
||||||
public get recorderSeconds() {
|
public get recorderSeconds(): number {
|
||||||
return this.recorder.encodedSamplePosition / 48000;
|
return this.recorder.encodedSamplePosition / 48000;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,7 +279,7 @@ export class VoiceRecording extends EventEmitter implements IDestroyable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async stop(): Promise<void> {
|
public async stop(): Promise<void> {
|
||||||
return Singleflight.for(this, "stop").do(async () => {
|
return Singleflight.for(this, "stop").do(async (): Promise<void> => {
|
||||||
if (!this.recording) {
|
if (!this.recording) {
|
||||||
throw new Error("No recording to stop");
|
throw new Error("No recording to stop");
|
||||||
}
|
}
|
||||||
@ -307,7 +307,7 @@ export class VoiceRecording extends EventEmitter implements IDestroyable {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public destroy() {
|
public destroy(): void {
|
||||||
// noinspection JSIgnoredPromiseFromCall - not concerned about stop() being called async here
|
// noinspection JSIgnoredPromiseFromCall - not concerned about stop() being called async here
|
||||||
this.stop();
|
this.stop();
|
||||||
this.removeAllListeners();
|
this.removeAllListeners();
|
||||||
|
@ -22,7 +22,7 @@ import { TimelineRenderingType } from "../contexts/RoomContext";
|
|||||||
import type { ICompletion, ISelectionRange } from "./Autocompleter";
|
import type { ICompletion, ISelectionRange } from "./Autocompleter";
|
||||||
|
|
||||||
export interface ICommand {
|
export interface ICommand {
|
||||||
command: string | null;
|
command: RegExpExecArray | null;
|
||||||
range: {
|
range: {
|
||||||
start: number;
|
start: number;
|
||||||
end: number;
|
end: number;
|
||||||
@ -59,7 +59,7 @@ export default abstract class AutocompleteProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public destroy() {
|
public destroy(): void {
|
||||||
// stub
|
// stub
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,7 +70,7 @@ export default abstract class AutocompleteProvider {
|
|||||||
* @param {boolean} force True if the user is forcing completion
|
* @param {boolean} force True if the user is forcing completion
|
||||||
* @return {object} { command, range } where both objects fields are null if no match
|
* @return {object} { command, range } where both objects fields are null if no match
|
||||||
*/
|
*/
|
||||||
public getCurrentCommand(query: string, selection: ISelectionRange, force = false) {
|
public getCurrentCommand(query: string, selection: ISelectionRange, force = false): ICommand {
|
||||||
let commandRegex = this.commandRegex;
|
let commandRegex = this.commandRegex;
|
||||||
|
|
||||||
if (force && this.shouldForceComplete()) {
|
if (force && this.shouldForceComplete()) {
|
||||||
@ -83,7 +83,7 @@ export default abstract class AutocompleteProvider {
|
|||||||
|
|
||||||
commandRegex.lastIndex = 0;
|
commandRegex.lastIndex = 0;
|
||||||
|
|
||||||
let match;
|
let match: RegExpExecArray;
|
||||||
while ((match = commandRegex.exec(query)) !== null) {
|
while ((match = commandRegex.exec(query)) !== null) {
|
||||||
const start = match.index;
|
const start = match.index;
|
||||||
const end = start + match[0].length;
|
const end = start + match[0].length;
|
||||||
|
@ -69,7 +69,7 @@ export default class Autocompleter {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public destroy() {
|
public destroy(): void {
|
||||||
this.providers.forEach((p) => {
|
this.providers.forEach((p) => {
|
||||||
p.destroy();
|
p.destroy();
|
||||||
});
|
});
|
||||||
@ -88,7 +88,7 @@ export default class Autocompleter {
|
|||||||
*/
|
*/
|
||||||
// list of results from each provider, each being a list of completions or null if it times out
|
// list of results from each provider, each being a list of completions or null if it times out
|
||||||
const completionsList: ICompletion[][] = await Promise.all(
|
const completionsList: ICompletion[][] = await Promise.all(
|
||||||
this.providers.map(async (provider) => {
|
this.providers.map(async (provider): Promise<ICompletion[] | null> => {
|
||||||
return timeout(
|
return timeout(
|
||||||
provider.getCompletions(query, selection, force, limit),
|
provider.getCompletions(query, selection, force, limit),
|
||||||
null,
|
null,
|
||||||
|
@ -100,7 +100,7 @@ export default class CommandProvider extends AutocompleteProvider {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public getName() {
|
public getName(): string {
|
||||||
return "*️⃣ " + _t("Commands");
|
return "*️⃣ " + _t("Commands");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ const SORTED_EMOJI: ISortedEmoji[] = EMOJI.sort((a, b) => {
|
|||||||
_orderBy: index,
|
_orderBy: index,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
function score(query, space) {
|
function score(query: string, space: string): number {
|
||||||
const index = space.indexOf(query);
|
const index = space.indexOf(query);
|
||||||
if (index === -1) {
|
if (index === -1) {
|
||||||
return Infinity;
|
return Infinity;
|
||||||
@ -154,7 +154,7 @@ export default class EmojiProvider extends AutocompleteProvider {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public getName() {
|
public getName(): string {
|
||||||
return "😃 " + _t("Emoji");
|
return "😃 " + _t("Emoji");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ export default class NotifProvider extends AutocompleteProvider {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public getName() {
|
public getName(): string {
|
||||||
return "❗️ " + _t("Room Notification");
|
return "❗️ " + _t("Room Notification");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@ export default class QueryMatcher<T extends {}> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public setObjects(objects: T[]) {
|
public setObjects(objects: T[]): void {
|
||||||
this._items = new Map();
|
this._items = new Map();
|
||||||
|
|
||||||
for (const object of objects) {
|
for (const object of objects) {
|
||||||
|
@ -37,7 +37,15 @@ function canonicalScore(displayedAlias: string, room: Room): number {
|
|||||||
return displayedAlias === room.getCanonicalAlias() ? 0 : 1;
|
return displayedAlias === room.getCanonicalAlias() ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
function matcherObject(room: Room, displayedAlias: string, matchName = "") {
|
function matcherObject(
|
||||||
|
room: Room,
|
||||||
|
displayedAlias: string,
|
||||||
|
matchName = "",
|
||||||
|
): {
|
||||||
|
room: Room;
|
||||||
|
matchName: string;
|
||||||
|
displayedAlias: string;
|
||||||
|
} {
|
||||||
return {
|
return {
|
||||||
room,
|
room,
|
||||||
matchName,
|
matchName,
|
||||||
@ -46,7 +54,7 @@ function matcherObject(room: Room, displayedAlias: string, matchName = "") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default class RoomProvider extends AutocompleteProvider {
|
export default class RoomProvider extends AutocompleteProvider {
|
||||||
protected matcher: QueryMatcher<Room>;
|
protected matcher: QueryMatcher<ReturnType<typeof matcherObject>>;
|
||||||
|
|
||||||
public constructor(room: Room, renderingType?: TimelineRenderingType) {
|
public constructor(room: Room, renderingType?: TimelineRenderingType) {
|
||||||
super({ commandRegex: ROOM_REGEX, renderingType });
|
super({ commandRegex: ROOM_REGEX, renderingType });
|
||||||
@ -55,7 +63,7 @@ export default class RoomProvider extends AutocompleteProvider {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected getRooms() {
|
protected getRooms(): Room[] {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
|
|
||||||
// filter out spaces here as they get their own autocomplete provider
|
// filter out spaces here as they get their own autocomplete provider
|
||||||
@ -68,7 +76,6 @@ export default class RoomProvider extends AutocompleteProvider {
|
|||||||
force = false,
|
force = false,
|
||||||
limit = -1,
|
limit = -1,
|
||||||
): Promise<ICompletion[]> {
|
): Promise<ICompletion[]> {
|
||||||
let completions = [];
|
|
||||||
const { command, range } = this.getCurrentCommand(query, selection, force);
|
const { command, range } = this.getCurrentCommand(query, selection, force);
|
||||||
if (command) {
|
if (command) {
|
||||||
// the only reason we need to do this is because Fuse only matches on properties
|
// the only reason we need to do this is because Fuse only matches on properties
|
||||||
@ -96,15 +103,15 @@ export default class RoomProvider extends AutocompleteProvider {
|
|||||||
|
|
||||||
this.matcher.setObjects(matcherObjects);
|
this.matcher.setObjects(matcherObjects);
|
||||||
const matchedString = command[0];
|
const matchedString = command[0];
|
||||||
completions = this.matcher.match(matchedString, limit);
|
let completions = this.matcher.match(matchedString, limit);
|
||||||
completions = sortBy(completions, [
|
completions = sortBy(completions, [
|
||||||
(c) => canonicalScore(c.displayedAlias, c.room),
|
(c) => canonicalScore(c.displayedAlias, c.room),
|
||||||
(c) => c.displayedAlias.length,
|
(c) => c.displayedAlias.length,
|
||||||
]);
|
]);
|
||||||
completions = uniqBy(completions, (match) => match.room);
|
completions = uniqBy(completions, (match) => match.room);
|
||||||
completions = completions
|
return completions
|
||||||
.map((room) => {
|
.map(
|
||||||
return {
|
(room): ICompletion => ({
|
||||||
completion: room.displayedAlias,
|
completion: room.displayedAlias,
|
||||||
completionId: room.room.roomId,
|
completionId: room.room.roomId,
|
||||||
type: "room",
|
type: "room",
|
||||||
@ -116,14 +123,14 @@ export default class RoomProvider extends AutocompleteProvider {
|
|||||||
</PillCompletion>
|
</PillCompletion>
|
||||||
),
|
),
|
||||||
range,
|
range,
|
||||||
};
|
}),
|
||||||
})
|
)
|
||||||
.filter((completion) => !!completion.completion && completion.completion.length > 0);
|
.filter((completion) => !!completion.completion && completion.completion.length > 0);
|
||||||
}
|
}
|
||||||
return completions;
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public getName() {
|
public getName(): string {
|
||||||
return _t("Rooms");
|
return _t("Rooms");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { Room } from "matrix-js-sdk/src/models/room";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import { _t } from "../languageHandler";
|
import { _t } from "../languageHandler";
|
||||||
@ -21,13 +22,13 @@ import { MatrixClientPeg } from "../MatrixClientPeg";
|
|||||||
import RoomProvider from "./RoomProvider";
|
import RoomProvider from "./RoomProvider";
|
||||||
|
|
||||||
export default class SpaceProvider extends RoomProvider {
|
export default class SpaceProvider extends RoomProvider {
|
||||||
protected getRooms() {
|
protected getRooms(): Room[] {
|
||||||
return MatrixClientPeg.get()
|
return MatrixClientPeg.get()
|
||||||
.getVisibleRooms()
|
.getVisibleRooms()
|
||||||
.filter((r) => r.isSpaceRoom());
|
.filter((r) => r.isSpaceRoom());
|
||||||
}
|
}
|
||||||
|
|
||||||
public getName() {
|
public getName(): string {
|
||||||
return _t("Spaces");
|
return _t("Spaces");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ export default class UserProvider extends AutocompleteProvider {
|
|||||||
MatrixClientPeg.get().on(RoomStateEvent.Update, this.onRoomStateUpdate);
|
MatrixClientPeg.get().on(RoomStateEvent.Update, this.onRoomStateUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
public destroy() {
|
public destroy(): void {
|
||||||
if (MatrixClientPeg.get()) {
|
if (MatrixClientPeg.get()) {
|
||||||
MatrixClientPeg.get().removeListener(RoomEvent.Timeline, this.onRoomTimeline);
|
MatrixClientPeg.get().removeListener(RoomEvent.Timeline, this.onRoomTimeline);
|
||||||
MatrixClientPeg.get().removeListener(RoomStateEvent.Update, this.onRoomStateUpdate);
|
MatrixClientPeg.get().removeListener(RoomStateEvent.Update, this.onRoomStateUpdate);
|
||||||
@ -77,7 +77,7 @@ export default class UserProvider extends AutocompleteProvider {
|
|||||||
toStartOfTimeline: boolean,
|
toStartOfTimeline: boolean,
|
||||||
removed: boolean,
|
removed: boolean,
|
||||||
data: IRoomTimelineData,
|
data: IRoomTimelineData,
|
||||||
) => {
|
): void => {
|
||||||
if (!room) return; // notification timeline, we'll get this event again with a room specific timeline
|
if (!room) return; // notification timeline, we'll get this event again with a room specific timeline
|
||||||
if (removed) return;
|
if (removed) return;
|
||||||
if (room.roomId !== this.room.roomId) return;
|
if (room.roomId !== this.room.roomId) return;
|
||||||
@ -93,7 +93,7 @@ export default class UserProvider extends AutocompleteProvider {
|
|||||||
this.onUserSpoke(ev.sender);
|
this.onUserSpoke(ev.sender);
|
||||||
};
|
};
|
||||||
|
|
||||||
private onRoomStateUpdate = (state: RoomState) => {
|
private onRoomStateUpdate = (state: RoomState): void => {
|
||||||
// ignore updates in other rooms
|
// ignore updates in other rooms
|
||||||
if (state.roomId !== this.room.roomId) return;
|
if (state.roomId !== this.room.roomId) return;
|
||||||
|
|
||||||
@ -150,7 +150,7 @@ export default class UserProvider extends AutocompleteProvider {
|
|||||||
return _t("Users");
|
return _t("Users");
|
||||||
}
|
}
|
||||||
|
|
||||||
private makeUsers() {
|
private makeUsers(): void {
|
||||||
const events = this.room.getLiveTimeline().getEvents();
|
const events = this.room.getLiveTimeline().getEvents();
|
||||||
const lastSpoken = {};
|
const lastSpoken = {};
|
||||||
|
|
||||||
@ -167,7 +167,7 @@ export default class UserProvider extends AutocompleteProvider {
|
|||||||
this.matcher.setObjects(this.users);
|
this.matcher.setObjects(this.users);
|
||||||
}
|
}
|
||||||
|
|
||||||
public onUserSpoke(user: RoomMember) {
|
public onUserSpoke(user: RoomMember): void {
|
||||||
if (!this.users) return;
|
if (!this.users) return;
|
||||||
if (!user) return;
|
if (!user) return;
|
||||||
if (user.userId === MatrixClientPeg.get().credentials.userId) return;
|
if (user.userId === MatrixClientPeg.get().credentials.userId) return;
|
||||||
|
@ -22,7 +22,7 @@ type DynamicHtmlElementProps<T extends keyof JSX.IntrinsicElements> =
|
|||||||
JSX.IntrinsicElements[T] extends HTMLAttributes<{}> ? DynamicElementProps<T> : DynamicElementProps<"div">;
|
JSX.IntrinsicElements[T] extends HTMLAttributes<{}> ? DynamicElementProps<T> : DynamicElementProps<"div">;
|
||||||
type DynamicElementProps<T extends keyof JSX.IntrinsicElements> = Partial<Omit<JSX.IntrinsicElements[T], "ref">>;
|
type DynamicElementProps<T extends keyof JSX.IntrinsicElements> = Partial<Omit<JSX.IntrinsicElements[T], "ref">>;
|
||||||
|
|
||||||
export type IProps<T extends keyof JSX.IntrinsicElements> = DynamicHtmlElementProps<T> & {
|
export type IProps<T extends keyof JSX.IntrinsicElements> = Omit<DynamicHtmlElementProps<T>, "onScroll"> & {
|
||||||
element?: T;
|
element?: T;
|
||||||
className?: string;
|
className?: string;
|
||||||
onScroll?: (event: Event) => void;
|
onScroll?: (event: Event) => void;
|
||||||
@ -39,7 +39,7 @@ export default class AutoHideScrollbar<T extends keyof JSX.IntrinsicElements> ex
|
|||||||
|
|
||||||
public readonly containerRef: React.RefObject<HTMLDivElement> = React.createRef();
|
public readonly containerRef: React.RefObject<HTMLDivElement> = React.createRef();
|
||||||
|
|
||||||
public componentDidMount() {
|
public componentDidMount(): void {
|
||||||
if (this.containerRef.current && this.props.onScroll) {
|
if (this.containerRef.current && this.props.onScroll) {
|
||||||
// Using the passive option to not block the main thread
|
// Using the passive option to not block the main thread
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#improving_scrolling_performance_with_passive_listeners
|
// https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#improving_scrolling_performance_with_passive_listeners
|
||||||
@ -49,13 +49,13 @@ export default class AutoHideScrollbar<T extends keyof JSX.IntrinsicElements> ex
|
|||||||
this.props.wrappedRef?.(this.containerRef.current);
|
this.props.wrappedRef?.(this.containerRef.current);
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentWillUnmount() {
|
public componentWillUnmount(): void {
|
||||||
if (this.containerRef.current && this.props.onScroll) {
|
if (this.containerRef.current && this.props.onScroll) {
|
||||||
this.containerRef.current.removeEventListener("scroll", this.props.onScroll);
|
this.containerRef.current.removeEventListener("scroll", this.props.onScroll);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render(): JSX.Element {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
const { element, className, onScroll, tabIndex, wrappedRef, children, ...otherProps } = this.props;
|
const { element, className, onScroll, tabIndex, wrappedRef, children, ...otherProps } = this.props;
|
||||||
|
|
||||||
|
@ -52,11 +52,11 @@ export const AutocompleteInput: React.FC<AutocompleteInputProps> = ({
|
|||||||
const editorContainerRef = useRef<HTMLDivElement>(null);
|
const editorContainerRef = useRef<HTMLDivElement>(null);
|
||||||
const editorRef = useRef<HTMLInputElement>(null);
|
const editorRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
const focusEditor = () => {
|
const focusEditor = (): void => {
|
||||||
editorRef?.current?.focus();
|
editorRef?.current?.focus();
|
||||||
};
|
};
|
||||||
|
|
||||||
const onQueryChange = async (e: ChangeEvent<HTMLInputElement>) => {
|
const onQueryChange = async (e: ChangeEvent<HTMLInputElement>): Promise<void> => {
|
||||||
const value = e.target.value.trim();
|
const value = e.target.value.trim();
|
||||||
setQuery(value);
|
setQuery(value);
|
||||||
|
|
||||||
@ -74,11 +74,11 @@ export const AutocompleteInput: React.FC<AutocompleteInputProps> = ({
|
|||||||
setSuggestions(matches);
|
setSuggestions(matches);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onClickInputArea = () => {
|
const onClickInputArea = (): void => {
|
||||||
focusEditor();
|
focusEditor();
|
||||||
};
|
};
|
||||||
|
|
||||||
const onKeyDown = (e: KeyboardEvent) => {
|
const onKeyDown = (e: KeyboardEvent): void => {
|
||||||
const hasModifiers = e.ctrlKey || e.shiftKey || e.metaKey;
|
const hasModifiers = e.ctrlKey || e.shiftKey || e.metaKey;
|
||||||
|
|
||||||
// when the field is empty and the user hits backspace remove the right-most target
|
// when the field is empty and the user hits backspace remove the right-most target
|
||||||
@ -87,7 +87,7 @@ export const AutocompleteInput: React.FC<AutocompleteInputProps> = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const toggleSelection = (completion: ICompletion) => {
|
const toggleSelection = (completion: ICompletion): void => {
|
||||||
const newSelection = [...selection];
|
const newSelection = [...selection];
|
||||||
const index = selection.findIndex((selection) => selection.completionId === completion.completionId);
|
const index = selection.findIndex((selection) => selection.completionId === completion.completionId);
|
||||||
|
|
||||||
@ -101,7 +101,7 @@ export const AutocompleteInput: React.FC<AutocompleteInputProps> = ({
|
|||||||
focusEditor();
|
focusEditor();
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeSelection = (completion: ICompletion) => {
|
const removeSelection = (completion: ICompletion): void => {
|
||||||
const newSelection = [...selection];
|
const newSelection = [...selection];
|
||||||
const index = selection.findIndex((selection) => selection.completionId === completion.completionId);
|
const index = selection.findIndex((selection) => selection.completionId === completion.completionId);
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ export enum ChevronFace {
|
|||||||
None = "none",
|
None = "none",
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IProps extends IPosition {
|
export interface MenuProps extends IPosition {
|
||||||
menuWidth?: number;
|
menuWidth?: number;
|
||||||
menuHeight?: number;
|
menuHeight?: number;
|
||||||
|
|
||||||
@ -77,7 +77,9 @@ export interface IProps extends IPosition {
|
|||||||
menuPaddingRight?: number;
|
menuPaddingRight?: number;
|
||||||
|
|
||||||
zIndex?: number;
|
zIndex?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IProps extends MenuProps {
|
||||||
// If true, insert an invisible screen-sized element behind the menu that when clicked will close it.
|
// If true, insert an invisible screen-sized element behind the menu that when clicked will close it.
|
||||||
hasBackground?: boolean;
|
hasBackground?: boolean;
|
||||||
// whether this context menu should be focus managed. If false it must handle itself
|
// whether this context menu should be focus managed. If false it must handle itself
|
||||||
@ -128,21 +130,21 @@ export default class ContextMenu extends React.PureComponent<IProps, IState> {
|
|||||||
this.initialFocus = document.activeElement as HTMLElement;
|
this.initialFocus = document.activeElement as HTMLElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount() {
|
public componentDidMount(): void {
|
||||||
Modal.on(ModalManagerEvent.Opened, this.onModalOpen);
|
Modal.on(ModalManagerEvent.Opened, this.onModalOpen);
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentWillUnmount() {
|
public componentWillUnmount(): void {
|
||||||
Modal.off(ModalManagerEvent.Opened, this.onModalOpen);
|
Modal.off(ModalManagerEvent.Opened, this.onModalOpen);
|
||||||
// return focus to the thing which had it before us
|
// return focus to the thing which had it before us
|
||||||
this.initialFocus.focus();
|
this.initialFocus.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
private onModalOpen = () => {
|
private onModalOpen = (): void => {
|
||||||
this.props.onFinished?.();
|
this.props.onFinished?.();
|
||||||
};
|
};
|
||||||
|
|
||||||
private collectContextMenuRect = (element: HTMLDivElement) => {
|
private collectContextMenuRect = (element: HTMLDivElement): void => {
|
||||||
// We don't need to clean up when unmounting, so ignore
|
// We don't need to clean up when unmounting, so ignore
|
||||||
if (!element) return;
|
if (!element) return;
|
||||||
|
|
||||||
@ -159,7 +161,7 @@ export default class ContextMenu extends React.PureComponent<IProps, IState> {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
private onContextMenu = (e) => {
|
private onContextMenu = (e: React.MouseEvent): void => {
|
||||||
if (this.props.onFinished) {
|
if (this.props.onFinished) {
|
||||||
this.props.onFinished();
|
this.props.onFinished();
|
||||||
|
|
||||||
@ -184,20 +186,20 @@ export default class ContextMenu extends React.PureComponent<IProps, IState> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private onContextMenuPreventBubbling = (e) => {
|
private onContextMenuPreventBubbling = (e: React.MouseEvent): void => {
|
||||||
// stop propagation so that any context menu handlers don't leak out of this context menu
|
// stop propagation so that any context menu handlers don't leak out of this context menu
|
||||||
// but do not inhibit the default browser menu
|
// but do not inhibit the default browser menu
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Prevent clicks on the background from going through to the component which opened the menu.
|
// Prevent clicks on the background from going through to the component which opened the menu.
|
||||||
private onFinished = (ev: React.MouseEvent) => {
|
private onFinished = (ev: React.MouseEvent): void => {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
this.props.onFinished?.();
|
this.props.onFinished?.();
|
||||||
};
|
};
|
||||||
|
|
||||||
private onClick = (ev: React.MouseEvent) => {
|
private onClick = (ev: React.MouseEvent): void => {
|
||||||
// Don't allow clicks to escape the context menu wrapper
|
// Don't allow clicks to escape the context menu wrapper
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
|
|
||||||
@ -208,7 +210,7 @@ export default class ContextMenu extends React.PureComponent<IProps, IState> {
|
|||||||
|
|
||||||
// We now only handle closing the ContextMenu in this keyDown handler.
|
// We now only handle closing the ContextMenu in this keyDown handler.
|
||||||
// All of the item/option navigation is delegated to RovingTabIndex.
|
// All of the item/option navigation is delegated to RovingTabIndex.
|
||||||
private onKeyDown = (ev: React.KeyboardEvent) => {
|
private onKeyDown = (ev: React.KeyboardEvent): void => {
|
||||||
ev.stopPropagation(); // prevent keyboard propagating out of the context menu, we're focus-locked
|
ev.stopPropagation(); // prevent keyboard propagating out of the context menu, we're focus-locked
|
||||||
|
|
||||||
const action = getKeyBindingsManager().getAccessibilityAction(ev);
|
const action = getKeyBindingsManager().getAccessibilityAction(ev);
|
||||||
@ -243,7 +245,7 @@ export default class ContextMenu extends React.PureComponent<IProps, IState> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
protected renderMenu(hasBackground = this.props.hasBackground) {
|
protected renderMenu(hasBackground = this.props.hasBackground): JSX.Element {
|
||||||
const position: Partial<Writeable<DOMRect>> = {};
|
const position: Partial<Writeable<DOMRect>> = {};
|
||||||
const {
|
const {
|
||||||
top,
|
top,
|
||||||
@ -501,17 +503,13 @@ export const toLeftOrRightOf = (elementRect: DOMRect, chevronOffset = 12): ToRig
|
|||||||
return toRightOf(elementRect, chevronOffset);
|
return toRightOf(elementRect, chevronOffset);
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AboveLeftOf = IPosition & {
|
|
||||||
chevronFace: ChevronFace;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Placement method for <ContextMenu /> to position context menu right-aligned and flowing to the left of elementRect,
|
// Placement method for <ContextMenu /> to position context menu right-aligned and flowing to the left of elementRect,
|
||||||
// and either above or below: wherever there is more space (maybe this should be aboveOrBelowLeftOf?)
|
// and either above or below: wherever there is more space (maybe this should be aboveOrBelowLeftOf?)
|
||||||
export const aboveLeftOf = (
|
export const aboveLeftOf = (
|
||||||
elementRect: Pick<DOMRect, "right" | "top" | "bottom">,
|
elementRect: Pick<DOMRect, "right" | "top" | "bottom">,
|
||||||
chevronFace = ChevronFace.None,
|
chevronFace = ChevronFace.None,
|
||||||
vPadding = 0,
|
vPadding = 0,
|
||||||
): AboveLeftOf => {
|
): MenuProps => {
|
||||||
const menuOptions: IPosition & { chevronFace: ChevronFace } = { chevronFace };
|
const menuOptions: IPosition & { chevronFace: ChevronFace } = { chevronFace };
|
||||||
|
|
||||||
const buttonRight = elementRect.right + window.scrollX;
|
const buttonRight = elementRect.right + window.scrollX;
|
||||||
@ -535,7 +533,7 @@ export const aboveRightOf = (
|
|||||||
elementRect: Pick<DOMRect, "left" | "top" | "bottom">,
|
elementRect: Pick<DOMRect, "left" | "top" | "bottom">,
|
||||||
chevronFace = ChevronFace.None,
|
chevronFace = ChevronFace.None,
|
||||||
vPadding = 0,
|
vPadding = 0,
|
||||||
): AboveLeftOf => {
|
): MenuProps => {
|
||||||
const menuOptions: IPosition & { chevronFace: ChevronFace } = { chevronFace };
|
const menuOptions: IPosition & { chevronFace: ChevronFace } = { chevronFace };
|
||||||
|
|
||||||
const buttonLeft = elementRect.left + window.scrollX;
|
const buttonLeft = elementRect.left + window.scrollX;
|
||||||
@ -555,11 +553,11 @@ export const aboveRightOf = (
|
|||||||
|
|
||||||
// Placement method for <ContextMenu /> to position context menu right-aligned and flowing to the left of elementRect
|
// Placement method for <ContextMenu /> to position context menu right-aligned and flowing to the left of elementRect
|
||||||
// and always above elementRect
|
// and always above elementRect
|
||||||
export const alwaysAboveLeftOf = (
|
export const alwaysMenuProps = (
|
||||||
elementRect: Pick<DOMRect, "right" | "bottom" | "top">,
|
elementRect: Pick<DOMRect, "right" | "bottom" | "top">,
|
||||||
chevronFace = ChevronFace.None,
|
chevronFace = ChevronFace.None,
|
||||||
vPadding = 0,
|
vPadding = 0,
|
||||||
) => {
|
): IPosition & { chevronFace: ChevronFace } => {
|
||||||
const menuOptions: IPosition & { chevronFace: ChevronFace } = { chevronFace };
|
const menuOptions: IPosition & { chevronFace: ChevronFace } = { chevronFace };
|
||||||
|
|
||||||
const buttonRight = elementRect.right + window.scrollX;
|
const buttonRight = elementRect.right + window.scrollX;
|
||||||
@ -578,7 +576,7 @@ export const alwaysAboveRightOf = (
|
|||||||
elementRect: Pick<DOMRect, "left" | "top">,
|
elementRect: Pick<DOMRect, "left" | "top">,
|
||||||
chevronFace = ChevronFace.None,
|
chevronFace = ChevronFace.None,
|
||||||
vPadding = 0,
|
vPadding = 0,
|
||||||
) => {
|
): IPosition & { chevronFace: ChevronFace } => {
|
||||||
const menuOptions: IPosition & { chevronFace: ChevronFace } = { chevronFace };
|
const menuOptions: IPosition & { chevronFace: ChevronFace } = { chevronFace };
|
||||||
|
|
||||||
const buttonLeft = elementRect.left + window.scrollX;
|
const buttonLeft = elementRect.left + window.scrollX;
|
||||||
@ -607,12 +605,12 @@ export const useContextMenu = <T extends any = HTMLElement>(inputRef?: RefObject
|
|||||||
}
|
}
|
||||||
|
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const open = (ev?: SyntheticEvent) => {
|
const open = (ev?: SyntheticEvent): void => {
|
||||||
ev?.preventDefault();
|
ev?.preventDefault();
|
||||||
ev?.stopPropagation();
|
ev?.stopPropagation();
|
||||||
setIsOpen(true);
|
setIsOpen(true);
|
||||||
};
|
};
|
||||||
const close = (ev?: SyntheticEvent) => {
|
const close = (ev?: SyntheticEvent): void => {
|
||||||
ev?.preventDefault();
|
ev?.preventDefault();
|
||||||
ev?.stopPropagation();
|
ev?.stopPropagation();
|
||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
@ -622,8 +620,11 @@ export const useContextMenu = <T extends any = HTMLElement>(inputRef?: RefObject
|
|||||||
};
|
};
|
||||||
|
|
||||||
// XXX: Deprecated, used only for dynamic Tooltips. Avoid using at all costs.
|
// XXX: Deprecated, used only for dynamic Tooltips. Avoid using at all costs.
|
||||||
export function createMenu(ElementClass, props) {
|
export function createMenu(
|
||||||
const onFinished = function (...args) {
|
ElementClass: typeof React.Component,
|
||||||
|
props: Record<string, any>,
|
||||||
|
): { close: (...args: any[]) => void } {
|
||||||
|
const onFinished = function (...args): void {
|
||||||
ReactDOM.unmountComponentAtNode(getOrCreateContainer());
|
ReactDOM.unmountComponentAtNode(getOrCreateContainer());
|
||||||
props?.onFinished?.apply(null, args);
|
props?.onFinished?.apply(null, args);
|
||||||
};
|
};
|
||||||
|
@ -60,7 +60,7 @@ export default class EmbeddedPage extends React.PureComponent<IProps, IState> {
|
|||||||
return sanitizeHtml(_t(s));
|
return sanitizeHtml(_t(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async fetchEmbed() {
|
private async fetchEmbed(): Promise<void> {
|
||||||
let res: Response;
|
let res: Response;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -37,7 +37,7 @@ const FileDropTarget: React.FC<IProps> = ({ parent, onFileDrop }) => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!parent || parent.ondrop) return;
|
if (!parent || parent.ondrop) return;
|
||||||
|
|
||||||
const onDragEnter = (ev: DragEvent) => {
|
const onDragEnter = (ev: DragEvent): void => {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
|
||||||
@ -55,7 +55,7 @@ const FileDropTarget: React.FC<IProps> = ({ parent, onFileDrop }) => {
|
|||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
const onDragLeave = (ev: DragEvent) => {
|
const onDragLeave = (ev: DragEvent): void => {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
|
||||||
@ -65,7 +65,7 @@ const FileDropTarget: React.FC<IProps> = ({ parent, onFileDrop }) => {
|
|||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
const onDragOver = (ev: DragEvent) => {
|
const onDragOver = (ev: DragEvent): void => {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
|
||||||
@ -79,7 +79,7 @@ const FileDropTarget: React.FC<IProps> = ({ parent, onFileDrop }) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onDrop = (ev: DragEvent) => {
|
const onDrop = (ev: DragEvent): void => {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
onFileDrop(ev.dataTransfer);
|
onFileDrop(ev.dataTransfer);
|
||||||
|
@ -223,7 +223,7 @@ class FilePanel extends React.Component<IProps, IState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render(): JSX.Element {
|
||||||
if (MatrixClientPeg.get().isGuest()) {
|
if (MatrixClientPeg.get().isGuest()) {
|
||||||
return (
|
return (
|
||||||
<BaseCard className="mx_FilePanel mx_RoomView_messageListWrapper" onClose={this.props.onClose}>
|
<BaseCard className="mx_FilePanel mx_RoomView_messageListWrapper" onClose={this.props.onClose}>
|
||||||
|
@ -22,7 +22,7 @@ interface IProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default class GenericErrorPage extends React.PureComponent<IProps> {
|
export default class GenericErrorPage extends React.PureComponent<IProps> {
|
||||||
public render() {
|
public render(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<div className="mx_GenericErrorPage">
|
<div className="mx_GenericErrorPage">
|
||||||
<div className="mx_GenericErrorPage_box">
|
<div className="mx_GenericErrorPage_box">
|
||||||
|
@ -33,17 +33,17 @@ import MiniAvatarUploader, { AVATAR_SIZE } from "../views/elements/MiniAvatarUpl
|
|||||||
import PosthogTrackers from "../../PosthogTrackers";
|
import PosthogTrackers from "../../PosthogTrackers";
|
||||||
import EmbeddedPage from "./EmbeddedPage";
|
import EmbeddedPage from "./EmbeddedPage";
|
||||||
|
|
||||||
const onClickSendDm = (ev: ButtonEvent) => {
|
const onClickSendDm = (ev: ButtonEvent): void => {
|
||||||
PosthogTrackers.trackInteraction("WebHomeCreateChatButton", ev);
|
PosthogTrackers.trackInteraction("WebHomeCreateChatButton", ev);
|
||||||
dis.dispatch({ action: "view_create_chat" });
|
dis.dispatch({ action: "view_create_chat" });
|
||||||
};
|
};
|
||||||
|
|
||||||
const onClickExplore = (ev: ButtonEvent) => {
|
const onClickExplore = (ev: ButtonEvent): void => {
|
||||||
PosthogTrackers.trackInteraction("WebHomeExploreRoomsButton", ev);
|
PosthogTrackers.trackInteraction("WebHomeExploreRoomsButton", ev);
|
||||||
dis.fire(Action.ViewRoomDirectory);
|
dis.fire(Action.ViewRoomDirectory);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onClickNewRoom = (ev: ButtonEvent) => {
|
const onClickNewRoom = (ev: ButtonEvent): void => {
|
||||||
PosthogTrackers.trackInteraction("WebHomeCreateRoomButton", ev);
|
PosthogTrackers.trackInteraction("WebHomeCreateRoomButton", ev);
|
||||||
dis.dispatch({ action: "view_create_room" });
|
dis.dispatch({ action: "view_create_room" });
|
||||||
};
|
};
|
||||||
@ -52,12 +52,17 @@ interface IProps {
|
|||||||
justRegistered?: boolean;
|
justRegistered?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getOwnProfile = (userId: string) => ({
|
const getOwnProfile = (
|
||||||
|
userId: string,
|
||||||
|
): {
|
||||||
|
displayName: 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),
|
||||||
});
|
});
|
||||||
|
|
||||||
const UserWelcomeTop = () => {
|
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));
|
||||||
|
@ -28,7 +28,7 @@ interface IProps {
|
|||||||
interface IState {}
|
interface IState {}
|
||||||
|
|
||||||
export default class HostSignupAction extends React.PureComponent<IProps, IState> {
|
export default class HostSignupAction extends React.PureComponent<IProps, IState> {
|
||||||
private openDialog = async () => {
|
private openDialog = async (): Promise<void> => {
|
||||||
this.props.onClick?.();
|
this.props.onClick?.();
|
||||||
await HostSignupStore.instance.setHostSignupActive(true);
|
await HostSignupStore.instance.setHostSignupActive(true);
|
||||||
};
|
};
|
||||||
|
@ -130,7 +130,7 @@ export default class InteractiveAuthComponent extends React.Component<IProps, IS
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount() {
|
public componentDidMount(): void {
|
||||||
this.authLogic
|
this.authLogic
|
||||||
.attemptAuth()
|
.attemptAuth()
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
@ -155,7 +155,7 @@ export default class InteractiveAuthComponent extends React.Component<IProps, IS
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentWillUnmount() {
|
public componentWillUnmount(): void {
|
||||||
this.unmounted = true;
|
this.unmounted = true;
|
||||||
|
|
||||||
if (this.intervalId !== null) {
|
if (this.intervalId !== null) {
|
||||||
@ -249,7 +249,7 @@ export default class InteractiveAuthComponent extends React.Component<IProps, IS
|
|||||||
this.authLogic.setEmailSid(sid);
|
this.authLogic.setEmailSid(sid);
|
||||||
};
|
};
|
||||||
|
|
||||||
public render() {
|
public render(): JSX.Element {
|
||||||
const stage = this.state.authStage;
|
const stage = this.state.authStage;
|
||||||
if (!stage) {
|
if (!stage) {
|
||||||
if (this.state.busy) {
|
if (this.state.busy) {
|
||||||
|
@ -89,7 +89,7 @@ export default class LeftPanel extends React.Component<IProps, IState> {
|
|||||||
return SettingsStore.getValue("feature_breadcrumbs_v2") ? BreadcrumbsMode.Labs : BreadcrumbsMode.Legacy;
|
return SettingsStore.getValue("feature_breadcrumbs_v2") ? BreadcrumbsMode.Labs : BreadcrumbsMode.Legacy;
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount() {
|
public componentDidMount(): void {
|
||||||
UIStore.instance.trackElementDimensions("ListContainer", this.listContainerRef.current);
|
UIStore.instance.trackElementDimensions("ListContainer", this.listContainerRef.current);
|
||||||
UIStore.instance.on("ListContainer", this.refreshStickyHeaders);
|
UIStore.instance.on("ListContainer", this.refreshStickyHeaders);
|
||||||
// Using the passive option to not block the main thread
|
// Using the passive option to not block the main thread
|
||||||
@ -97,7 +97,7 @@ export default class LeftPanel extends React.Component<IProps, IState> {
|
|||||||
this.listContainerRef.current?.addEventListener("scroll", this.onScroll, { passive: true });
|
this.listContainerRef.current?.addEventListener("scroll", this.onScroll, { passive: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentWillUnmount() {
|
public componentWillUnmount(): void {
|
||||||
BreadcrumbsStore.instance.off(UPDATE_EVENT, this.onBreadcrumbsUpdate);
|
BreadcrumbsStore.instance.off(UPDATE_EVENT, this.onBreadcrumbsUpdate);
|
||||||
RoomListStore.instance.off(LISTS_UPDATE_EVENT, this.onBreadcrumbsUpdate);
|
RoomListStore.instance.off(LISTS_UPDATE_EVENT, this.onBreadcrumbsUpdate);
|
||||||
SpaceStore.instance.off(UPDATE_SELECTED_SPACE, this.updateActiveSpace);
|
SpaceStore.instance.off(UPDATE_SELECTED_SPACE, this.updateActiveSpace);
|
||||||
@ -112,25 +112,25 @@ export default class LeftPanel extends React.Component<IProps, IState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateActiveSpace = (activeSpace: SpaceKey) => {
|
private updateActiveSpace = (activeSpace: SpaceKey): void => {
|
||||||
this.setState({ activeSpace });
|
this.setState({ activeSpace });
|
||||||
};
|
};
|
||||||
|
|
||||||
private onDialPad = () => {
|
private onDialPad = (): void => {
|
||||||
dis.fire(Action.OpenDialPad);
|
dis.fire(Action.OpenDialPad);
|
||||||
};
|
};
|
||||||
|
|
||||||
private onExplore = (ev: ButtonEvent) => {
|
private onExplore = (ev: ButtonEvent): void => {
|
||||||
dis.fire(Action.ViewRoomDirectory);
|
dis.fire(Action.ViewRoomDirectory);
|
||||||
PosthogTrackers.trackInteraction("WebLeftPanelExploreRoomsButton", ev);
|
PosthogTrackers.trackInteraction("WebLeftPanelExploreRoomsButton", ev);
|
||||||
};
|
};
|
||||||
|
|
||||||
private refreshStickyHeaders = () => {
|
private refreshStickyHeaders = (): void => {
|
||||||
if (!this.listContainerRef.current) return; // ignore: no headers to sticky
|
if (!this.listContainerRef.current) return; // ignore: no headers to sticky
|
||||||
this.handleStickyHeaders(this.listContainerRef.current);
|
this.handleStickyHeaders(this.listContainerRef.current);
|
||||||
};
|
};
|
||||||
|
|
||||||
private onBreadcrumbsUpdate = () => {
|
private onBreadcrumbsUpdate = (): void => {
|
||||||
const newVal = LeftPanel.breadcrumbsMode;
|
const newVal = LeftPanel.breadcrumbsMode;
|
||||||
if (newVal !== this.state.showBreadcrumbs) {
|
if (newVal !== this.state.showBreadcrumbs) {
|
||||||
this.setState({ showBreadcrumbs: newVal });
|
this.setState({ showBreadcrumbs: newVal });
|
||||||
@ -141,7 +141,7 @@ export default class LeftPanel extends React.Component<IProps, IState> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private handleStickyHeaders(list: HTMLDivElement) {
|
private handleStickyHeaders(list: HTMLDivElement): void {
|
||||||
if (this.isDoingStickyHeaders) return;
|
if (this.isDoingStickyHeaders) return;
|
||||||
this.isDoingStickyHeaders = true;
|
this.isDoingStickyHeaders = true;
|
||||||
window.requestAnimationFrame(() => {
|
window.requestAnimationFrame(() => {
|
||||||
@ -150,7 +150,7 @@ export default class LeftPanel extends React.Component<IProps, IState> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private doStickyHeaders(list: HTMLDivElement) {
|
private doStickyHeaders(list: HTMLDivElement): void {
|
||||||
const topEdge = list.scrollTop;
|
const topEdge = list.scrollTop;
|
||||||
const bottomEdge = list.offsetHeight + list.scrollTop;
|
const bottomEdge = list.offsetHeight + list.scrollTop;
|
||||||
const sublists = list.querySelectorAll<HTMLDivElement>(".mx_RoomSublist:not(.mx_RoomSublist_hidden)");
|
const sublists = list.querySelectorAll<HTMLDivElement>(".mx_RoomSublist:not(.mx_RoomSublist_hidden)");
|
||||||
@ -282,20 +282,20 @@ export default class LeftPanel extends React.Component<IProps, IState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private onScroll = (ev: Event) => {
|
private onScroll = (ev: Event): void => {
|
||||||
const list = ev.target as HTMLDivElement;
|
const list = ev.target as HTMLDivElement;
|
||||||
this.handleStickyHeaders(list);
|
this.handleStickyHeaders(list);
|
||||||
};
|
};
|
||||||
|
|
||||||
private onFocus = (ev: React.FocusEvent) => {
|
private onFocus = (ev: React.FocusEvent): void => {
|
||||||
this.focusedElement = ev.target;
|
this.focusedElement = ev.target;
|
||||||
};
|
};
|
||||||
|
|
||||||
private onBlur = () => {
|
private onBlur = (): void => {
|
||||||
this.focusedElement = null;
|
this.focusedElement = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
private onKeyDown = (ev: React.KeyboardEvent, state?: IRovingTabIndexState) => {
|
private onKeyDown = (ev: React.KeyboardEvent, state?: IRovingTabIndexState): void => {
|
||||||
if (!this.focusedElement) return;
|
if (!this.focusedElement) return;
|
||||||
|
|
||||||
const action = getKeyBindingsManager().getRoomListAction(ev);
|
const action = getKeyBindingsManager().getRoomListAction(ev);
|
||||||
|
@ -142,7 +142,7 @@ export default class LegacyCallEventGrouper extends EventEmitter {
|
|||||||
return [...this.events][0]?.getRoomId();
|
return [...this.events][0]?.getRoomId();
|
||||||
}
|
}
|
||||||
|
|
||||||
private onSilencedCallsChanged = () => {
|
private onSilencedCallsChanged = (): void => {
|
||||||
const newState = LegacyCallHandler.instance.isCallSilenced(this.callId);
|
const newState = LegacyCallHandler.instance.isCallSilenced(this.callId);
|
||||||
this.emit(LegacyCallEventGrouperEvent.SilencedChanged, newState);
|
this.emit(LegacyCallEventGrouperEvent.SilencedChanged, newState);
|
||||||
};
|
};
|
||||||
@ -163,20 +163,20 @@ export default class LegacyCallEventGrouper extends EventEmitter {
|
|||||||
LegacyCallHandler.instance.placeCall(this.roomId, this.isVoice ? CallType.Voice : CallType.Video);
|
LegacyCallHandler.instance.placeCall(this.roomId, this.isVoice ? CallType.Voice : CallType.Video);
|
||||||
};
|
};
|
||||||
|
|
||||||
public toggleSilenced = () => {
|
public toggleSilenced = (): void => {
|
||||||
const silenced = LegacyCallHandler.instance.isCallSilenced(this.callId);
|
const silenced = LegacyCallHandler.instance.isCallSilenced(this.callId);
|
||||||
silenced
|
silenced
|
||||||
? LegacyCallHandler.instance.unSilenceCall(this.callId)
|
? LegacyCallHandler.instance.unSilenceCall(this.callId)
|
||||||
: LegacyCallHandler.instance.silenceCall(this.callId);
|
: LegacyCallHandler.instance.silenceCall(this.callId);
|
||||||
};
|
};
|
||||||
|
|
||||||
private setCallListeners() {
|
private setCallListeners(): void {
|
||||||
if (!this.call) return;
|
if (!this.call) return;
|
||||||
this.call.addListener(CallEvent.State, this.setState);
|
this.call.addListener(CallEvent.State, this.setState);
|
||||||
this.call.addListener(CallEvent.LengthChanged, this.onLengthChanged);
|
this.call.addListener(CallEvent.LengthChanged, this.onLengthChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
private setState = () => {
|
private setState = (): void => {
|
||||||
if (CONNECTING_STATES.includes(this.call?.state)) {
|
if (CONNECTING_STATES.includes(this.call?.state)) {
|
||||||
this.state = CallState.Connecting;
|
this.state = CallState.Connecting;
|
||||||
} else if (SUPPORTED_STATES.includes(this.call?.state)) {
|
} else if (SUPPORTED_STATES.includes(this.call?.state)) {
|
||||||
@ -190,7 +190,7 @@ export default class LegacyCallEventGrouper extends EventEmitter {
|
|||||||
this.emit(LegacyCallEventGrouperEvent.StateChanged, this.state);
|
this.emit(LegacyCallEventGrouperEvent.StateChanged, this.state);
|
||||||
};
|
};
|
||||||
|
|
||||||
private setCall = () => {
|
private setCall = (): void => {
|
||||||
if (this.call) return;
|
if (this.call) return;
|
||||||
|
|
||||||
this.call = LegacyCallHandler.instance.getCallById(this.callId);
|
this.call = LegacyCallHandler.instance.getCallById(this.callId);
|
||||||
@ -198,7 +198,7 @@ export default class LegacyCallEventGrouper extends EventEmitter {
|
|||||||
this.setState();
|
this.setState();
|
||||||
};
|
};
|
||||||
|
|
||||||
public add(event: MatrixEvent) {
|
public add(event: MatrixEvent): void {
|
||||||
if (this.events.has(event)) return; // nothing to do
|
if (this.events.has(event)) return; // nothing to do
|
||||||
this.events.add(event);
|
this.events.add(event);
|
||||||
this.setCall();
|
this.setCall();
|
||||||
|
@ -159,7 +159,7 @@ class LoggedInView extends React.Component<IProps, IState> {
|
|||||||
this.resizeHandler = React.createRef();
|
this.resizeHandler = React.createRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount() {
|
public componentDidMount(): void {
|
||||||
document.addEventListener("keydown", this.onNativeKeyDown, false);
|
document.addEventListener("keydown", this.onNativeKeyDown, false);
|
||||||
LegacyCallHandler.instance.addListener(LegacyCallHandlerEvent.CallState, this.onCallState);
|
LegacyCallHandler.instance.addListener(LegacyCallHandlerEvent.CallState, this.onCallState);
|
||||||
|
|
||||||
@ -191,7 +191,7 @@ class LoggedInView extends React.Component<IProps, IState> {
|
|||||||
this.refreshBackgroundImage();
|
this.refreshBackgroundImage();
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentWillUnmount() {
|
public componentWillUnmount(): void {
|
||||||
document.removeEventListener("keydown", this.onNativeKeyDown, false);
|
document.removeEventListener("keydown", this.onNativeKeyDown, false);
|
||||||
LegacyCallHandler.instance.removeListener(LegacyCallHandlerEvent.CallState, this.onCallState);
|
LegacyCallHandler.instance.removeListener(LegacyCallHandlerEvent.CallState, this.onCallState);
|
||||||
this._matrixClient.removeListener(ClientEvent.AccountData, this.onAccountData);
|
this._matrixClient.removeListener(ClientEvent.AccountData, this.onAccountData);
|
||||||
@ -221,14 +221,14 @@ class LoggedInView extends React.Component<IProps, IState> {
|
|||||||
this.setState({ backgroundImage });
|
this.setState({ backgroundImage });
|
||||||
};
|
};
|
||||||
|
|
||||||
public canResetTimelineInRoom = (roomId: string) => {
|
public canResetTimelineInRoom = (roomId: string): boolean => {
|
||||||
if (!this._roomView.current) {
|
if (!this._roomView.current) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return this._roomView.current.canResetTimeline();
|
return this._roomView.current.canResetTimeline();
|
||||||
};
|
};
|
||||||
|
|
||||||
private createResizer() {
|
private createResizer(): Resizer {
|
||||||
let panelSize;
|
let panelSize;
|
||||||
let panelCollapsed;
|
let panelCollapsed;
|
||||||
const collapseConfig: ICollapseConfig = {
|
const collapseConfig: ICollapseConfig = {
|
||||||
@ -268,7 +268,7 @@ class LoggedInView extends React.Component<IProps, IState> {
|
|||||||
return resizer;
|
return resizer;
|
||||||
}
|
}
|
||||||
|
|
||||||
private loadResizerPreferences() {
|
private loadResizerPreferences(): void {
|
||||||
let lhsSize = parseInt(window.localStorage.getItem("mx_lhs_size"), 10);
|
let lhsSize = parseInt(window.localStorage.getItem("mx_lhs_size"), 10);
|
||||||
if (isNaN(lhsSize)) {
|
if (isNaN(lhsSize)) {
|
||||||
lhsSize = 350;
|
lhsSize = 350;
|
||||||
@ -276,13 +276,13 @@ class LoggedInView extends React.Component<IProps, IState> {
|
|||||||
this.resizer.forHandleWithId("lp-resizer").resize(lhsSize);
|
this.resizer.forHandleWithId("lp-resizer").resize(lhsSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
private onAccountData = (event: MatrixEvent) => {
|
private onAccountData = (event: MatrixEvent): void => {
|
||||||
if (event.getType() === "m.ignored_user_list") {
|
if (event.getType() === "m.ignored_user_list") {
|
||||||
dis.dispatch({ action: "ignore_state_changed" });
|
dis.dispatch({ action: "ignore_state_changed" });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private onCompactLayoutChanged = () => {
|
private onCompactLayoutChanged = (): void => {
|
||||||
this.setState({
|
this.setState({
|
||||||
useCompactLayout: SettingsStore.getValue("useCompactLayout"),
|
useCompactLayout: SettingsStore.getValue("useCompactLayout"),
|
||||||
});
|
});
|
||||||
@ -311,13 +311,13 @@ class LoggedInView extends React.Component<IProps, IState> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private onUsageLimitDismissed = () => {
|
private onUsageLimitDismissed = (): void => {
|
||||||
this.setState({
|
this.setState({
|
||||||
usageLimitDismissed: true,
|
usageLimitDismissed: true,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
private calculateServerLimitToast(syncError: IState["syncErrorData"], usageLimitEventContent?: IUsageLimit) {
|
private calculateServerLimitToast(syncError: IState["syncErrorData"], usageLimitEventContent?: IUsageLimit): void {
|
||||||
const error = (syncError?.error as MatrixError)?.errcode === "M_RESOURCE_LIMIT_EXCEEDED";
|
const error = (syncError?.error as MatrixError)?.errcode === "M_RESOURCE_LIMIT_EXCEEDED";
|
||||||
if (error) {
|
if (error) {
|
||||||
usageLimitEventContent = (syncError?.error as MatrixError).data as IUsageLimit;
|
usageLimitEventContent = (syncError?.error as MatrixError).data as IUsageLimit;
|
||||||
@ -337,9 +337,9 @@ class LoggedInView extends React.Component<IProps, IState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateServerNoticeEvents = async () => {
|
private updateServerNoticeEvents = async (): Promise<void> => {
|
||||||
const serverNoticeList = RoomListStore.instance.orderedLists[DefaultTagID.ServerNotice];
|
const serverNoticeList = RoomListStore.instance.orderedLists[DefaultTagID.ServerNotice];
|
||||||
if (!serverNoticeList) return [];
|
if (!serverNoticeList) return;
|
||||||
|
|
||||||
const events = [];
|
const events = [];
|
||||||
let pinnedEventTs = 0;
|
let pinnedEventTs = 0;
|
||||||
@ -379,7 +379,7 @@ class LoggedInView extends React.Component<IProps, IState> {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
private onPaste = (ev: ClipboardEvent) => {
|
private onPaste = (ev: ClipboardEvent): void => {
|
||||||
const element = ev.target as HTMLElement;
|
const element = ev.target as HTMLElement;
|
||||||
const inputableElement = getInputableElement(element);
|
const inputableElement = getInputableElement(element);
|
||||||
if (inputableElement === document.activeElement) return; // nothing to do
|
if (inputableElement === document.activeElement) return; // nothing to do
|
||||||
@ -422,13 +422,13 @@ class LoggedInView extends React.Component<IProps, IState> {
|
|||||||
We also listen with a native listener on the document to get keydown events when no element is focused.
|
We also listen with a native listener on the document to get keydown events when no element is focused.
|
||||||
Bubbling is irrelevant here as the target is the body element.
|
Bubbling is irrelevant here as the target is the body element.
|
||||||
*/
|
*/
|
||||||
private onReactKeyDown = (ev) => {
|
private onReactKeyDown = (ev): void => {
|
||||||
// events caught while bubbling up on the root element
|
// events caught while bubbling up on the root element
|
||||||
// of this component, so something must be focused.
|
// of this component, so something must be focused.
|
||||||
this.onKeyDown(ev);
|
this.onKeyDown(ev);
|
||||||
};
|
};
|
||||||
|
|
||||||
private onNativeKeyDown = (ev) => {
|
private onNativeKeyDown = (ev): void => {
|
||||||
// only pass this if there is no focused element.
|
// only pass this if there is no focused element.
|
||||||
// if there is, onKeyDown will be called by the
|
// if there is, onKeyDown will be called by the
|
||||||
// react keydown handler that respects the react bubbling order.
|
// react keydown handler that respects the react bubbling order.
|
||||||
@ -437,7 +437,7 @@ class LoggedInView extends React.Component<IProps, IState> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private onKeyDown = (ev) => {
|
private onKeyDown = (ev): void => {
|
||||||
let handled = false;
|
let handled = false;
|
||||||
|
|
||||||
const roomAction = getKeyBindingsManager().getRoomAction(ev);
|
const roomAction = getKeyBindingsManager().getRoomAction(ev);
|
||||||
@ -615,13 +615,13 @@ class LoggedInView extends React.Component<IProps, IState> {
|
|||||||
* dispatch a page-up/page-down/etc to the appropriate component
|
* dispatch a page-up/page-down/etc to the appropriate component
|
||||||
* @param {Object} ev The key event
|
* @param {Object} ev The key event
|
||||||
*/
|
*/
|
||||||
private onScrollKeyPressed = (ev) => {
|
private onScrollKeyPressed = (ev): void => {
|
||||||
if (this._roomView.current) {
|
if (this._roomView.current) {
|
||||||
this._roomView.current.handleScrollKey(ev);
|
this._roomView.current.handleScrollKey(ev);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public render() {
|
public render(): JSX.Element {
|
||||||
let pageElement;
|
let pageElement;
|
||||||
|
|
||||||
switch (this.props.page_type) {
|
switch (this.props.page_type) {
|
||||||
|
@ -216,7 +216,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
realQueryParams: {},
|
realQueryParams: {},
|
||||||
startingFragmentQueryParams: {},
|
startingFragmentQueryParams: {},
|
||||||
config: {},
|
config: {},
|
||||||
onTokenLoginCompleted: () => {},
|
onTokenLoginCompleted: (): void => {},
|
||||||
};
|
};
|
||||||
|
|
||||||
private firstSyncComplete = false;
|
private firstSyncComplete = false;
|
||||||
@ -317,7 +317,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
this.props.realQueryParams,
|
this.props.realQueryParams,
|
||||||
this.props.defaultDeviceDisplayName,
|
this.props.defaultDeviceDisplayName,
|
||||||
this.getFragmentAfterLogin(),
|
this.getFragmentAfterLogin(),
|
||||||
).then(async (loggedIn) => {
|
).then(async (loggedIn): Promise<boolean | void> => {
|
||||||
if (this.props.realQueryParams?.loginToken) {
|
if (this.props.realQueryParams?.loginToken) {
|
||||||
// remove the loginToken from the URL regardless
|
// remove the loginToken from the URL regardless
|
||||||
this.props.onTokenLoginCompleted();
|
this.props.onTokenLoginCompleted();
|
||||||
@ -353,7 +353,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
initSentry(SdkConfig.get("sentry"));
|
initSentry(SdkConfig.get("sentry"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async postLoginSetup() {
|
private async postLoginSetup(): Promise<void> {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
const cryptoEnabled = cli.isCryptoEnabled();
|
const cryptoEnabled = cli.isCryptoEnabled();
|
||||||
if (!cryptoEnabled) {
|
if (!cryptoEnabled) {
|
||||||
@ -367,7 +367,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
// as a proxy to figure out if it's worth prompting the user to verify
|
// as a proxy to figure out if it's worth prompting the user to verify
|
||||||
// from another device.
|
// from another device.
|
||||||
promisesList.push(
|
promisesList.push(
|
||||||
(async () => {
|
(async (): Promise<void> => {
|
||||||
crossSigningIsSetUp = await cli.userHasCrossSigningKeys();
|
crossSigningIsSetUp = await cli.userHasCrossSigningKeys();
|
||||||
})(),
|
})(),
|
||||||
);
|
);
|
||||||
@ -417,7 +417,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
window.addEventListener("resize", this.onWindowResized);
|
window.addEventListener("resize", this.onWindowResized);
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidUpdate(prevProps, prevState) {
|
public componentDidUpdate(prevProps, prevState): void {
|
||||||
if (this.shouldTrackPageChange(prevState, this.state)) {
|
if (this.shouldTrackPageChange(prevState, this.state)) {
|
||||||
const durationMs = this.stopPageChangeTimer();
|
const durationMs = this.stopPageChangeTimer();
|
||||||
PosthogTrackers.instance.trackPageChange(this.state.view, this.state.page_type, durationMs);
|
PosthogTrackers.instance.trackPageChange(this.state.view, this.state.page_type, durationMs);
|
||||||
@ -428,7 +428,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentWillUnmount() {
|
public componentWillUnmount(): void {
|
||||||
Lifecycle.stopMatrixClient();
|
Lifecycle.stopMatrixClient();
|
||||||
dis.unregister(this.dispatcherRef);
|
dis.unregister(this.dispatcherRef);
|
||||||
this.themeWatcher.stop();
|
this.themeWatcher.stop();
|
||||||
@ -477,7 +477,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private getServerProperties() {
|
private getServerProperties(): { serverConfig: ValidatedServerConfig } {
|
||||||
let props = this.state.serverConfig;
|
let props = this.state.serverConfig;
|
||||||
if (!props) props = this.props.serverConfig; // for unit tests
|
if (!props) props = this.props.serverConfig; // for unit tests
|
||||||
if (!props) props = SdkConfig.get("validated_server_config");
|
if (!props) props = SdkConfig.get("validated_server_config");
|
||||||
@ -513,11 +513,11 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
// to try logging out.
|
// to try logging out.
|
||||||
}
|
}
|
||||||
|
|
||||||
private startPageChangeTimer() {
|
private startPageChangeTimer(): void {
|
||||||
PerformanceMonitor.instance.start(PerformanceEntryNames.PAGE_CHANGE);
|
PerformanceMonitor.instance.start(PerformanceEntryNames.PAGE_CHANGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private stopPageChangeTimer() {
|
private stopPageChangeTimer(): number | null {
|
||||||
const perfMonitor = PerformanceMonitor.instance;
|
const perfMonitor = PerformanceMonitor.instance;
|
||||||
|
|
||||||
perfMonitor.stop(PerformanceEntryNames.PAGE_CHANGE);
|
perfMonitor.stop(PerformanceEntryNames.PAGE_CHANGE);
|
||||||
@ -876,13 +876,13 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private setPage(pageType: PageType) {
|
private setPage(pageType: PageType): void {
|
||||||
this.setState({
|
this.setState({
|
||||||
page_type: pageType,
|
page_type: pageType,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async startRegistration(params: { [key: string]: string }) {
|
private async startRegistration(params: { [key: string]: string }): Promise<void> {
|
||||||
const newState: Partial<IState> = {
|
const newState: Partial<IState> = {
|
||||||
view: Views.REGISTER,
|
view: Views.REGISTER,
|
||||||
};
|
};
|
||||||
@ -916,7 +916,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// switch view to the given room
|
// switch view to the given room
|
||||||
private async viewRoom(roomInfo: ViewRoomPayload) {
|
private async viewRoom(roomInfo: ViewRoomPayload): Promise<void> {
|
||||||
this.focusComposer = true;
|
this.focusComposer = true;
|
||||||
|
|
||||||
if (roomInfo.room_alias) {
|
if (roomInfo.room_alias) {
|
||||||
@ -992,7 +992,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private viewSomethingBehindModal() {
|
private viewSomethingBehindModal(): void {
|
||||||
if (this.state.view !== Views.LOGGED_IN) {
|
if (this.state.view !== Views.LOGGED_IN) {
|
||||||
this.viewWelcome();
|
this.viewWelcome();
|
||||||
return;
|
return;
|
||||||
@ -1002,7 +1002,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private viewWelcome() {
|
private viewWelcome(): void {
|
||||||
if (shouldUseLoginForWelcome(SdkConfig.get())) {
|
if (shouldUseLoginForWelcome(SdkConfig.get())) {
|
||||||
return this.viewLogin();
|
return this.viewLogin();
|
||||||
}
|
}
|
||||||
@ -1014,7 +1014,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
this.themeWatcher.recheck();
|
this.themeWatcher.recheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
private viewLogin(otherState?: any) {
|
private viewLogin(otherState?: any): void {
|
||||||
this.setStateForNewView({
|
this.setStateForNewView({
|
||||||
view: Views.LOGIN,
|
view: Views.LOGIN,
|
||||||
...otherState,
|
...otherState,
|
||||||
@ -1024,7 +1024,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
this.themeWatcher.recheck();
|
this.themeWatcher.recheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
private viewHome(justRegistered = false) {
|
private viewHome(justRegistered = false): void {
|
||||||
// The home page requires the "logged in" view, so we'll set that.
|
// The home page requires the "logged in" view, so we'll set that.
|
||||||
this.setStateForNewView({
|
this.setStateForNewView({
|
||||||
view: Views.LOGGED_IN,
|
view: Views.LOGGED_IN,
|
||||||
@ -1037,7 +1037,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
this.themeWatcher.recheck();
|
this.themeWatcher.recheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
private viewUser(userId: string, subAction: string) {
|
private viewUser(userId: string, subAction: string): void {
|
||||||
// Wait for the first sync so that `getRoom` gives us a room object if it's
|
// Wait for the first sync so that `getRoom` gives us a room object if it's
|
||||||
// in the sync response
|
// in the sync response
|
||||||
const waitForSync = this.firstSyncPromise ? this.firstSyncPromise.promise : Promise.resolve();
|
const waitForSync = this.firstSyncPromise ? this.firstSyncPromise.promise : Promise.resolve();
|
||||||
@ -1052,7 +1052,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async createRoom(defaultPublic = false, defaultName?: string, type?: RoomType) {
|
private async createRoom(defaultPublic = false, defaultName?: string, type?: RoomType): Promise<void> {
|
||||||
const modal = Modal.createDialog(CreateRoomDialog, {
|
const modal = Modal.createDialog(CreateRoomDialog, {
|
||||||
type,
|
type,
|
||||||
defaultPublic,
|
defaultPublic,
|
||||||
@ -1065,7 +1065,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private chatCreateOrReuse(userId: string) {
|
private chatCreateOrReuse(userId: string): void {
|
||||||
const snakedConfig = new SnakedObject<IConfigOptions>(this.props.config);
|
const snakedConfig = new SnakedObject<IConfigOptions>(this.props.config);
|
||||||
// Use a deferred action to reshow the dialog once the user has registered
|
// Use a deferred action to reshow the dialog once the user has registered
|
||||||
if (MatrixClientPeg.get().isGuest()) {
|
if (MatrixClientPeg.get().isGuest()) {
|
||||||
@ -1115,11 +1115,11 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private leaveRoomWarnings(roomId: string) {
|
private leaveRoomWarnings(roomId: string): JSX.Element[] {
|
||||||
const roomToLeave = MatrixClientPeg.get().getRoom(roomId);
|
const roomToLeave = MatrixClientPeg.get().getRoom(roomId);
|
||||||
const isSpace = roomToLeave?.isSpaceRoom();
|
const isSpace = roomToLeave?.isSpaceRoom();
|
||||||
// Show a warning if there are additional complications.
|
// Show a warning if there are additional complications.
|
||||||
const warnings = [];
|
const warnings: JSX.Element[] = [];
|
||||||
|
|
||||||
const memberCount = roomToLeave.currentState.getJoinedMemberCount();
|
const memberCount = roomToLeave.currentState.getJoinedMemberCount();
|
||||||
if (memberCount === 1) {
|
if (memberCount === 1) {
|
||||||
@ -1153,7 +1153,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
return warnings;
|
return warnings;
|
||||||
}
|
}
|
||||||
|
|
||||||
private leaveRoom(roomId: string) {
|
private leaveRoom(roomId: string): void {
|
||||||
const roomToLeave = MatrixClientPeg.get().getRoom(roomId);
|
const roomToLeave = MatrixClientPeg.get().getRoom(roomId);
|
||||||
const warnings = this.leaveRoomWarnings(roomId);
|
const warnings = this.leaveRoomWarnings(roomId);
|
||||||
|
|
||||||
@ -1184,7 +1184,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private forgetRoom(roomId: string) {
|
private forgetRoom(roomId: string): void {
|
||||||
const room = MatrixClientPeg.get().getRoom(roomId);
|
const room = MatrixClientPeg.get().getRoom(roomId);
|
||||||
MatrixClientPeg.get()
|
MatrixClientPeg.get()
|
||||||
.forget(roomId)
|
.forget(roomId)
|
||||||
@ -1208,7 +1208,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async copyRoom(roomId: string) {
|
private async copyRoom(roomId: string): Promise<void> {
|
||||||
const roomLink = makeRoomPermalink(roomId);
|
const roomLink = makeRoomPermalink(roomId);
|
||||||
const success = await copyPlaintext(roomLink);
|
const success = await copyPlaintext(roomLink);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
@ -1223,13 +1223,13 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
* Starts a chat with the welcome user, if the user doesn't already have one
|
* Starts a chat with the welcome user, if the user doesn't already have one
|
||||||
* @returns {string} The room ID of the new room, or null if no room was created
|
* @returns {string} The room ID of the new room, or null if no room was created
|
||||||
*/
|
*/
|
||||||
private async startWelcomeUserChat() {
|
private async startWelcomeUserChat(): Promise<string | null> {
|
||||||
// We can end up with multiple tabs post-registration where the user
|
// We can end up with multiple tabs post-registration where the user
|
||||||
// might then end up with a session and we don't want them all making
|
// might then end up with a session and we don't want them all making
|
||||||
// a chat with the welcome user: try to de-dupe.
|
// a chat with the welcome user: try to de-dupe.
|
||||||
// We need to wait for the first sync to complete for this to
|
// We need to wait for the first sync to complete for this to
|
||||||
// work though.
|
// work though.
|
||||||
let waitFor;
|
let waitFor: Promise<void>;
|
||||||
if (!this.firstSyncComplete) {
|
if (!this.firstSyncComplete) {
|
||||||
waitFor = this.firstSyncPromise.promise;
|
waitFor = this.firstSyncPromise.promise;
|
||||||
} else {
|
} else {
|
||||||
@ -1254,7 +1254,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
// run without the update to m.direct, making another welcome
|
// run without the update to m.direct, making another welcome
|
||||||
// user room (it doesn't wait for new data from the server, just
|
// user room (it doesn't wait for new data from the server, just
|
||||||
// the saved sync to be loaded).
|
// the saved sync to be loaded).
|
||||||
const saveWelcomeUser = (ev: MatrixEvent) => {
|
const saveWelcomeUser = (ev: MatrixEvent): void => {
|
||||||
if (ev.getType() === EventType.Direct && ev.getContent()[snakedConfig.get("welcome_user_id")]) {
|
if (ev.getType() === EventType.Direct && ev.getContent()[snakedConfig.get("welcome_user_id")]) {
|
||||||
MatrixClientPeg.get().store.save(true);
|
MatrixClientPeg.get().store.save(true);
|
||||||
MatrixClientPeg.get().removeListener(ClientEvent.AccountData, saveWelcomeUser);
|
MatrixClientPeg.get().removeListener(ClientEvent.AccountData, saveWelcomeUser);
|
||||||
@ -1270,7 +1270,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
/**
|
/**
|
||||||
* Called when a new logged in session has started
|
* Called when a new logged in session has started
|
||||||
*/
|
*/
|
||||||
private async onLoggedIn() {
|
private async onLoggedIn(): Promise<void> {
|
||||||
ThemeController.isLogin = false;
|
ThemeController.isLogin = false;
|
||||||
this.themeWatcher.recheck();
|
this.themeWatcher.recheck();
|
||||||
StorageManager.tryPersistStorage();
|
StorageManager.tryPersistStorage();
|
||||||
@ -1301,7 +1301,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async onShowPostLoginScreen(useCase?: UseCase) {
|
private async onShowPostLoginScreen(useCase?: UseCase): Promise<void> {
|
||||||
if (useCase) {
|
if (useCase) {
|
||||||
PosthogAnalytics.instance.setProperty("ftueUseCaseSelection", useCase);
|
PosthogAnalytics.instance.setProperty("ftueUseCaseSelection", useCase);
|
||||||
SettingsStore.setValue("FTUE.useCaseSelection", null, SettingLevel.ACCOUNT, useCase);
|
SettingsStore.setValue("FTUE.useCaseSelection", null, SettingLevel.ACCOUNT, useCase);
|
||||||
@ -1370,7 +1370,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private initPosthogAnalyticsToast() {
|
private initPosthogAnalyticsToast(): void {
|
||||||
// Show the analytics toast if necessary
|
// Show the analytics toast if necessary
|
||||||
if (SettingsStore.getValue("pseudonymousAnalyticsOptIn") === null) {
|
if (SettingsStore.getValue("pseudonymousAnalyticsOptIn") === null) {
|
||||||
showAnalyticsToast();
|
showAnalyticsToast();
|
||||||
@ -1397,7 +1397,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private showScreenAfterLogin() {
|
private showScreenAfterLogin(): void {
|
||||||
// If screenAfterLogin is set, use that, then null it so that a second login will
|
// If screenAfterLogin is set, use that, then null it so that a second login will
|
||||||
// result in view_home_page, _user_settings or _room_directory
|
// result in view_home_page, _user_settings or _room_directory
|
||||||
if (this.screenAfterLogin && this.screenAfterLogin.screen) {
|
if (this.screenAfterLogin && this.screenAfterLogin.screen) {
|
||||||
@ -1415,7 +1415,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private viewLastRoom() {
|
private viewLastRoom(): void {
|
||||||
dis.dispatch<ViewRoomPayload>({
|
dis.dispatch<ViewRoomPayload>({
|
||||||
action: Action.ViewRoom,
|
action: Action.ViewRoom,
|
||||||
room_id: localStorage.getItem("mx_last_room_id"),
|
room_id: localStorage.getItem("mx_last_room_id"),
|
||||||
@ -1426,7 +1426,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
/**
|
/**
|
||||||
* Called when the session is logged out
|
* Called when the session is logged out
|
||||||
*/
|
*/
|
||||||
private onLoggedOut() {
|
private onLoggedOut(): void {
|
||||||
this.viewLogin({
|
this.viewLogin({
|
||||||
ready: false,
|
ready: false,
|
||||||
collapseLhs: false,
|
collapseLhs: false,
|
||||||
@ -1439,7 +1439,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
/**
|
/**
|
||||||
* Called when the session is softly logged out
|
* Called when the session is softly logged out
|
||||||
*/
|
*/
|
||||||
private onSoftLogout() {
|
private onSoftLogout(): void {
|
||||||
this.notifyNewScreen("soft_logout");
|
this.notifyNewScreen("soft_logout");
|
||||||
this.setStateForNewView({
|
this.setStateForNewView({
|
||||||
view: Views.SOFT_LOGOUT,
|
view: Views.SOFT_LOGOUT,
|
||||||
@ -1455,7 +1455,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
* Called just before the matrix client is started
|
* Called just before the matrix client is started
|
||||||
* (useful for setting listeners)
|
* (useful for setting listeners)
|
||||||
*/
|
*/
|
||||||
private onWillStartClient() {
|
private onWillStartClient(): void {
|
||||||
// reset the 'have completed first sync' flag,
|
// reset the 'have completed first sync' flag,
|
||||||
// since we're about to start the client and therefore about
|
// since we're about to start the client and therefore about
|
||||||
// to do the first sync
|
// to do the first sync
|
||||||
@ -1610,7 +1610,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
cli.on(CryptoEvent.KeyBackupFailed, async (errcode) => {
|
cli.on(CryptoEvent.KeyBackupFailed, async (errcode): Promise<void> => {
|
||||||
let haveNewVersion;
|
let haveNewVersion;
|
||||||
let newVersionInfo;
|
let newVersionInfo;
|
||||||
// if key backup is still enabled, there must be a new backup in place
|
// if key backup is still enabled, there must be a new backup in place
|
||||||
@ -1678,7 +1678,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
* setting up anything that requires the client to be started.
|
* setting up anything that requires the client to be started.
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
private onClientStarted() {
|
private onClientStarted(): void {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
|
|
||||||
if (cli.isCryptoEnabled()) {
|
if (cli.isCryptoEnabled()) {
|
||||||
@ -1700,7 +1700,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public showScreen(screen: string, params?: { [key: string]: any }) {
|
public showScreen(screen: string, params?: { [key: string]: any }): void {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
const isLoggedOutOrGuest = !cli || cli.isGuest();
|
const isLoggedOutOrGuest = !cli || cli.isGuest();
|
||||||
if (!isLoggedOutOrGuest && AUTH_SCREENS.includes(screen)) {
|
if (!isLoggedOutOrGuest && AUTH_SCREENS.includes(screen)) {
|
||||||
@ -1861,14 +1861,14 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private notifyNewScreen(screen: string, replaceLast = false) {
|
private notifyNewScreen(screen: string, replaceLast = false): void {
|
||||||
if (this.props.onNewScreen) {
|
if (this.props.onNewScreen) {
|
||||||
this.props.onNewScreen(screen, replaceLast);
|
this.props.onNewScreen(screen, replaceLast);
|
||||||
}
|
}
|
||||||
this.setPageSubtitle();
|
this.setPageSubtitle();
|
||||||
}
|
}
|
||||||
|
|
||||||
private onLogoutClick(event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) {
|
private onLogoutClick(event: React.MouseEvent<HTMLAnchorElement, MouseEvent>): void {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: "logout",
|
action: "logout",
|
||||||
});
|
});
|
||||||
@ -1876,7 +1876,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleResize = () => {
|
private handleResize = (): void => {
|
||||||
const LHS_THRESHOLD = 1000;
|
const LHS_THRESHOLD = 1000;
|
||||||
const width = UIStore.instance.windowWidth;
|
const width = UIStore.instance.windowWidth;
|
||||||
|
|
||||||
@ -1892,19 +1892,19 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
this.state.resizeNotifier.notifyWindowResized();
|
this.state.resizeNotifier.notifyWindowResized();
|
||||||
};
|
};
|
||||||
|
|
||||||
private dispatchTimelineResize() {
|
private dispatchTimelineResize(): void {
|
||||||
dis.dispatch({ action: "timeline_resize" });
|
dis.dispatch({ action: "timeline_resize" });
|
||||||
}
|
}
|
||||||
|
|
||||||
private onRegisterClick = () => {
|
private onRegisterClick = (): void => {
|
||||||
this.showScreen("register");
|
this.showScreen("register");
|
||||||
};
|
};
|
||||||
|
|
||||||
private onLoginClick = () => {
|
private onLoginClick = (): void => {
|
||||||
this.showScreen("login");
|
this.showScreen("login");
|
||||||
};
|
};
|
||||||
|
|
||||||
private onForgotPasswordClick = () => {
|
private onForgotPasswordClick = (): void => {
|
||||||
this.showScreen("forgot_password");
|
this.showScreen("forgot_password");
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1926,7 +1926,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private setPageSubtitle(subtitle = "") {
|
private setPageSubtitle(subtitle = ""): void {
|
||||||
if (this.state.currentRoomId) {
|
if (this.state.currentRoomId) {
|
||||||
const client = MatrixClientPeg.get();
|
const client = MatrixClientPeg.get();
|
||||||
const room = client && client.getRoom(this.state.currentRoomId);
|
const room = client && client.getRoom(this.state.currentRoomId);
|
||||||
@ -1963,11 +1963,11 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
this.setPageSubtitle();
|
this.setPageSubtitle();
|
||||||
};
|
};
|
||||||
|
|
||||||
private onServerConfigChange = (serverConfig: ValidatedServerConfig) => {
|
private onServerConfigChange = (serverConfig: ValidatedServerConfig): void => {
|
||||||
this.setState({ serverConfig });
|
this.setState({ serverConfig });
|
||||||
};
|
};
|
||||||
|
|
||||||
private makeRegistrationUrl = (params: QueryDict) => {
|
private makeRegistrationUrl = (params: QueryDict): string => {
|
||||||
if (this.props.startingFragmentQueryParams.referrer) {
|
if (this.props.startingFragmentQueryParams.referrer) {
|
||||||
params.referrer = this.props.startingFragmentQueryParams.referrer;
|
params.referrer = this.props.startingFragmentQueryParams.referrer;
|
||||||
}
|
}
|
||||||
@ -2016,7 +2016,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
return fragmentAfterLogin;
|
return fragmentAfterLogin;
|
||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render(): JSX.Element {
|
||||||
const fragmentAfterLogin = this.getFragmentAfterLogin();
|
const fragmentAfterLogin = this.getFragmentAfterLogin();
|
||||||
let view = null;
|
let view = null;
|
||||||
|
|
||||||
@ -2132,7 +2132,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else if (this.state.view === Views.USE_CASE_SELECTION) {
|
} else if (this.state.view === Views.USE_CASE_SELECTION) {
|
||||||
view = <UseCaseSelection onFinished={(useCase) => this.onShowPostLoginScreen(useCase)} />;
|
view = <UseCaseSelection onFinished={(useCase): Promise<void> => this.onShowPostLoginScreen(useCase)} />;
|
||||||
} else {
|
} else {
|
||||||
logger.error(`Unknown view ${this.state.view}`);
|
logger.error(`Unknown view ${this.state.view}`);
|
||||||
}
|
}
|
||||||
|
@ -296,19 +296,19 @@ export default class MessagePanel extends React.Component<IProps, IState> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount() {
|
public componentDidMount(): void {
|
||||||
this.calculateRoomMembersCount();
|
this.calculateRoomMembersCount();
|
||||||
this.props.room?.currentState.on(RoomStateEvent.Update, this.calculateRoomMembersCount);
|
this.props.room?.currentState.on(RoomStateEvent.Update, this.calculateRoomMembersCount);
|
||||||
this.isMounted = true;
|
this.isMounted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentWillUnmount() {
|
public componentWillUnmount(): void {
|
||||||
this.isMounted = false;
|
this.isMounted = false;
|
||||||
this.props.room?.currentState.off(RoomStateEvent.Update, this.calculateRoomMembersCount);
|
this.props.room?.currentState.off(RoomStateEvent.Update, this.calculateRoomMembersCount);
|
||||||
SettingsStore.unwatchSetting(this.showTypingNotificationsWatcherRef);
|
SettingsStore.unwatchSetting(this.showTypingNotificationsWatcherRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidUpdate(prevProps, prevState) {
|
public componentDidUpdate(prevProps, prevState): void {
|
||||||
if (prevProps.layout !== this.props.layout) {
|
if (prevProps.layout !== this.props.layout) {
|
||||||
this.calculateRoomMembersCount();
|
this.calculateRoomMembersCount();
|
||||||
}
|
}
|
||||||
@ -752,7 +752,7 @@ export default class MessagePanel extends React.Component<IProps, IState> {
|
|||||||
const readReceipts = this.readReceiptsByEvent[eventId];
|
const readReceipts = this.readReceiptsByEvent[eventId];
|
||||||
|
|
||||||
let isLastSuccessful = false;
|
let isLastSuccessful = false;
|
||||||
const isSentState = (s) => !s || s === "sent";
|
const isSentState = (s): boolean => !s || s === "sent";
|
||||||
const isSent = isSentState(mxEv.getAssociatedStatus());
|
const isSent = isSentState(mxEv.getAssociatedStatus());
|
||||||
const hasNextEvent = nextEvent && this.shouldShowEvent(nextEvent);
|
const hasNextEvent = nextEvent && this.shouldShowEvent(nextEvent);
|
||||||
if (!hasNextEvent && isSent) {
|
if (!hasNextEvent && isSent) {
|
||||||
@ -982,7 +982,7 @@ export default class MessagePanel extends React.Component<IProps, IState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render(): JSX.Element {
|
||||||
let topSpinner;
|
let topSpinner;
|
||||||
let bottomSpinner;
|
let bottomSpinner;
|
||||||
if (this.props.backPaginating) {
|
if (this.props.backPaginating) {
|
||||||
|
@ -37,15 +37,15 @@ export default class NonUrgentToastContainer extends React.PureComponent<IProps,
|
|||||||
NonUrgentToastStore.instance.on(UPDATE_EVENT, this.onUpdateToasts);
|
NonUrgentToastStore.instance.on(UPDATE_EVENT, this.onUpdateToasts);
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentWillUnmount() {
|
public componentWillUnmount(): void {
|
||||||
NonUrgentToastStore.instance.off(UPDATE_EVENT, this.onUpdateToasts);
|
NonUrgentToastStore.instance.off(UPDATE_EVENT, this.onUpdateToasts);
|
||||||
}
|
}
|
||||||
|
|
||||||
private onUpdateToasts = () => {
|
private onUpdateToasts = (): void => {
|
||||||
this.setState({ toasts: NonUrgentToastStore.instance.components });
|
this.setState({ toasts: NonUrgentToastStore.instance.components });
|
||||||
};
|
};
|
||||||
|
|
||||||
public render() {
|
public render(): JSX.Element {
|
||||||
const toasts = this.state.toasts.map((t, i) => {
|
const toasts = this.state.toasts.map((t, i) => {
|
||||||
return (
|
return (
|
||||||
<div className="mx_NonUrgentToastContainer_toast" key={`toast-${i}`}>
|
<div className="mx_NonUrgentToastContainer_toast" key={`toast-${i}`}>
|
||||||
|
@ -55,7 +55,7 @@ export default class NotificationPanel extends React.PureComponent<IProps, IStat
|
|||||||
this.setState({ narrow });
|
this.setState({ narrow });
|
||||||
};
|
};
|
||||||
|
|
||||||
public render() {
|
public render(): JSX.Element {
|
||||||
const emptyState = (
|
const emptyState = (
|
||||||
<div className="mx_RightPanel_empty mx_NotificationPanel_empty">
|
<div className="mx_RightPanel_empty mx_NotificationPanel_empty">
|
||||||
<h2>{_t("You're all caught up")}</h2>
|
<h2>{_t("You're all caught up")}</h2>
|
||||||
|
@ -79,7 +79,7 @@ export default class PictureInPictureDragger extends React.Component<IProps> {
|
|||||||
this._moving = value;
|
this._moving = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount() {
|
public componentDidMount(): void {
|
||||||
document.addEventListener("mousemove", this.onMoving);
|
document.addEventListener("mousemove", this.onMoving);
|
||||||
document.addEventListener("mouseup", this.onEndMoving);
|
document.addEventListener("mouseup", this.onEndMoving);
|
||||||
UIStore.instance.on(UI_EVENTS.Resize, this.onResize);
|
UIStore.instance.on(UI_EVENTS.Resize, this.onResize);
|
||||||
@ -87,7 +87,7 @@ export default class PictureInPictureDragger extends React.Component<IProps> {
|
|||||||
this.snap();
|
this.snap();
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentWillUnmount() {
|
public componentWillUnmount(): void {
|
||||||
document.removeEventListener("mousemove", this.onMoving);
|
document.removeEventListener("mousemove", this.onMoving);
|
||||||
document.removeEventListener("mouseup", this.onEndMoving);
|
document.removeEventListener("mouseup", this.onEndMoving);
|
||||||
UIStore.instance.off(UI_EVENTS.Resize, this.onResize);
|
UIStore.instance.off(UI_EVENTS.Resize, this.onResize);
|
||||||
@ -97,7 +97,7 @@ export default class PictureInPictureDragger extends React.Component<IProps> {
|
|||||||
if (prevProps.children !== this.props.children) this.snap(true);
|
if (prevProps.children !== this.props.children) this.snap(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private animationCallback = () => {
|
private animationCallback = (): void => {
|
||||||
if (
|
if (
|
||||||
!this.moving &&
|
!this.moving &&
|
||||||
Math.abs(this.translationX - this.desiredTranslationX) <= 1 &&
|
Math.abs(this.translationX - this.desiredTranslationX) <= 1 &&
|
||||||
@ -119,13 +119,13 @@ export default class PictureInPictureDragger extends React.Component<IProps> {
|
|||||||
this.props.onMove?.();
|
this.props.onMove?.();
|
||||||
};
|
};
|
||||||
|
|
||||||
private setStyle = () => {
|
private setStyle = (): void => {
|
||||||
if (!this.callViewWrapper.current) return;
|
if (!this.callViewWrapper.current) return;
|
||||||
// Set the element's style directly, bypassing React for efficiency
|
// Set the element's style directly, bypassing React for efficiency
|
||||||
this.callViewWrapper.current.style.transform = `translateX(${this.translationX}px) translateY(${this.translationY}px)`;
|
this.callViewWrapper.current.style.transform = `translateX(${this.translationX}px) translateY(${this.translationY}px)`;
|
||||||
};
|
};
|
||||||
|
|
||||||
private setTranslation(inTranslationX: number, inTranslationY: number) {
|
private setTranslation(inTranslationX: number, inTranslationY: number): void {
|
||||||
const width = this.callViewWrapper.current?.clientWidth || PIP_VIEW_WIDTH;
|
const width = this.callViewWrapper.current?.clientWidth || PIP_VIEW_WIDTH;
|
||||||
const height = this.callViewWrapper.current?.clientHeight || PIP_VIEW_HEIGHT;
|
const height = this.callViewWrapper.current?.clientHeight || PIP_VIEW_HEIGHT;
|
||||||
|
|
||||||
@ -152,7 +152,7 @@ export default class PictureInPictureDragger extends React.Component<IProps> {
|
|||||||
this.snap(false);
|
this.snap(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
private snap = (animate = false) => {
|
private snap = (animate = false): void => {
|
||||||
const translationX = this.desiredTranslationX;
|
const translationX = this.desiredTranslationX;
|
||||||
const translationY = this.desiredTranslationY;
|
const translationY = this.desiredTranslationY;
|
||||||
// We subtract the PiP size from the window size in order to calculate
|
// We subtract the PiP size from the window size in order to calculate
|
||||||
@ -187,14 +187,14 @@ export default class PictureInPictureDragger extends React.Component<IProps> {
|
|||||||
this.scheduledUpdate.mark();
|
this.scheduledUpdate.mark();
|
||||||
};
|
};
|
||||||
|
|
||||||
private onStartMoving = (event: React.MouseEvent | MouseEvent) => {
|
private onStartMoving = (event: React.MouseEvent | MouseEvent): void => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
||||||
this.mouseHeld = true;
|
this.mouseHeld = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
private onMoving = (event: MouseEvent) => {
|
private onMoving = (event: MouseEvent): void => {
|
||||||
if (!this.mouseHeld) return;
|
if (!this.mouseHeld) return;
|
||||||
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@ -210,7 +210,7 @@ export default class PictureInPictureDragger extends React.Component<IProps> {
|
|||||||
this.setTranslation(event.pageX - this.initX, event.pageY - this.initY);
|
this.setTranslation(event.pageX - this.initX, event.pageY - this.initY);
|
||||||
};
|
};
|
||||||
|
|
||||||
private onEndMoving = (event: MouseEvent) => {
|
private onEndMoving = (event: MouseEvent): void => {
|
||||||
if (!this.mouseHeld) return;
|
if (!this.mouseHeld) return;
|
||||||
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@ -223,7 +223,7 @@ export default class PictureInPictureDragger extends React.Component<IProps> {
|
|||||||
this.snap(true);
|
this.snap(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
private onClickCapture = (event: React.MouseEvent) => {
|
private onClickCapture = (event: React.MouseEvent): void => {
|
||||||
// To prevent mouse up events during dragging from being double-counted
|
// To prevent mouse up events during dragging from being double-counted
|
||||||
// as clicks, we cancel clicks before they ever reach the target
|
// as clicks, we cancel clicks before they ever reach the target
|
||||||
if (this.moving) {
|
if (this.moving) {
|
||||||
@ -232,7 +232,7 @@ export default class PictureInPictureDragger extends React.Component<IProps> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public render() {
|
public render(): JSX.Element {
|
||||||
const style = {
|
const style = {
|
||||||
transform: `translateX(${this.translationX}px) translateY(${this.translationY}px)`,
|
transform: `translateX(${this.translationX}px) translateY(${this.translationY}px)`,
|
||||||
};
|
};
|
||||||
|
@ -135,7 +135,7 @@ class PipContainerInner extends React.Component<IProps, IState> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount() {
|
public componentDidMount(): void {
|
||||||
LegacyCallHandler.instance.addListener(LegacyCallHandlerEvent.CallChangeRoom, this.updateCalls);
|
LegacyCallHandler.instance.addListener(LegacyCallHandlerEvent.CallChangeRoom, this.updateCalls);
|
||||||
LegacyCallHandler.instance.addListener(LegacyCallHandlerEvent.CallState, this.updateCalls);
|
LegacyCallHandler.instance.addListener(LegacyCallHandlerEvent.CallState, this.updateCalls);
|
||||||
SdkContextClass.instance.roomViewStore.addListener(UPDATE_EVENT, this.onRoomViewStoreUpdate);
|
SdkContextClass.instance.roomViewStore.addListener(UPDATE_EVENT, this.onRoomViewStoreUpdate);
|
||||||
@ -149,7 +149,7 @@ class PipContainerInner extends React.Component<IProps, IState> {
|
|||||||
ActiveWidgetStore.instance.on(ActiveWidgetStoreEvent.Undock, this.onWidgetDockChanges);
|
ActiveWidgetStore.instance.on(ActiveWidgetStoreEvent.Undock, this.onWidgetDockChanges);
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentWillUnmount() {
|
public componentWillUnmount(): void {
|
||||||
LegacyCallHandler.instance.removeListener(LegacyCallHandlerEvent.CallChangeRoom, this.updateCalls);
|
LegacyCallHandler.instance.removeListener(LegacyCallHandlerEvent.CallChangeRoom, this.updateCalls);
|
||||||
LegacyCallHandler.instance.removeListener(LegacyCallHandlerEvent.CallState, this.updateCalls);
|
LegacyCallHandler.instance.removeListener(LegacyCallHandlerEvent.CallState, this.updateCalls);
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
@ -164,9 +164,9 @@ class PipContainerInner extends React.Component<IProps, IState> {
|
|||||||
ActiveWidgetStore.instance.off(ActiveWidgetStoreEvent.Undock, this.onWidgetDockChanges);
|
ActiveWidgetStore.instance.off(ActiveWidgetStoreEvent.Undock, this.onWidgetDockChanges);
|
||||||
}
|
}
|
||||||
|
|
||||||
private onMove = () => this.props.movePersistedElement.current?.();
|
private onMove = (): void => this.props.movePersistedElement.current?.();
|
||||||
|
|
||||||
private onRoomViewStoreUpdate = () => {
|
private onRoomViewStoreUpdate = (): void => {
|
||||||
const newRoomId = SdkContextClass.instance.roomViewStore.getRoomId();
|
const newRoomId = SdkContextClass.instance.roomViewStore.getRoomId();
|
||||||
const oldRoomId = this.state.viewedRoomId;
|
const oldRoomId = this.state.viewedRoomId;
|
||||||
if (newRoomId === oldRoomId) return;
|
if (newRoomId === oldRoomId) return;
|
||||||
@ -213,7 +213,7 @@ class PipContainerInner extends React.Component<IProps, IState> {
|
|||||||
this.updateShowWidgetInPip();
|
this.updateShowWidgetInPip();
|
||||||
};
|
};
|
||||||
|
|
||||||
private onCallRemoteHold = () => {
|
private onCallRemoteHold = (): void => {
|
||||||
if (!this.state.viewedRoomId) return;
|
if (!this.state.viewedRoomId) return;
|
||||||
const [primaryCall, secondaryCalls] = getPrimarySecondaryCallsForPip(this.state.viewedRoomId);
|
const [primaryCall, secondaryCalls] = getPrimarySecondaryCallsForPip(this.state.viewedRoomId);
|
||||||
|
|
||||||
@ -238,7 +238,7 @@ class PipContainerInner extends React.Component<IProps, IState> {
|
|||||||
public updateShowWidgetInPip(
|
public updateShowWidgetInPip(
|
||||||
persistentWidgetId = this.state.persistentWidgetId,
|
persistentWidgetId = this.state.persistentWidgetId,
|
||||||
persistentRoomId = this.state.persistentRoomId,
|
persistentRoomId = this.state.persistentRoomId,
|
||||||
) {
|
): void {
|
||||||
let fromAnotherRoom = false;
|
let fromAnotherRoom = false;
|
||||||
let notDocked = false;
|
let notDocked = false;
|
||||||
// Sanity check the room - the widget may have been destroyed between render cycles, and
|
// Sanity check the room - the widget may have been destroyed between render cycles, and
|
||||||
@ -293,7 +293,7 @@ class PipContainerInner extends React.Component<IProps, IState> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render(): JSX.Element {
|
||||||
const pipMode = true;
|
const pipMode = true;
|
||||||
let pipContent: Array<CreatePipChildren> = [];
|
let pipContent: Array<CreatePipChildren> = [];
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@ export default class RightPanel extends React.Component<IProps, IState> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private onRoomStateMember = (ev: MatrixEvent, state: RoomState, member: RoomMember) => {
|
private onRoomStateMember = (ev: MatrixEvent, state: RoomState, member: RoomMember): void => {
|
||||||
if (!this.props.room || member.roomId !== this.props.room.roomId) {
|
if (!this.props.room || member.roomId !== this.props.room.roomId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -118,11 +118,11 @@ export default class RightPanel extends React.Component<IProps, IState> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private onRightPanelStoreUpdate = () => {
|
private onRightPanelStoreUpdate = (): void => {
|
||||||
this.setState({ ...(RightPanel.getDerivedStateFromProps(this.props) as IState) });
|
this.setState({ ...(RightPanel.getDerivedStateFromProps(this.props) as IState) });
|
||||||
};
|
};
|
||||||
|
|
||||||
private onClose = () => {
|
private onClose = (): void => {
|
||||||
// XXX: There are three different ways of 'closing' this panel depending on what state
|
// XXX: There are three different ways of 'closing' this panel depending on what state
|
||||||
// things are in... this knows far more than it should do about the state of the rest
|
// things are in... this knows far more than it should do about the state of the rest
|
||||||
// of the app and is generally a bit silly.
|
// of the app and is generally a bit silly.
|
||||||
|
@ -39,15 +39,15 @@ export default class RoomSearch extends React.PureComponent<IProps> {
|
|||||||
this.dispatcherRef = defaultDispatcher.register(this.onAction);
|
this.dispatcherRef = defaultDispatcher.register(this.onAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentWillUnmount() {
|
public componentWillUnmount(): void {
|
||||||
defaultDispatcher.unregister(this.dispatcherRef);
|
defaultDispatcher.unregister(this.dispatcherRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
private openSpotlight() {
|
private openSpotlight(): void {
|
||||||
Modal.createDialog(SpotlightDialog, {}, "mx_SpotlightDialog_wrapper", false, true);
|
Modal.createDialog(SpotlightDialog, {}, "mx_SpotlightDialog_wrapper", false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private onAction = (payload: ActionPayload) => {
|
private onAction = (payload: ActionPayload): void => {
|
||||||
if (payload.action === "focus_room_filter") {
|
if (payload.action === "focus_room_filter") {
|
||||||
this.openSpotlight();
|
this.openSpotlight();
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ import RoomContext from "../../contexts/RoomContext";
|
|||||||
import SettingsStore from "../../settings/SettingsStore";
|
import SettingsStore from "../../settings/SettingsStore";
|
||||||
|
|
||||||
const DEBUG = false;
|
const DEBUG = false;
|
||||||
let debuglog = function (msg: string) {};
|
let debuglog = function (msg: string): void {};
|
||||||
|
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
@ -76,7 +76,7 @@ export const RoomSearchView = forwardRef<ScrollPanel, Props>(
|
|||||||
|
|
||||||
return searchPromise
|
return searchPromise
|
||||||
.then(
|
.then(
|
||||||
async (results) => {
|
async (results): Promise<boolean> => {
|
||||||
debuglog("search complete");
|
debuglog("search complete");
|
||||||
if (aborted.current) {
|
if (aborted.current) {
|
||||||
logger.error("Discarding stale search results");
|
logger.error("Discarding stale search results");
|
||||||
@ -209,7 +209,7 @@ export const RoomSearchView = forwardRef<ScrollPanel, Props>(
|
|||||||
|
|
||||||
// once dynamic content in the search results load, make the scrollPanel check
|
// once dynamic content in the search results load, make the scrollPanel check
|
||||||
// the scroll offsets.
|
// the scroll offsets.
|
||||||
const onHeightChanged = () => {
|
const onHeightChanged = (): void => {
|
||||||
const scrollPanel = ref.current;
|
const scrollPanel = ref.current;
|
||||||
scrollPanel?.checkScroll();
|
scrollPanel?.checkScroll();
|
||||||
};
|
};
|
||||||
|
@ -146,7 +146,7 @@ export default class RoomStatusBar extends React.PureComponent<IProps, IState> {
|
|||||||
dis.fire(Action.FocusSendMessageComposer);
|
dis.fire(Action.FocusSendMessageComposer);
|
||||||
};
|
};
|
||||||
|
|
||||||
private onRoomLocalEchoUpdated = (ev: MatrixEvent, room: Room) => {
|
private onRoomLocalEchoUpdated = (ev: MatrixEvent, room: Room): void => {
|
||||||
if (room.roomId !== this.props.room.roomId) return;
|
if (room.roomId !== this.props.room.roomId) return;
|
||||||
const messages = getUnsentMessages(this.props.room);
|
const messages = getUnsentMessages(this.props.room);
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -115,7 +115,7 @@ import VoipUserMapper from "../../VoipUserMapper";
|
|||||||
import { isCallEvent } from "./LegacyCallEventGrouper";
|
import { isCallEvent } from "./LegacyCallEventGrouper";
|
||||||
|
|
||||||
const DEBUG = false;
|
const DEBUG = false;
|
||||||
let debuglog = function (msg: string) {};
|
let debuglog = function (msg: string): void {};
|
||||||
|
|
||||||
const BROWSER_SUPPORTS_SANDBOX = "sandbox" in document.createElement("iframe");
|
const BROWSER_SUPPORTS_SANDBOX = "sandbox" in document.createElement("iframe");
|
||||||
|
|
||||||
@ -248,7 +248,7 @@ function LocalRoomView(props: LocalRoomViewProps): ReactElement {
|
|||||||
encryptionTile = <EncryptionEvent mxEvent={encryptionEvent} />;
|
encryptionTile = <EncryptionEvent mxEvent={encryptionEvent} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const onRetryClicked = () => {
|
const onRetryClicked = (): void => {
|
||||||
room.state = LocalRoomState.NEW;
|
room.state = LocalRoomState.NEW;
|
||||||
defaultDispatcher.dispatch({
|
defaultDispatcher.dispatch({
|
||||||
action: "local_room_event",
|
action: "local_room_event",
|
||||||
@ -470,21 +470,21 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
private onIsResizing = (resizing: boolean) => {
|
private onIsResizing = (resizing: boolean): void => {
|
||||||
this.setState({ resizing });
|
this.setState({ resizing });
|
||||||
};
|
};
|
||||||
|
|
||||||
private onWidgetStoreUpdate = () => {
|
private onWidgetStoreUpdate = (): void => {
|
||||||
if (!this.state.room) return;
|
if (!this.state.room) return;
|
||||||
this.checkWidgets(this.state.room);
|
this.checkWidgets(this.state.room);
|
||||||
};
|
};
|
||||||
|
|
||||||
private onWidgetEchoStoreUpdate = () => {
|
private onWidgetEchoStoreUpdate = (): void => {
|
||||||
if (!this.state.room) return;
|
if (!this.state.room) return;
|
||||||
this.checkWidgets(this.state.room);
|
this.checkWidgets(this.state.room);
|
||||||
};
|
};
|
||||||
|
|
||||||
private onWidgetLayoutChange = () => {
|
private onWidgetLayoutChange = (): void => {
|
||||||
if (!this.state.room) return;
|
if (!this.state.room) return;
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: "appsDrawer",
|
action: "appsDrawer",
|
||||||
@ -505,7 +505,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
private getMainSplitContentType = (room: Room) => {
|
private getMainSplitContentType = (room: Room): MainSplitContentType => {
|
||||||
if (
|
if (
|
||||||
(SettingsStore.getValue("feature_group_calls") && this.context.roomViewStore.isViewingCall()) ||
|
(SettingsStore.getValue("feature_group_calls") && this.context.roomViewStore.isViewingCall()) ||
|
||||||
isVideoRoom(room)
|
isVideoRoom(room)
|
||||||
@ -707,7 +707,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private onActiveCalls = () => {
|
private onActiveCalls = (): void => {
|
||||||
if (this.state.roomId === undefined) return;
|
if (this.state.roomId === undefined) return;
|
||||||
const activeCall = CallStore.instance.getActiveCall(this.state.roomId);
|
const activeCall = CallStore.instance.getActiveCall(this.state.roomId);
|
||||||
|
|
||||||
@ -727,7 +727,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
this.setState({ activeCall });
|
this.setState({ activeCall });
|
||||||
};
|
};
|
||||||
|
|
||||||
private getRoomId = () => {
|
private getRoomId = (): string => {
|
||||||
// According to `onRoomViewStoreUpdate`, `state.roomId` can be null
|
// According to `onRoomViewStoreUpdate`, `state.roomId` can be null
|
||||||
// if we have a room alias we haven't resolved yet. To work around this,
|
// if we have a room alias we haven't resolved yet. To work around this,
|
||||||
// first we'll try the room object if it's there, and then fallback to
|
// first we'll try the room object if it's there, and then fallback to
|
||||||
@ -736,7 +736,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
return this.state.room ? this.state.room.roomId : this.state.roomId;
|
return this.state.room ? this.state.room.roomId : this.state.roomId;
|
||||||
};
|
};
|
||||||
|
|
||||||
private getPermalinkCreatorForRoom(room: Room) {
|
private getPermalinkCreatorForRoom(room: Room): RoomPermalinkCreator {
|
||||||
if (this.permalinkCreators[room.roomId]) return this.permalinkCreators[room.roomId];
|
if (this.permalinkCreators[room.roomId]) return this.permalinkCreators[room.roomId];
|
||||||
|
|
||||||
this.permalinkCreators[room.roomId] = new RoomPermalinkCreator(room);
|
this.permalinkCreators[room.roomId] = new RoomPermalinkCreator(room);
|
||||||
@ -750,14 +750,14 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
return this.permalinkCreators[room.roomId];
|
return this.permalinkCreators[room.roomId];
|
||||||
}
|
}
|
||||||
|
|
||||||
private stopAllPermalinkCreators() {
|
private stopAllPermalinkCreators(): void {
|
||||||
if (!this.permalinkCreators) return;
|
if (!this.permalinkCreators) return;
|
||||||
for (const roomId of Object.keys(this.permalinkCreators)) {
|
for (const roomId of Object.keys(this.permalinkCreators)) {
|
||||||
this.permalinkCreators[roomId].stop();
|
this.permalinkCreators[roomId].stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private setupRoom(room: Room, roomId: string, joining: boolean, shouldPeek: boolean) {
|
private setupRoom(room: Room, roomId: string, joining: boolean, shouldPeek: boolean): void {
|
||||||
// if this is an unknown room then we're in one of three states:
|
// if this is an unknown room then we're in one of three states:
|
||||||
// - This is a room we can peek into (search engine) (we can /peek)
|
// - This is a room we can peek into (search engine) (we can /peek)
|
||||||
// - This is a room we can publicly join or were invited to. (we can /join)
|
// - This is a room we can publicly join or were invited to. (we can /join)
|
||||||
@ -822,7 +822,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private shouldShowApps(room: Room) {
|
private shouldShowApps(room: Room): boolean {
|
||||||
if (!BROWSER_SUPPORTS_SANDBOX || !room) return false;
|
if (!BROWSER_SUPPORTS_SANDBOX || !room) return false;
|
||||||
|
|
||||||
// Check if user has previously chosen to hide the app drawer for this
|
// Check if user has previously chosen to hide the app drawer for this
|
||||||
@ -838,7 +838,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
return isManuallyShown && widgets.length > 0;
|
return isManuallyShown && widgets.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount() {
|
public componentDidMount(): void {
|
||||||
this.onRoomViewStoreUpdate(true);
|
this.onRoomViewStoreUpdate(true);
|
||||||
|
|
||||||
const call = this.getCallForRoom();
|
const call = this.getCallForRoom();
|
||||||
@ -851,7 +851,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
window.addEventListener("beforeunload", this.onPageUnload);
|
window.addEventListener("beforeunload", this.onPageUnload);
|
||||||
}
|
}
|
||||||
|
|
||||||
public shouldComponentUpdate(nextProps, nextState) {
|
public shouldComponentUpdate(nextProps, nextState): boolean {
|
||||||
const hasPropsDiff = objectHasDiff(this.props, nextProps);
|
const hasPropsDiff = objectHasDiff(this.props, nextProps);
|
||||||
|
|
||||||
const { upgradeRecommendation, ...state } = this.state;
|
const { upgradeRecommendation, ...state } = this.state;
|
||||||
@ -864,7 +864,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
return hasPropsDiff || hasStateDiff;
|
return hasPropsDiff || hasStateDiff;
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidUpdate() {
|
public componentDidUpdate(): void {
|
||||||
// Note: We check the ref here with a flag because componentDidMount, despite
|
// Note: We check the ref here with a flag because componentDidMount, despite
|
||||||
// documentation, does not define our messagePanel ref. It looks like our spinner
|
// documentation, does not define our messagePanel ref. It looks like our spinner
|
||||||
// in render() prevents the ref from being set on first mount, so we try and
|
// in render() prevents the ref from being set on first mount, so we try and
|
||||||
@ -877,7 +877,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentWillUnmount() {
|
public componentWillUnmount(): void {
|
||||||
// set a boolean to say we've been unmounted, which any pending
|
// set a boolean to say we've been unmounted, which any pending
|
||||||
// promises can use to throw away their results.
|
// promises can use to throw away their results.
|
||||||
//
|
//
|
||||||
@ -947,13 +947,13 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private onRightPanelStoreUpdate = () => {
|
private onRightPanelStoreUpdate = (): void => {
|
||||||
this.setState({
|
this.setState({
|
||||||
showRightPanel: this.context.rightPanelStore.isOpenForRoom(this.state.roomId),
|
showRightPanel: this.context.rightPanelStore.isOpenForRoom(this.state.roomId),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
private onPageUnload = (event) => {
|
private onPageUnload = (event): string => {
|
||||||
if (ContentMessages.sharedInstance().getCurrentUploads().length > 0) {
|
if (ContentMessages.sharedInstance().getCurrentUploads().length > 0) {
|
||||||
return (event.returnValue = _t("You seem to be uploading files, are you sure you want to quit?"));
|
return (event.returnValue = _t("You seem to be uploading files, are you sure you want to quit?"));
|
||||||
} else if (this.getCallForRoom() && this.state.callState !== "ended") {
|
} else if (this.getCallForRoom() && this.state.callState !== "ended") {
|
||||||
@ -961,7 +961,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private onReactKeyDown = (ev) => {
|
private onReactKeyDown = (ev): void => {
|
||||||
let handled = false;
|
let handled = false;
|
||||||
|
|
||||||
const action = getKeyBindingsManager().getRoomAction(ev);
|
const action = getKeyBindingsManager().getRoomAction(ev);
|
||||||
@ -1120,12 +1120,12 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private onLocalRoomEvent(roomId: string) {
|
private onLocalRoomEvent(roomId: string): void {
|
||||||
if (roomId !== this.state.room.roomId) return;
|
if (roomId !== this.state.room.roomId) return;
|
||||||
createRoomFromLocalRoom(this.context.client, this.state.room as LocalRoom);
|
createRoomFromLocalRoom(this.context.client, this.state.room as LocalRoom);
|
||||||
}
|
}
|
||||||
|
|
||||||
private onRoomTimeline = (ev: MatrixEvent, room: Room | null, toStartOfTimeline: boolean, removed, data) => {
|
private onRoomTimeline = (ev: MatrixEvent, room: Room | null, toStartOfTimeline: boolean, removed, data): void => {
|
||||||
if (this.unmounted) return;
|
if (this.unmounted) return;
|
||||||
|
|
||||||
// ignore events for other rooms or the notification timeline set
|
// ignore events for other rooms or the notification timeline set
|
||||||
@ -1167,7 +1167,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private onEventDecrypted = (ev: MatrixEvent) => {
|
private onEventDecrypted = (ev: MatrixEvent): void => {
|
||||||
if (!this.state.room || !this.state.matrixClientIsReady) return; // not ready at all
|
if (!this.state.room || !this.state.matrixClientIsReady) return; // not ready at all
|
||||||
if (ev.getRoomId() !== this.state.room.roomId) return; // not for us
|
if (ev.getRoomId() !== this.state.room.roomId) return; // not for us
|
||||||
this.updateVisibleDecryptionFailures();
|
this.updateVisibleDecryptionFailures();
|
||||||
@ -1175,7 +1175,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
this.handleEffects(ev);
|
this.handleEffects(ev);
|
||||||
};
|
};
|
||||||
|
|
||||||
private handleEffects = (ev: MatrixEvent) => {
|
private handleEffects = (ev: MatrixEvent): void => {
|
||||||
const notifState = this.context.roomNotificationStateStore.getRoomState(this.state.room);
|
const notifState = this.context.roomNotificationStateStore.getRoomState(this.state.room);
|
||||||
if (!notifState.isUnread) return;
|
if (!notifState.isUnread) return;
|
||||||
|
|
||||||
@ -1189,19 +1189,19 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
private onRoomName = (room: Room) => {
|
private onRoomName = (room: Room): void => {
|
||||||
if (this.state.room && room.roomId == this.state.room.roomId) {
|
if (this.state.room && room.roomId == this.state.room.roomId) {
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private onKeyBackupStatus = () => {
|
private onKeyBackupStatus = (): void => {
|
||||||
// Key backup status changes affect whether the in-room recovery
|
// Key backup status changes affect whether the in-room recovery
|
||||||
// reminder is displayed.
|
// reminder is displayed.
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
};
|
};
|
||||||
|
|
||||||
public canResetTimeline = () => {
|
public canResetTimeline = (): boolean => {
|
||||||
if (!this.messagePanel) {
|
if (!this.messagePanel) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1216,7 +1216,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
|
|
||||||
// called when state.room is first initialised (either at initial load,
|
// called when state.room is first initialised (either at initial load,
|
||||||
// after a successful peek, or after we join the room).
|
// after a successful peek, or after we join the room).
|
||||||
private onRoomLoaded = (room: Room) => {
|
private onRoomLoaded = (room: Room): void => {
|
||||||
if (this.unmounted) return;
|
if (this.unmounted) return;
|
||||||
// Attach a widget store listener only when we get a room
|
// Attach a widget store listener only when we get a room
|
||||||
this.context.widgetLayoutStore.on(WidgetLayoutStore.emissionForRoom(room), this.onWidgetLayoutChange);
|
this.context.widgetLayoutStore.on(WidgetLayoutStore.emissionForRoom(room), this.onWidgetLayoutChange);
|
||||||
@ -1251,17 +1251,17 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private getRoomTombstone(room = this.state.room) {
|
private getRoomTombstone(room = this.state.room): MatrixEvent | undefined {
|
||||||
return room?.currentState.getStateEvents(EventType.RoomTombstone, "");
|
return room?.currentState.getStateEvents(EventType.RoomTombstone, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
private async calculateRecommendedVersion(room: Room) {
|
private async calculateRecommendedVersion(room: Room): Promise<void> {
|
||||||
const upgradeRecommendation = await room.getRecommendedVersion();
|
const upgradeRecommendation = await room.getRecommendedVersion();
|
||||||
if (this.unmounted) return;
|
if (this.unmounted) return;
|
||||||
this.setState({ upgradeRecommendation });
|
this.setState({ upgradeRecommendation });
|
||||||
}
|
}
|
||||||
|
|
||||||
private async loadMembersIfJoined(room: Room) {
|
private async loadMembersIfJoined(room: Room): Promise<void> {
|
||||||
// lazy load members if enabled
|
// lazy load members if enabled
|
||||||
if (this.context.client.hasLazyLoadMembersEnabled()) {
|
if (this.context.client.hasLazyLoadMembersEnabled()) {
|
||||||
if (room && room.getMyMembership() === "join") {
|
if (room && room.getMyMembership() === "join") {
|
||||||
@ -1280,14 +1280,14 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private calculatePeekRules(room: Room) {
|
private calculatePeekRules(room: Room): void {
|
||||||
const historyVisibility = room.currentState.getStateEvents(EventType.RoomHistoryVisibility, "");
|
const historyVisibility = room.currentState.getStateEvents(EventType.RoomHistoryVisibility, "");
|
||||||
this.setState({
|
this.setState({
|
||||||
canPeek: historyVisibility?.getContent().history_visibility === HistoryVisibility.WorldReadable,
|
canPeek: historyVisibility?.getContent().history_visibility === HistoryVisibility.WorldReadable,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private updatePreviewUrlVisibility({ roomId }: Room) {
|
private updatePreviewUrlVisibility({ roomId }: Room): void {
|
||||||
// URL Previews in E2EE rooms can be a privacy leak so use a different setting which is per-room explicit
|
// URL Previews in E2EE rooms can be a privacy leak so use a different setting which is per-room explicit
|
||||||
const key = this.context.client.isRoomEncrypted(roomId) ? "urlPreviewsEnabled_e2ee" : "urlPreviewsEnabled";
|
const key = this.context.client.isRoomEncrypted(roomId) ? "urlPreviewsEnabled_e2ee" : "urlPreviewsEnabled";
|
||||||
this.setState({
|
this.setState({
|
||||||
@ -1295,7 +1295,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private onRoom = (room: Room) => {
|
private onRoom = (room: Room): void => {
|
||||||
if (!room || room.roomId !== this.state.roomId) {
|
if (!room || room.roomId !== this.state.roomId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1318,7 +1318,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
private onDeviceVerificationChanged = (userId: string) => {
|
private onDeviceVerificationChanged = (userId: string): void => {
|
||||||
const room = this.state.room;
|
const room = this.state.room;
|
||||||
if (!room?.currentState.getMember(userId)) {
|
if (!room?.currentState.getMember(userId)) {
|
||||||
return;
|
return;
|
||||||
@ -1326,7 +1326,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
this.updateE2EStatus(room);
|
this.updateE2EStatus(room);
|
||||||
};
|
};
|
||||||
|
|
||||||
private onUserVerificationChanged = (userId: string) => {
|
private onUserVerificationChanged = (userId: string): void => {
|
||||||
const room = this.state.room;
|
const room = this.state.room;
|
||||||
if (!room || !room.currentState.getMember(userId)) {
|
if (!room || !room.currentState.getMember(userId)) {
|
||||||
return;
|
return;
|
||||||
@ -1334,14 +1334,14 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
this.updateE2EStatus(room);
|
this.updateE2EStatus(room);
|
||||||
};
|
};
|
||||||
|
|
||||||
private onCrossSigningKeysChanged = () => {
|
private onCrossSigningKeysChanged = (): void => {
|
||||||
const room = this.state.room;
|
const room = this.state.room;
|
||||||
if (room) {
|
if (room) {
|
||||||
this.updateE2EStatus(room);
|
this.updateE2EStatus(room);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private async updateE2EStatus(room: Room) {
|
private async updateE2EStatus(room: Room): Promise<void> {
|
||||||
if (!this.context.client.isRoomEncrypted(room.roomId)) return;
|
if (!this.context.client.isRoomEncrypted(room.roomId)) return;
|
||||||
|
|
||||||
// If crypto is not currently enabled, we aren't tracking devices at all,
|
// If crypto is not currently enabled, we aren't tracking devices at all,
|
||||||
@ -1357,13 +1357,13 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
this.setState({ e2eStatus });
|
this.setState({ e2eStatus });
|
||||||
}
|
}
|
||||||
|
|
||||||
private onUrlPreviewsEnabledChange = () => {
|
private onUrlPreviewsEnabledChange = (): void => {
|
||||||
if (this.state.room) {
|
if (this.state.room) {
|
||||||
this.updatePreviewUrlVisibility(this.state.room);
|
this.updatePreviewUrlVisibility(this.state.room);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private onRoomStateEvents = (ev: MatrixEvent, state: RoomState) => {
|
private onRoomStateEvents = (ev: MatrixEvent, state: RoomState): void => {
|
||||||
// ignore if we don't have a room yet
|
// ignore if we don't have a room yet
|
||||||
if (!this.state.room || this.state.room.roomId !== state.roomId) return;
|
if (!this.state.room || this.state.room.roomId !== state.roomId) return;
|
||||||
|
|
||||||
@ -1377,7 +1377,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private onRoomStateUpdate = (state: RoomState) => {
|
private onRoomStateUpdate = (state: RoomState): void => {
|
||||||
// ignore members in other rooms
|
// ignore members in other rooms
|
||||||
if (state.roomId !== this.state.room?.roomId) {
|
if (state.roomId !== this.state.room?.roomId) {
|
||||||
return;
|
return;
|
||||||
@ -1386,7 +1386,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
this.updateRoomMembers();
|
this.updateRoomMembers();
|
||||||
};
|
};
|
||||||
|
|
||||||
private onMyMembership = (room: Room, membership: string, oldMembership: string) => {
|
private onMyMembership = (room: Room, membership: string, oldMembership: string): void => {
|
||||||
if (room.roomId === this.state.roomId) {
|
if (room.roomId === this.state.roomId) {
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
this.loadMembersIfJoined(room);
|
this.loadMembersIfJoined(room);
|
||||||
@ -1394,7 +1394,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private updatePermissions(room: Room) {
|
private updatePermissions(room: Room): void {
|
||||||
if (room) {
|
if (room) {
|
||||||
const me = this.context.client.getUserId();
|
const me = this.context.client.getUserId();
|
||||||
const canReact =
|
const canReact =
|
||||||
@ -1420,7 +1420,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
{ leading: true, trailing: true },
|
{ leading: true, trailing: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
private checkDesktopNotifications() {
|
private checkDesktopNotifications(): void {
|
||||||
const memberCount = this.state.room.getJoinedMemberCount() + this.state.room.getInvitedMemberCount();
|
const memberCount = this.state.room.getJoinedMemberCount() + this.state.room.getInvitedMemberCount();
|
||||||
// if they are not alone prompt the user about notifications so they don't miss replies
|
// if they are not alone prompt the user about notifications so they don't miss replies
|
||||||
if (memberCount > 1 && Notifier.shouldShowPrompt()) {
|
if (memberCount > 1 && Notifier.shouldShowPrompt()) {
|
||||||
@ -1428,7 +1428,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateDMState() {
|
private updateDMState(): void {
|
||||||
const room = this.state.room;
|
const room = this.state.room;
|
||||||
if (room.getMyMembership() != "join") {
|
if (room.getMyMembership() != "join") {
|
||||||
return;
|
return;
|
||||||
@ -1439,7 +1439,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private onInviteClick = () => {
|
private onInviteClick = (): void => {
|
||||||
// open the room inviter
|
// open the room inviter
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: "view_invite",
|
action: "view_invite",
|
||||||
@ -1447,7 +1447,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
private onJoinButtonClicked = () => {
|
private onJoinButtonClicked = (): void => {
|
||||||
// If the user is a ROU, allow them to transition to a PWLU
|
// If the user is a ROU, allow them to transition to a PWLU
|
||||||
if (this.context.client?.isGuest()) {
|
if (this.context.client?.isGuest()) {
|
||||||
// Join this room once the user has registered and logged in
|
// Join this room once the user has registered and logged in
|
||||||
@ -1489,7 +1489,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
{ leading: false, trailing: true },
|
{ leading: false, trailing: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
private onMessageListScroll = () => {
|
private onMessageListScroll = (): void => {
|
||||||
if (this.messagePanel.isAtEndOfLiveTimeline()) {
|
if (this.messagePanel.isAtEndOfLiveTimeline()) {
|
||||||
this.setState({
|
this.setState({
|
||||||
numUnreadMessages: 0,
|
numUnreadMessages: 0,
|
||||||
@ -1504,7 +1504,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
this.updateVisibleDecryptionFailures();
|
this.updateVisibleDecryptionFailures();
|
||||||
};
|
};
|
||||||
|
|
||||||
private resetJumpToEvent = (eventId?: string) => {
|
private resetJumpToEvent = (eventId?: string): void => {
|
||||||
if (
|
if (
|
||||||
this.state.initialEventId &&
|
this.state.initialEventId &&
|
||||||
this.state.initialEventScrollIntoView &&
|
this.state.initialEventScrollIntoView &&
|
||||||
@ -1523,7 +1523,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private injectSticker(url: string, info: object, text: string, threadId: string | null) {
|
private injectSticker(url: string, info: object, text: string, threadId: string | null): void {
|
||||||
if (this.context.client.isGuest()) {
|
if (this.context.client.isGuest()) {
|
||||||
dis.dispatch({ action: "require_registration" });
|
dis.dispatch({ action: "require_registration" });
|
||||||
return;
|
return;
|
||||||
@ -1539,7 +1539,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private onSearch = (term: string, scope: SearchScope) => {
|
private onSearch = (term: string, scope: SearchScope): void => {
|
||||||
const roomId = scope === SearchScope.Room ? this.state.room.roomId : undefined;
|
const roomId = scope === SearchScope.Room ? this.state.room.roomId : undefined;
|
||||||
debuglog("sending search request");
|
debuglog("sending search request");
|
||||||
const abortController = new AbortController();
|
const abortController = new AbortController();
|
||||||
@ -1569,21 +1569,21 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
private onAppsClick = () => {
|
private onAppsClick = (): void => {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: "appsDrawer",
|
action: "appsDrawer",
|
||||||
show: !this.state.showApps,
|
show: !this.state.showApps,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
private onForgetClick = () => {
|
private onForgetClick = (): void => {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: "forget_room",
|
action: "forget_room",
|
||||||
room_id: this.state.room.roomId,
|
room_id: this.state.room.roomId,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
private onRejectButtonClicked = () => {
|
private onRejectButtonClicked = (): void => {
|
||||||
this.setState({
|
this.setState({
|
||||||
rejecting: true,
|
rejecting: true,
|
||||||
});
|
});
|
||||||
@ -1611,7 +1611,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
private onRejectAndIgnoreClick = async () => {
|
private onRejectAndIgnoreClick = async (): Promise<void> => {
|
||||||
this.setState({
|
this.setState({
|
||||||
rejecting: true,
|
rejecting: true,
|
||||||
});
|
});
|
||||||
@ -1644,7 +1644,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private onRejectThreepidInviteButtonClicked = () => {
|
private onRejectThreepidInviteButtonClicked = (): void => {
|
||||||
// We can reject 3pid invites in the same way that we accept them,
|
// We can reject 3pid invites in the same way that we accept them,
|
||||||
// using /leave rather than /join. In the short term though, we
|
// using /leave rather than /join. In the short term though, we
|
||||||
// just ignore them.
|
// just ignore them.
|
||||||
@ -1652,7 +1652,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
dis.fire(Action.ViewRoomDirectory);
|
dis.fire(Action.ViewRoomDirectory);
|
||||||
};
|
};
|
||||||
|
|
||||||
private onSearchClick = () => {
|
private onSearchClick = (): void => {
|
||||||
this.setState({
|
this.setState({
|
||||||
timelineRenderingType:
|
timelineRenderingType:
|
||||||
this.state.timelineRenderingType === TimelineRenderingType.Search
|
this.state.timelineRenderingType === TimelineRenderingType.Search
|
||||||
@ -1674,7 +1674,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// jump down to the bottom of this room, where new events are arriving
|
// jump down to the bottom of this room, where new events are arriving
|
||||||
private jumpToLiveTimeline = () => {
|
private jumpToLiveTimeline = (): void => {
|
||||||
if (this.state.initialEventId && this.state.isInitialEventHighlighted) {
|
if (this.state.initialEventId && this.state.isInitialEventHighlighted) {
|
||||||
// If we were viewing a highlighted event, firing view_room without
|
// If we were viewing a highlighted event, firing view_room without
|
||||||
// an event will take care of both clearing the URL fragment and
|
// an event will take care of both clearing the URL fragment and
|
||||||
@ -1692,18 +1692,18 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// jump up to wherever our read marker is
|
// jump up to wherever our read marker is
|
||||||
private jumpToReadMarker = () => {
|
private jumpToReadMarker = (): void => {
|
||||||
this.messagePanel.jumpToReadMarker();
|
this.messagePanel.jumpToReadMarker();
|
||||||
};
|
};
|
||||||
|
|
||||||
// update the read marker to match the read-receipt
|
// update the read marker to match the read-receipt
|
||||||
private forgetReadMarker = (ev) => {
|
private forgetReadMarker = (ev): void => {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
this.messagePanel.forgetReadMarker();
|
this.messagePanel.forgetReadMarker();
|
||||||
};
|
};
|
||||||
|
|
||||||
// decide whether or not the top 'unread messages' bar should be shown
|
// decide whether or not the top 'unread messages' bar should be shown
|
||||||
private updateTopUnreadMessagesBar = () => {
|
private updateTopUnreadMessagesBar = (): void => {
|
||||||
if (!this.messagePanel) {
|
if (!this.messagePanel) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1754,12 +1754,12 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private onStatusBarVisible = () => {
|
private onStatusBarVisible = (): void => {
|
||||||
if (this.unmounted || this.state.statusBarVisible) return;
|
if (this.unmounted || this.state.statusBarVisible) return;
|
||||||
this.setState({ statusBarVisible: true });
|
this.setState({ statusBarVisible: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
private onStatusBarHidden = () => {
|
private onStatusBarHidden = (): void => {
|
||||||
// This is currently not desired as it is annoying if it keeps expanding and collapsing
|
// This is currently not desired as it is annoying if it keeps expanding and collapsing
|
||||||
if (this.unmounted || !this.state.statusBarVisible) return;
|
if (this.unmounted || !this.state.statusBarVisible) return;
|
||||||
this.setState({ statusBarVisible: false });
|
this.setState({ statusBarVisible: false });
|
||||||
@ -1770,7 +1770,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
*
|
*
|
||||||
* We pass it down to the scroll panel.
|
* We pass it down to the scroll panel.
|
||||||
*/
|
*/
|
||||||
public handleScrollKey = (ev) => {
|
public handleScrollKey = (ev): void => {
|
||||||
let panel: ScrollPanel | TimelinePanel;
|
let panel: ScrollPanel | TimelinePanel;
|
||||||
if (this.searchResultsPanel.current) {
|
if (this.searchResultsPanel.current) {
|
||||||
panel = this.searchResultsPanel.current;
|
panel = this.searchResultsPanel.current;
|
||||||
@ -1793,24 +1793,24 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
|
|
||||||
// this has to be a proper method rather than an unnamed function,
|
// this has to be a proper method rather than an unnamed function,
|
||||||
// otherwise react calls it with null on each update.
|
// otherwise react calls it with null on each update.
|
||||||
private gatherTimelinePanelRef = (r) => {
|
private gatherTimelinePanelRef = (r): void => {
|
||||||
this.messagePanel = r;
|
this.messagePanel = r;
|
||||||
};
|
};
|
||||||
|
|
||||||
private getOldRoom() {
|
private getOldRoom(): Room | null {
|
||||||
const createEvent = this.state.room.currentState.getStateEvents(EventType.RoomCreate, "");
|
const createEvent = this.state.room.currentState.getStateEvents(EventType.RoomCreate, "");
|
||||||
if (!createEvent || !createEvent.getContent()["predecessor"]) return null;
|
if (!createEvent || !createEvent.getContent()["predecessor"]) return null;
|
||||||
|
|
||||||
return this.context.client.getRoom(createEvent.getContent()["predecessor"]["room_id"]);
|
return this.context.client.getRoom(createEvent.getContent()["predecessor"]["room_id"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getHiddenHighlightCount() {
|
public getHiddenHighlightCount(): number {
|
||||||
const oldRoom = this.getOldRoom();
|
const oldRoom = this.getOldRoom();
|
||||||
if (!oldRoom) return 0;
|
if (!oldRoom) return 0;
|
||||||
return oldRoom.getUnreadNotificationCount(NotificationCountType.Highlight);
|
return oldRoom.getUnreadNotificationCount(NotificationCountType.Highlight);
|
||||||
}
|
}
|
||||||
|
|
||||||
public onHiddenHighlightsClick = () => {
|
public onHiddenHighlightsClick = (): void => {
|
||||||
const oldRoom = this.getOldRoom();
|
const oldRoom = this.getOldRoom();
|
||||||
if (!oldRoom) return;
|
if (!oldRoom) return;
|
||||||
dis.dispatch<ViewRoomPayload>({
|
dis.dispatch<ViewRoomPayload>({
|
||||||
@ -1826,7 +1826,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private onFileDrop = (dataTransfer: DataTransfer) =>
|
private onFileDrop = (dataTransfer: DataTransfer): Promise<void> =>
|
||||||
ContentMessages.sharedInstance().sendContentListToRoom(
|
ContentMessages.sharedInstance().sendContentListToRoom(
|
||||||
Array.from(dataTransfer.files),
|
Array.from(dataTransfer.files),
|
||||||
this.state.room?.roomId ?? this.state.roomId,
|
this.state.room?.roomId ?? this.state.roomId,
|
||||||
@ -1869,7 +1869,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render(): JSX.Element {
|
||||||
if (this.state.room instanceof LocalRoom) {
|
if (this.state.room instanceof LocalRoom) {
|
||||||
if (this.state.room.state === LocalRoomState.CREATING) {
|
if (this.state.room.state === LocalRoomState.CREATING) {
|
||||||
return this.renderLocalRoomCreateLoader();
|
return this.renderLocalRoomCreateLoader();
|
||||||
|
@ -35,7 +35,7 @@ const UNFILL_REQUEST_DEBOUNCE_MS = 200;
|
|||||||
// much while the content loads.
|
// much while the content loads.
|
||||||
const PAGE_SIZE = 400;
|
const PAGE_SIZE = 400;
|
||||||
|
|
||||||
const debuglog = (...args: any[]) => {
|
const debuglog = (...args: any[]): void => {
|
||||||
if (SettingsStore.getValue("debug_scroll_panel")) {
|
if (SettingsStore.getValue("debug_scroll_panel")) {
|
||||||
logger.log.call(console, "ScrollPanel debuglog:", ...args);
|
logger.log.call(console, "ScrollPanel debuglog:", ...args);
|
||||||
}
|
}
|
||||||
@ -227,14 +227,14 @@ export default class ScrollPanel extends React.Component<IProps> {
|
|||||||
this.props.resizeNotifier?.removeListener("middlePanelResizedNoisy", this.onResize);
|
this.props.resizeNotifier?.removeListener("middlePanelResizedNoisy", this.onResize);
|
||||||
}
|
}
|
||||||
|
|
||||||
private onScroll = (ev: Event | React.UIEvent): void => {
|
private onScroll = (ev: Event): void => {
|
||||||
// skip scroll events caused by resizing
|
// skip scroll events caused by resizing
|
||||||
if (this.props.resizeNotifier && this.props.resizeNotifier.isResizing) return;
|
if (this.props.resizeNotifier && this.props.resizeNotifier.isResizing) return;
|
||||||
debuglog("onScroll called past resize gate; scroll node top:", this.getScrollNode().scrollTop);
|
debuglog("onScroll called past resize gate; scroll node top:", this.getScrollNode().scrollTop);
|
||||||
this.scrollTimeout.restart();
|
this.scrollTimeout.restart();
|
||||||
this.saveScrollState();
|
this.saveScrollState();
|
||||||
this.updatePreventShrinking();
|
this.updatePreventShrinking();
|
||||||
this.props.onScroll?.(ev as Event);
|
this.props.onScroll?.(ev);
|
||||||
// noinspection JSIgnoredPromiseFromCall
|
// noinspection JSIgnoredPromiseFromCall
|
||||||
this.checkFillState();
|
this.checkFillState();
|
||||||
};
|
};
|
||||||
@ -587,7 +587,7 @@ export default class ScrollPanel extends React.Component<IProps> {
|
|||||||
* Scroll up/down in response to a scroll key
|
* Scroll up/down in response to a scroll key
|
||||||
* @param {object} ev the keyboard event
|
* @param {object} ev the keyboard event
|
||||||
*/
|
*/
|
||||||
public handleScrollKey = (ev: KeyboardEvent) => {
|
public handleScrollKey = (ev: KeyboardEvent): void => {
|
||||||
const roomAction = getKeyBindingsManager().getRoomAction(ev);
|
const roomAction = getKeyBindingsManager().getRoomAction(ev);
|
||||||
switch (roomAction) {
|
switch (roomAction) {
|
||||||
case KeyBindingAction.ScrollUp:
|
case KeyBindingAction.ScrollUp:
|
||||||
@ -853,7 +853,7 @@ export default class ScrollPanel extends React.Component<IProps> {
|
|||||||
return this.divScroll;
|
return this.divScroll;
|
||||||
}
|
}
|
||||||
|
|
||||||
private collectScroll = (divScroll: HTMLDivElement) => {
|
private collectScroll = (divScroll: HTMLDivElement): void => {
|
||||||
this.divScroll = divScroll;
|
this.divScroll = divScroll;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -114,12 +114,12 @@ const Tile: React.FC<ITileProps> = ({
|
|||||||
const [onFocus, isActive, ref] = useRovingTabIndex();
|
const [onFocus, isActive, ref] = useRovingTabIndex();
|
||||||
const [busy, setBusy] = useState(false);
|
const [busy, setBusy] = useState(false);
|
||||||
|
|
||||||
const onPreviewClick = (ev: ButtonEvent) => {
|
const onPreviewClick = (ev: ButtonEvent): void => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
onViewRoomClick();
|
onViewRoomClick();
|
||||||
};
|
};
|
||||||
const onJoinClick = async (ev: ButtonEvent) => {
|
const onJoinClick = async (ev: ButtonEvent): Promise<void> => {
|
||||||
setBusy(true);
|
setBusy(true);
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
@ -271,7 +271,7 @@ const Tile: React.FC<ITileProps> = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (showChildren) {
|
if (showChildren) {
|
||||||
const onChildrenKeyDown = (e) => {
|
const onChildrenKeyDown = (e): void => {
|
||||||
const action = getKeyBindingsManager().getAccessibilityAction(e);
|
const action = getKeyBindingsManager().getAccessibilityAction(e);
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case KeyBindingAction.ArrowLeft:
|
case KeyBindingAction.ArrowLeft:
|
||||||
@ -439,7 +439,7 @@ const toLocalRoom = (cli: MatrixClient, room: IHierarchyRoom): IHierarchyRoom =>
|
|||||||
return room;
|
return room;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const HierarchyLevel = ({
|
export const HierarchyLevel: React.FC<IHierarchyLevelProps> = ({
|
||||||
root,
|
root,
|
||||||
roomSet,
|
roomSet,
|
||||||
hierarchy,
|
hierarchy,
|
||||||
@ -448,7 +448,7 @@ export const HierarchyLevel = ({
|
|||||||
onViewRoomClick,
|
onViewRoomClick,
|
||||||
onJoinRoomClick,
|
onJoinRoomClick,
|
||||||
onToggleClick,
|
onToggleClick,
|
||||||
}: IHierarchyLevelProps) => {
|
}) => {
|
||||||
const cli = useContext(MatrixClientContext);
|
const cli = useContext(MatrixClientContext);
|
||||||
const space = cli.getRoom(root.room_id);
|
const space = cli.getRoom(root.room_id);
|
||||||
const hasPermissions = space?.currentState.maySendStateEvent(EventType.SpaceChild, cli.getUserId());
|
const hasPermissions = space?.currentState.maySendStateEvent(EventType.SpaceChild, cli.getUserId());
|
||||||
@ -553,7 +553,7 @@ export const useRoomHierarchy = (
|
|||||||
});
|
});
|
||||||
|
|
||||||
const loadMore = useCallback(
|
const loadMore = useCallback(
|
||||||
async (pageSize?: number) => {
|
async (pageSize?: number): Promise<void> => {
|
||||||
if (hierarchy.loading || !hierarchy.canLoadMore || hierarchy.noSupport || error) return;
|
if (hierarchy.loading || !hierarchy.canLoadMore || hierarchy.noSupport || error) return;
|
||||||
await hierarchy.load(pageSize).catch(setError);
|
await hierarchy.load(pageSize).catch(setError);
|
||||||
setRooms(hierarchy.rooms);
|
setRooms(hierarchy.rooms);
|
||||||
@ -578,8 +578,8 @@ export const useRoomHierarchy = (
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const useIntersectionObserver = (callback: () => void) => {
|
const useIntersectionObserver = (callback: () => void): ((element: HTMLDivElement) => void) => {
|
||||||
const handleObserver = (entries: IntersectionObserverEntry[]) => {
|
const handleObserver = (entries: IntersectionObserverEntry[]): void => {
|
||||||
const target = entries[0];
|
const target = entries[0];
|
||||||
if (target.isIntersecting) {
|
if (target.isIntersecting) {
|
||||||
callback();
|
callback();
|
||||||
@ -610,7 +610,7 @@ interface IManageButtonsProps {
|
|||||||
setError: Dispatch<SetStateAction<string>>;
|
setError: Dispatch<SetStateAction<string>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ManageButtons = ({ hierarchy, selected, setSelected, setError }: IManageButtonsProps) => {
|
const ManageButtons: React.FC<IManageButtonsProps> = ({ hierarchy, selected, setSelected, setError }) => {
|
||||||
const cli = useContext(MatrixClientContext);
|
const cli = useContext(MatrixClientContext);
|
||||||
|
|
||||||
const [removing, setRemoving] = useState(false);
|
const [removing, setRemoving] = useState(false);
|
||||||
@ -645,7 +645,7 @@ const ManageButtons = ({ hierarchy, selected, setSelected, setError }: IManageBu
|
|||||||
<>
|
<>
|
||||||
<Button
|
<Button
|
||||||
{...props}
|
{...props}
|
||||||
onClick={async () => {
|
onClick={async (): Promise<void> => {
|
||||||
setRemoving(true);
|
setRemoving(true);
|
||||||
try {
|
try {
|
||||||
const userId = cli.getUserId();
|
const userId = cli.getUserId();
|
||||||
@ -680,7 +680,7 @@ const ManageButtons = ({ hierarchy, selected, setSelected, setError }: IManageBu
|
|||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
{...props}
|
{...props}
|
||||||
onClick={async () => {
|
onClick={async (): Promise<void> => {
|
||||||
setSaving(true);
|
setSaving(true);
|
||||||
try {
|
try {
|
||||||
for (const [parentId, childId] of selectedRelations) {
|
for (const [parentId, childId] of selectedRelations) {
|
||||||
@ -713,7 +713,7 @@ const ManageButtons = ({ hierarchy, selected, setSelected, setError }: IManageBu
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const SpaceHierarchy = ({ space, initialText = "", showRoom, additionalButtons }: IProps) => {
|
const SpaceHierarchy: React.FC<IProps> = ({ space, initialText = "", showRoom, additionalButtons }) => {
|
||||||
const cli = useContext(MatrixClientContext);
|
const cli = useContext(MatrixClientContext);
|
||||||
const [query, setQuery] = useState(initialText);
|
const [query, setQuery] = useState(initialText);
|
||||||
|
|
||||||
|
@ -103,7 +103,7 @@ enum Phase {
|
|||||||
PrivateExistingRooms,
|
PrivateExistingRooms,
|
||||||
}
|
}
|
||||||
|
|
||||||
const SpaceLandingAddButton = ({ space }) => {
|
const SpaceLandingAddButton: React.FC<{ space: Room }> = ({ space }) => {
|
||||||
const [menuDisplayed, handle, openMenu, closeMenu] = useContextMenu();
|
const [menuDisplayed, handle, openMenu, closeMenu] = useContextMenu();
|
||||||
const canCreateRoom = shouldShowComponent(UIComponent.CreateRooms);
|
const canCreateRoom = shouldShowComponent(UIComponent.CreateRooms);
|
||||||
const canCreateSpace = shouldShowComponent(UIComponent.CreateSpaces);
|
const canCreateSpace = shouldShowComponent(UIComponent.CreateSpaces);
|
||||||
@ -128,7 +128,7 @@ const SpaceLandingAddButton = ({ space }) => {
|
|||||||
<IconizedContextMenuOption
|
<IconizedContextMenuOption
|
||||||
label={_t("New room")}
|
label={_t("New room")}
|
||||||
iconClassName="mx_RoomList_iconNewRoom"
|
iconClassName="mx_RoomList_iconNewRoom"
|
||||||
onClick={async (e) => {
|
onClick={async (e): Promise<void> => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
closeMenu();
|
closeMenu();
|
||||||
@ -143,7 +143,7 @@ const SpaceLandingAddButton = ({ space }) => {
|
|||||||
<IconizedContextMenuOption
|
<IconizedContextMenuOption
|
||||||
label={_t("New video room")}
|
label={_t("New video room")}
|
||||||
iconClassName="mx_RoomList_iconNewVideoRoom"
|
iconClassName="mx_RoomList_iconNewVideoRoom"
|
||||||
onClick={async (e) => {
|
onClick={async (e): Promise<void> => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
closeMenu();
|
closeMenu();
|
||||||
@ -210,7 +210,7 @@ const SpaceLandingAddButton = ({ space }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const SpaceLanding = ({ space }: { space: Room }) => {
|
const SpaceLanding: React.FC<{ space: Room }> = ({ space }) => {
|
||||||
const cli = useContext(MatrixClientContext);
|
const cli = useContext(MatrixClientContext);
|
||||||
const myMembership = useMyRoomMembership(space);
|
const myMembership = useMyRoomMembership(space);
|
||||||
const userId = cli.getUserId();
|
const userId = cli.getUserId();
|
||||||
@ -259,7 +259,7 @@ const SpaceLanding = ({ space }: { space: Room }) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const onMembersClick = () => {
|
const onMembersClick = (): void => {
|
||||||
RightPanelStore.instance.setCard({ phase: RightPanelPhases.SpaceMemberList });
|
RightPanelStore.instance.setCard({ phase: RightPanelPhases.SpaceMemberList });
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -297,7 +297,12 @@ const SpaceLanding = ({ space }: { space: Room }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const SpaceSetupFirstRooms = ({ space, title, description, onFinished }) => {
|
const SpaceSetupFirstRooms: React.FC<{
|
||||||
|
space: Room;
|
||||||
|
title: string;
|
||||||
|
description: JSX.Element;
|
||||||
|
onFinished(firstRoomId?: string): void;
|
||||||
|
}> = ({ space, title, description, onFinished }) => {
|
||||||
const [busy, setBusy] = useState(false);
|
const [busy, setBusy] = useState(false);
|
||||||
const [error, setError] = useState("");
|
const [error, setError] = useState("");
|
||||||
const numFields = 3;
|
const numFields = 3;
|
||||||
@ -321,7 +326,7 @@ const SpaceSetupFirstRooms = ({ space, title, description, onFinished }) => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const onNextClick = async (ev: ButtonEvent) => {
|
const onNextClick = async (ev: ButtonEvent): Promise<void> => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
if (busy) return;
|
if (busy) return;
|
||||||
setError("");
|
setError("");
|
||||||
@ -354,7 +359,7 @@ const SpaceSetupFirstRooms = ({ space, title, description, onFinished }) => {
|
|||||||
setBusy(false);
|
setBusy(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
let onClick = (ev: ButtonEvent) => {
|
let onClick = (ev: ButtonEvent): void => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
onFinished();
|
onFinished();
|
||||||
};
|
};
|
||||||
@ -389,7 +394,10 @@ const SpaceSetupFirstRooms = ({ space, title, description, onFinished }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const SpaceAddExistingRooms = ({ space, onFinished }) => {
|
const SpaceAddExistingRooms: React.FC<{
|
||||||
|
space: Room;
|
||||||
|
onFinished(): void;
|
||||||
|
}> = ({ space, onFinished }) => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h1>{_t("What do you want to organise?")}</h1>
|
<h1>{_t("What do you want to organise?")}</h1>
|
||||||
@ -420,7 +428,12 @@ interface ISpaceSetupPublicShareProps extends Pick<IProps & IState, "justCreated
|
|||||||
onFinished(): void;
|
onFinished(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SpaceSetupPublicShare = ({ justCreatedOpts, space, onFinished, firstRoomId }: ISpaceSetupPublicShareProps) => {
|
const SpaceSetupPublicShare: React.FC<ISpaceSetupPublicShareProps> = ({
|
||||||
|
justCreatedOpts,
|
||||||
|
space,
|
||||||
|
onFinished,
|
||||||
|
firstRoomId,
|
||||||
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div className="mx_SpaceRoomView_publicShare">
|
<div className="mx_SpaceRoomView_publicShare">
|
||||||
<h1>
|
<h1>
|
||||||
@ -443,7 +456,11 @@ const SpaceSetupPublicShare = ({ justCreatedOpts, space, onFinished, firstRoomId
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const SpaceSetupPrivateScope = ({ space, justCreatedOpts, onFinished }) => {
|
const SpaceSetupPrivateScope: React.FC<{
|
||||||
|
space: Room;
|
||||||
|
justCreatedOpts: IOpts;
|
||||||
|
onFinished(createRooms: boolean): void;
|
||||||
|
}> = ({ space, justCreatedOpts, onFinished }) => {
|
||||||
return (
|
return (
|
||||||
<div className="mx_SpaceRoomView_privateScope">
|
<div className="mx_SpaceRoomView_privateScope">
|
||||||
<h1>{_t("Who are you working with?")}</h1>
|
<h1>{_t("Who are you working with?")}</h1>
|
||||||
@ -485,7 +502,10 @@ const validateEmailRules = withValidation({
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
const SpaceSetupPrivateInvite = ({ space, onFinished }) => {
|
const SpaceSetupPrivateInvite: React.FC<{
|
||||||
|
space: Room;
|
||||||
|
onFinished(): void;
|
||||||
|
}> = ({ space, onFinished }) => {
|
||||||
const [busy, setBusy] = useState(false);
|
const [busy, setBusy] = useState(false);
|
||||||
const [error, setError] = useState("");
|
const [error, setError] = useState("");
|
||||||
const numFields = 3;
|
const numFields = 3;
|
||||||
@ -501,7 +521,7 @@ const SpaceSetupPrivateInvite = ({ space, onFinished }) => {
|
|||||||
label={_t("Email address")}
|
label={_t("Email address")}
|
||||||
placeholder={_t("Email")}
|
placeholder={_t("Email")}
|
||||||
value={emailAddresses[i]}
|
value={emailAddresses[i]}
|
||||||
onChange={(ev) => setEmailAddress(i, ev.target.value)}
|
onChange={(ev: React.ChangeEvent<HTMLInputElement>) => setEmailAddress(i, ev.target.value)}
|
||||||
ref={fieldRefs[i]}
|
ref={fieldRefs[i]}
|
||||||
onValidate={validateEmailRules}
|
onValidate={validateEmailRules}
|
||||||
autoFocus={i === 0}
|
autoFocus={i === 0}
|
||||||
@ -510,7 +530,7 @@ const SpaceSetupPrivateInvite = ({ space, onFinished }) => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const onNextClick = async (ev) => {
|
const onNextClick = async (ev: ButtonEvent): Promise<void> => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
if (busy) return;
|
if (busy) return;
|
||||||
setError("");
|
setError("");
|
||||||
@ -548,7 +568,7 @@ const SpaceSetupPrivateInvite = ({ space, onFinished }) => {
|
|||||||
setBusy(false);
|
setBusy(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
let onClick = (ev) => {
|
let onClick = (ev: ButtonEvent): void => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
onFinished();
|
onFinished();
|
||||||
};
|
};
|
||||||
@ -642,29 +662,29 @@ export default class SpaceRoomView extends React.PureComponent<IProps, IState> {
|
|||||||
RightPanelStore.instance.on(UPDATE_EVENT, this.onRightPanelStoreUpdate);
|
RightPanelStore.instance.on(UPDATE_EVENT, this.onRightPanelStoreUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount() {
|
public componentDidMount(): void {
|
||||||
this.context.on(RoomEvent.MyMembership, this.onMyMembership);
|
this.context.on(RoomEvent.MyMembership, this.onMyMembership);
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentWillUnmount() {
|
public componentWillUnmount(): void {
|
||||||
defaultDispatcher.unregister(this.dispatcherRef);
|
defaultDispatcher.unregister(this.dispatcherRef);
|
||||||
RightPanelStore.instance.off(UPDATE_EVENT, this.onRightPanelStoreUpdate);
|
RightPanelStore.instance.off(UPDATE_EVENT, this.onRightPanelStoreUpdate);
|
||||||
this.context.off(RoomEvent.MyMembership, this.onMyMembership);
|
this.context.off(RoomEvent.MyMembership, this.onMyMembership);
|
||||||
}
|
}
|
||||||
|
|
||||||
private onMyMembership = (room: Room, myMembership: string) => {
|
private onMyMembership = (room: Room, myMembership: string): void => {
|
||||||
if (room.roomId === this.props.space.roomId) {
|
if (room.roomId === this.props.space.roomId) {
|
||||||
this.setState({ myMembership });
|
this.setState({ myMembership });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private onRightPanelStoreUpdate = () => {
|
private onRightPanelStoreUpdate = (): void => {
|
||||||
this.setState({
|
this.setState({
|
||||||
showRightPanel: RightPanelStore.instance.isOpenForRoom(this.props.space.roomId),
|
showRightPanel: RightPanelStore.instance.isOpenForRoom(this.props.space.roomId),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
private onAction = (payload: ActionPayload) => {
|
private onAction = (payload: ActionPayload): void => {
|
||||||
if (payload.action === Action.ViewRoom && payload.room_id === this.props.space.roomId) {
|
if (payload.action === Action.ViewRoom && payload.room_id === this.props.space.roomId) {
|
||||||
this.setState({ phase: Phase.Landing });
|
this.setState({ phase: Phase.Landing });
|
||||||
return;
|
return;
|
||||||
@ -698,7 +718,7 @@ export default class SpaceRoomView extends React.PureComponent<IProps, IState> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private goToFirstRoom = async () => {
|
private goToFirstRoom = async (): Promise<void> => {
|
||||||
if (this.state.firstRoomId) {
|
if (this.state.firstRoomId) {
|
||||||
defaultDispatcher.dispatch<ViewRoomPayload>({
|
defaultDispatcher.dispatch<ViewRoomPayload>({
|
||||||
action: Action.ViewRoom,
|
action: Action.ViewRoom,
|
||||||
@ -711,7 +731,7 @@ export default class SpaceRoomView extends React.PureComponent<IProps, IState> {
|
|||||||
this.setState({ phase: Phase.Landing });
|
this.setState({ phase: Phase.Landing });
|
||||||
};
|
};
|
||||||
|
|
||||||
private renderBody() {
|
private renderBody(): JSX.Element {
|
||||||
switch (this.state.phase) {
|
switch (this.state.phase) {
|
||||||
case Phase.Landing:
|
case Phase.Landing:
|
||||||
if (this.state.myMembership === "join") {
|
if (this.state.myMembership === "join") {
|
||||||
@ -794,7 +814,7 @@ export default class SpaceRoomView extends React.PureComponent<IProps, IState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render(): JSX.Element {
|
||||||
const rightPanel =
|
const rightPanel =
|
||||||
this.state.showRightPanel && this.state.phase === Phase.Landing ? (
|
this.state.showRightPanel && this.state.phase === Phase.Landing ? (
|
||||||
<RightPanel room={this.props.space} resizeNotifier={this.props.resizeNotifier} />
|
<RightPanel room={this.props.space} resizeNotifier={this.props.resizeNotifier} />
|
||||||
|
@ -19,7 +19,7 @@ interface Props extends DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLEleme
|
|||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function SplashPage({ children, className, ...other }: Props) {
|
export default function SplashPage({ children, className, ...other }: Props): JSX.Element {
|
||||||
const classes = classNames(className, "mx_SplashPage");
|
const classes = classNames(className, "mx_SplashPage");
|
||||||
return (
|
return (
|
||||||
<main {...other} className={classes}>
|
<main {...other} className={classes}>
|
||||||
|
@ -86,7 +86,7 @@ export default class TabbedView extends React.Component<IProps, IState> {
|
|||||||
* @param {Tab} tab the tab to show
|
* @param {Tab} tab the tab to show
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
private setActiveTab(tab: Tab) {
|
private setActiveTab(tab: Tab): void {
|
||||||
// make sure this tab is still in available tabs
|
// make sure this tab is still in available tabs
|
||||||
if (!!this.getTabById(tab.id)) {
|
if (!!this.getTabById(tab.id)) {
|
||||||
if (this.props.onChange) this.props.onChange(tab.id);
|
if (this.props.onChange) this.props.onChange(tab.id);
|
||||||
@ -96,7 +96,7 @@ export default class TabbedView extends React.Component<IProps, IState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderTabLabel(tab: Tab) {
|
private renderTabLabel(tab: Tab): JSX.Element {
|
||||||
let classes = "mx_TabbedView_tabLabel ";
|
let classes = "mx_TabbedView_tabLabel ";
|
||||||
|
|
||||||
if (this.state.activeTabId === tab.id) classes += "mx_TabbedView_tabLabel_active";
|
if (this.state.activeTabId === tab.id) classes += "mx_TabbedView_tabLabel_active";
|
||||||
@ -106,7 +106,7 @@ export default class TabbedView extends React.Component<IProps, IState> {
|
|||||||
tabIcon = <span className={`mx_TabbedView_maskedIcon ${tab.icon}`} />;
|
tabIcon = <span className={`mx_TabbedView_maskedIcon ${tab.icon}`} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const onClickHandler = () => this.setActiveTab(tab);
|
const onClickHandler = (): void => this.setActiveTab(tab);
|
||||||
|
|
||||||
const label = _t(tab.label);
|
const label = _t(tab.label);
|
||||||
return (
|
return (
|
||||||
|
@ -61,15 +61,12 @@ type ThreadPanelHeaderOption = {
|
|||||||
key: ThreadFilterType;
|
key: ThreadFilterType;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ThreadPanelHeaderFilterOptionItem = ({
|
export const ThreadPanelHeaderFilterOptionItem: React.FC<
|
||||||
label,
|
ThreadPanelHeaderOption & {
|
||||||
description,
|
onClick: () => void;
|
||||||
onClick,
|
isSelected: boolean;
|
||||||
isSelected,
|
}
|
||||||
}: ThreadPanelHeaderOption & {
|
> = ({ label, description, onClick, isSelected }) => {
|
||||||
onClick: () => void;
|
|
||||||
isSelected: boolean;
|
|
||||||
}) => {
|
|
||||||
return (
|
return (
|
||||||
<MenuItemRadio active={isSelected} className="mx_ThreadPanel_Header_FilterOptionItem" onClick={onClick}>
|
<MenuItemRadio active={isSelected} className="mx_ThreadPanel_Header_FilterOptionItem" onClick={onClick}>
|
||||||
<span>{label}</span>
|
<span>{label}</span>
|
||||||
@ -78,15 +75,11 @@ export const ThreadPanelHeaderFilterOptionItem = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ThreadPanelHeader = ({
|
export const ThreadPanelHeader: React.FC<{
|
||||||
filterOption,
|
|
||||||
setFilterOption,
|
|
||||||
empty,
|
|
||||||
}: {
|
|
||||||
filterOption: ThreadFilterType;
|
filterOption: ThreadFilterType;
|
||||||
setFilterOption: (filterOption: ThreadFilterType) => void;
|
setFilterOption: (filterOption: ThreadFilterType) => void;
|
||||||
empty: boolean;
|
empty: boolean;
|
||||||
}) => {
|
}> = ({ filterOption, setFilterOption, empty }) => {
|
||||||
const [menuDisplayed, button, openMenu, closeMenu] = useContextMenu<HTMLElement>();
|
const [menuDisplayed, button, openMenu, closeMenu] = useContextMenu<HTMLElement>();
|
||||||
const options: readonly ThreadPanelHeaderOption[] = [
|
const options: readonly ThreadPanelHeaderOption[] = [
|
||||||
{
|
{
|
||||||
|
@ -137,7 +137,7 @@ export default class ThreadView extends React.Component<IProps, IState> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidUpdate(prevProps) {
|
public componentDidUpdate(prevProps): void {
|
||||||
if (prevProps.mxEvent !== this.props.mxEvent) {
|
if (prevProps.mxEvent !== this.props.mxEvent) {
|
||||||
this.setupThread(this.props.mxEvent);
|
this.setupThread(this.props.mxEvent);
|
||||||
}
|
}
|
||||||
@ -192,7 +192,7 @@ export default class ThreadView extends React.Component<IProps, IState> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private setupThread = (mxEv: MatrixEvent) => {
|
private setupThread = (mxEv: MatrixEvent): void => {
|
||||||
let thread = this.props.room.getThread(mxEv.getId());
|
let thread = this.props.room.getThread(mxEv.getId());
|
||||||
if (!thread) {
|
if (!thread) {
|
||||||
thread = this.props.room.createThread(mxEv.getId(), mxEv, [mxEv], true);
|
thread = this.props.room.createThread(mxEv.getId(), mxEv, [mxEv], true);
|
||||||
@ -200,7 +200,7 @@ export default class ThreadView extends React.Component<IProps, IState> {
|
|||||||
this.updateThread(thread);
|
this.updateThread(thread);
|
||||||
};
|
};
|
||||||
|
|
||||||
private onNewThread = (thread: Thread) => {
|
private onNewThread = (thread: Thread): void => {
|
||||||
if (thread.id === this.props.mxEvent.getId()) {
|
if (thread.id === this.props.mxEvent.getId()) {
|
||||||
this.setupThread(this.props.mxEvent);
|
this.setupThread(this.props.mxEvent);
|
||||||
}
|
}
|
||||||
@ -218,7 +218,7 @@ export default class ThreadView extends React.Component<IProps, IState> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateThread = (thread?: Thread) => {
|
private updateThread = (thread?: Thread): void => {
|
||||||
if (this.state.thread === thread) return;
|
if (this.state.thread === thread) return;
|
||||||
|
|
||||||
this.setupThreadListeners(thread, this.state.thread);
|
this.setupThreadListeners(thread, this.state.thread);
|
||||||
@ -276,7 +276,7 @@ export default class ThreadView extends React.Component<IProps, IState> {
|
|||||||
this.setState({ narrow });
|
this.setState({ narrow });
|
||||||
};
|
};
|
||||||
|
|
||||||
private onKeyDown = (ev: KeyboardEvent) => {
|
private onKeyDown = (ev: KeyboardEvent): void => {
|
||||||
let handled = false;
|
let handled = false;
|
||||||
|
|
||||||
const action = getKeyBindingsManager().getRoomAction(ev);
|
const action = getKeyBindingsManager().getRoomAction(ev);
|
||||||
@ -300,7 +300,7 @@ export default class ThreadView extends React.Component<IProps, IState> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private onFileDrop = (dataTransfer: DataTransfer) => {
|
private onFileDrop = (dataTransfer: DataTransfer): void => {
|
||||||
const roomId = this.props.mxEvent.getRoomId();
|
const roomId = this.props.mxEvent.getRoomId();
|
||||||
if (roomId) {
|
if (roomId) {
|
||||||
ContentMessages.sharedInstance().sendContentListToRoom(
|
ContentMessages.sharedInstance().sendContentListToRoom(
|
||||||
|
@ -31,6 +31,7 @@ import { Thread, ThreadEvent } from "matrix-js-sdk/src/models/thread";
|
|||||||
import { ReceiptType } from "matrix-js-sdk/src/@types/read_receipts";
|
import { ReceiptType } from "matrix-js-sdk/src/@types/read_receipts";
|
||||||
import { MatrixError } from "matrix-js-sdk/src/http-api";
|
import { MatrixError } from "matrix-js-sdk/src/http-api";
|
||||||
import { ReadReceipt } from "matrix-js-sdk/src/models/read-receipt";
|
import { ReadReceipt } from "matrix-js-sdk/src/models/read-receipt";
|
||||||
|
import { Relations } from "matrix-js-sdk/src/models/relations";
|
||||||
|
|
||||||
import SettingsStore from "../../settings/SettingsStore";
|
import SettingsStore from "../../settings/SettingsStore";
|
||||||
import { Layout } from "../../settings/enums/Layout";
|
import { Layout } from "../../settings/enums/Layout";
|
||||||
@ -67,7 +68,7 @@ const READ_MARKER_DEBOUNCE_MS = 100;
|
|||||||
// How far off-screen a decryption failure can be for it to still count as "visible"
|
// How far off-screen a decryption failure can be for it to still count as "visible"
|
||||||
const VISIBLE_DECRYPTION_FAILURE_MARGIN = 100;
|
const VISIBLE_DECRYPTION_FAILURE_MARGIN = 100;
|
||||||
|
|
||||||
const debuglog = (...args: any[]) => {
|
const debuglog = (...args: any[]): void => {
|
||||||
if (SettingsStore.getValue("debug_timeline_panel")) {
|
if (SettingsStore.getValue("debug_timeline_panel")) {
|
||||||
logger.log.call(console, "TimelinePanel debuglog:", ...args);
|
logger.log.call(console, "TimelinePanel debuglog:", ...args);
|
||||||
}
|
}
|
||||||
@ -315,7 +316,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
|
|||||||
this.props.timelineSet.room?.on(ThreadEvent.Update, this.onThreadUpdate);
|
this.props.timelineSet.room?.on(ThreadEvent.Update, this.onThreadUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount() {
|
public componentDidMount(): void {
|
||||||
if (this.props.manageReadReceipts) {
|
if (this.props.manageReadReceipts) {
|
||||||
this.updateReadReceiptOnUserActivity();
|
this.updateReadReceiptOnUserActivity();
|
||||||
}
|
}
|
||||||
@ -325,7 +326,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
|
|||||||
this.initTimeline(this.props);
|
this.initTimeline(this.props);
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidUpdate(prevProps) {
|
public componentDidUpdate(prevProps: Readonly<IProps>): void {
|
||||||
if (prevProps.timelineSet !== this.props.timelineSet) {
|
if (prevProps.timelineSet !== this.props.timelineSet) {
|
||||||
// throw new Error("changing timelineSet on a TimelinePanel is not supported");
|
// throw new Error("changing timelineSet on a TimelinePanel is not supported");
|
||||||
|
|
||||||
@ -360,7 +361,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentWillUnmount() {
|
public componentWillUnmount(): void {
|
||||||
// set a boolean to say we've been unmounted, which any pending
|
// set a boolean to say we've been unmounted, which any pending
|
||||||
// promises can use to throw away their results.
|
// promises can use to throw away their results.
|
||||||
//
|
//
|
||||||
@ -616,7 +617,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
private onMessageListScroll = (e) => {
|
private onMessageListScroll = (e: Event): void => {
|
||||||
this.props.onScroll?.(e);
|
this.props.onScroll?.(e);
|
||||||
if (this.props.manageReadMarkers) {
|
if (this.props.manageReadMarkers) {
|
||||||
this.doManageReadMarkers();
|
this.doManageReadMarkers();
|
||||||
@ -777,7 +778,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public canResetTimeline = () => this.messagePanel?.current?.isAtBottom();
|
public canResetTimeline = (): boolean => this.messagePanel?.current?.isAtBottom();
|
||||||
|
|
||||||
private onRoomRedaction = (ev: MatrixEvent, room: Room): void => {
|
private onRoomRedaction = (ev: MatrixEvent, room: Room): void => {
|
||||||
if (this.unmounted) return;
|
if (this.unmounted) return;
|
||||||
@ -1060,7 +1061,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
|
|||||||
this.state.readMarkerEventId ?? "",
|
this.state.readMarkerEventId ?? "",
|
||||||
sendRRs ? lastReadEvent ?? undefined : undefined, // Public read receipt (could be null)
|
sendRRs ? lastReadEvent ?? undefined : undefined, // Public read receipt (could be null)
|
||||||
lastReadEvent ?? undefined, // Private read receipt (could be null)
|
lastReadEvent ?? undefined, // Private read receipt (could be null)
|
||||||
).catch(async (e) => {
|
).catch(async (e): Promise<void> => {
|
||||||
// /read_markers API is not implemented on this HS, fallback to just RR
|
// /read_markers API is not implemented on this HS, fallback to just RR
|
||||||
if (e.errcode === "M_UNRECOGNIZED" && lastReadEvent) {
|
if (e.errcode === "M_UNRECOGNIZED" && lastReadEvent) {
|
||||||
if (
|
if (
|
||||||
@ -1070,10 +1071,11 @@ class TimelinePanel extends React.Component<IProps, IState> {
|
|||||||
)
|
)
|
||||||
return;
|
return;
|
||||||
try {
|
try {
|
||||||
return await cli.sendReadReceipt(
|
await cli.sendReadReceipt(
|
||||||
lastReadEvent,
|
lastReadEvent,
|
||||||
sendRRs ? ReceiptType.Read : ReceiptType.ReadPrivate,
|
sendRRs ? ReceiptType.Read : ReceiptType.ReadPrivate,
|
||||||
);
|
);
|
||||||
|
return;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(e);
|
logger.error(e);
|
||||||
this.lastRRSentEventId = undefined;
|
this.lastRRSentEventId = undefined;
|
||||||
@ -1314,7 +1316,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
|
|||||||
*
|
*
|
||||||
* We pass it down to the scroll panel.
|
* We pass it down to the scroll panel.
|
||||||
*/
|
*/
|
||||||
public handleScrollKey = (ev) => {
|
public handleScrollKey = (ev: React.KeyboardEvent): void => {
|
||||||
if (!this.messagePanel.current) return;
|
if (!this.messagePanel.current) return;
|
||||||
|
|
||||||
// jump to the live timeline on ctrl-end, rather than the end of the
|
// jump to the live timeline on ctrl-end, rather than the end of the
|
||||||
@ -1342,7 +1344,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private scrollIntoView(eventId?: string, pixelOffset?: number, offsetBase?: number): void {
|
private scrollIntoView(eventId?: string, pixelOffset?: number, offsetBase?: number): void {
|
||||||
const doScroll = () => {
|
const doScroll = (): void => {
|
||||||
if (!this.messagePanel.current) return;
|
if (!this.messagePanel.current) return;
|
||||||
if (eventId) {
|
if (eventId) {
|
||||||
debuglog(
|
debuglog(
|
||||||
@ -1401,7 +1403,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
|
|||||||
? new TimelineWindow(cli, this.props.overlayTimelineSet, { windowLimit: this.props.timelineCap })
|
? new TimelineWindow(cli, this.props.overlayTimelineSet, { windowLimit: this.props.timelineCap })
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
const onLoaded = () => {
|
const onLoaded = (): void => {
|
||||||
if (this.unmounted) return;
|
if (this.unmounted) return;
|
||||||
|
|
||||||
// clear the timeline min-height when (re)loading the timeline
|
// clear the timeline min-height when (re)loading the timeline
|
||||||
@ -1441,7 +1443,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onError = (error: MatrixError) => {
|
const onError = (error: MatrixError): void => {
|
||||||
if (this.unmounted) return;
|
if (this.unmounted) return;
|
||||||
|
|
||||||
this.setState({ timelineLoading: false });
|
this.setState({ timelineLoading: false });
|
||||||
@ -1504,7 +1506,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const prom = this.timelineWindow.load(eventId, INITIAL_SIZE).then(async () => {
|
const prom = this.timelineWindow.load(eventId, INITIAL_SIZE).then(async (): Promise<void> => {
|
||||||
if (this.overlayTimelineWindow) {
|
if (this.overlayTimelineWindow) {
|
||||||
// @TODO(kerrya) use timestampToEvent to load the overlay timeline
|
// @TODO(kerrya) use timestampToEvent to load the overlay timeline
|
||||||
// with more correct position when main TL eventId is truthy
|
// with more correct position when main TL eventId is truthy
|
||||||
@ -1746,7 +1748,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
|
|||||||
const wrapperRect = messagePanelNode.getBoundingClientRect();
|
const wrapperRect = messagePanelNode.getBoundingClientRect();
|
||||||
const myUserId = MatrixClientPeg.get().credentials.userId;
|
const myUserId = MatrixClientPeg.get().credentials.userId;
|
||||||
|
|
||||||
const isNodeInView = (node: HTMLElement) => {
|
const isNodeInView = (node: HTMLElement): boolean => {
|
||||||
if (node) {
|
if (node) {
|
||||||
const boundingRect = node.getBoundingClientRect();
|
const boundingRect = node.getBoundingClientRect();
|
||||||
if (
|
if (
|
||||||
@ -1877,13 +1879,14 @@ class TimelinePanel extends React.Component<IProps, IState> {
|
|||||||
eventId: string,
|
eventId: string,
|
||||||
relationType: RelationType | string,
|
relationType: RelationType | string,
|
||||||
eventType: EventType | string,
|
eventType: EventType | string,
|
||||||
) => this.props.timelineSet.relations?.getChildEventsForEvent(eventId, relationType, eventType);
|
): Relations | undefined =>
|
||||||
|
this.props.timelineSet.relations?.getChildEventsForEvent(eventId, relationType, eventType);
|
||||||
|
|
||||||
private buildLegacyCallEventGroupers(events?: MatrixEvent[]): void {
|
private buildLegacyCallEventGroupers(events?: MatrixEvent[]): void {
|
||||||
this.callEventGroupers = buildLegacyCallEventGroupers(this.callEventGroupers, events);
|
this.callEventGroupers = buildLegacyCallEventGroupers(this.callEventGroupers, events);
|
||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render(): JSX.Element {
|
||||||
// just show a spinner while the timeline loads.
|
// just show a spinner while the timeline loads.
|
||||||
//
|
//
|
||||||
// put it in a div of the right class (mx_RoomView_messagePanel) so
|
// put it in a div of the right class (mx_RoomView_messagePanel) so
|
||||||
|
@ -39,18 +39,18 @@ export default class ToastContainer extends React.Component<{}, IState> {
|
|||||||
ToastStore.sharedInstance().on("update", this.onToastStoreUpdate);
|
ToastStore.sharedInstance().on("update", this.onToastStoreUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentWillUnmount() {
|
public componentWillUnmount(): void {
|
||||||
ToastStore.sharedInstance().removeListener("update", this.onToastStoreUpdate);
|
ToastStore.sharedInstance().removeListener("update", this.onToastStoreUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
private onToastStoreUpdate = () => {
|
private onToastStoreUpdate = (): void => {
|
||||||
this.setState({
|
this.setState({
|
||||||
toasts: ToastStore.sharedInstance().getToasts(),
|
toasts: ToastStore.sharedInstance().getToasts(),
|
||||||
countSeen: ToastStore.sharedInstance().getCountSeen(),
|
countSeen: ToastStore.sharedInstance().getCountSeen(),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
public render() {
|
public render(): JSX.Element {
|
||||||
const totalCount = this.state.toasts.length;
|
const totalCount = this.state.toasts.length;
|
||||||
const isStacked = totalCount > 1;
|
const isStacked = totalCount > 1;
|
||||||
let toast;
|
let toast;
|
||||||
|
@ -65,12 +65,12 @@ export default class UploadBar extends React.PureComponent<IProps, IState> {
|
|||||||
this.state = this.calculateState();
|
this.state = this.calculateState();
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount() {
|
public componentDidMount(): void {
|
||||||
this.dispatcherRef = dis.register(this.onAction);
|
this.dispatcherRef = dis.register(this.onAction);
|
||||||
this.mounted = true;
|
this.mounted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentWillUnmount() {
|
public componentWillUnmount(): void {
|
||||||
this.mounted = false;
|
this.mounted = false;
|
||||||
dis.unregister(this.dispatcherRef!);
|
dis.unregister(this.dispatcherRef!);
|
||||||
}
|
}
|
||||||
@ -91,19 +91,19 @@ export default class UploadBar extends React.PureComponent<IProps, IState> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private onAction = (payload: ActionPayload) => {
|
private onAction = (payload: ActionPayload): void => {
|
||||||
if (!this.mounted) return;
|
if (!this.mounted) return;
|
||||||
if (isUploadPayload(payload)) {
|
if (isUploadPayload(payload)) {
|
||||||
this.setState(this.calculateState());
|
this.setState(this.calculateState());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private onCancelClick = (ev: ButtonEvent) => {
|
private onCancelClick = (ev: ButtonEvent): void => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ContentMessages.sharedInstance().cancelUpload(this.state.currentUpload!);
|
ContentMessages.sharedInstance().cancelUpload(this.state.currentUpload!);
|
||||||
};
|
};
|
||||||
|
|
||||||
public render() {
|
public render(): JSX.Element {
|
||||||
if (!this.state.currentFile) {
|
if (!this.state.currentFile) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ import defaultDispatcher from "../../dispatcher/dispatcher";
|
|||||||
import { ActionPayload } from "../../dispatcher/payloads";
|
import { ActionPayload } from "../../dispatcher/payloads";
|
||||||
import { Action } from "../../dispatcher/actions";
|
import { Action } from "../../dispatcher/actions";
|
||||||
import { _t } from "../../languageHandler";
|
import { _t } from "../../languageHandler";
|
||||||
import { ChevronFace, ContextMenuButton } from "./ContextMenu";
|
import { ChevronFace, ContextMenuButton, MenuProps } from "./ContextMenu";
|
||||||
import { UserTab } from "../views/dialogs/UserTab";
|
import { UserTab } from "../views/dialogs/UserTab";
|
||||||
import { OpenToTabPayload } from "../../dispatcher/payloads/OpenToTabPayload";
|
import { OpenToTabPayload } from "../../dispatcher/payloads/OpenToTabPayload";
|
||||||
import FeedbackDialog from "../views/dialogs/FeedbackDialog";
|
import FeedbackDialog from "../views/dialogs/FeedbackDialog";
|
||||||
@ -67,7 +67,7 @@ interface IState {
|
|||||||
showLiveAvatarAddon: boolean;
|
showLiveAvatarAddon: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const toRightOf = (rect: PartialDOMRect) => {
|
const toRightOf = (rect: PartialDOMRect): MenuProps => {
|
||||||
return {
|
return {
|
||||||
left: rect.width + rect.left + 8,
|
left: rect.width + rect.left + 8,
|
||||||
top: rect.top,
|
top: rect.top,
|
||||||
@ -75,7 +75,7 @@ const toRightOf = (rect: PartialDOMRect) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const below = (rect: PartialDOMRect) => {
|
const below = (rect: PartialDOMRect): MenuProps => {
|
||||||
return {
|
return {
|
||||||
left: rect.left,
|
left: rect.left,
|
||||||
top: rect.top + rect.height,
|
top: rect.top + rect.height,
|
||||||
@ -118,7 +118,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
public componentDidMount() {
|
public componentDidMount(): void {
|
||||||
this.context.voiceBroadcastRecordingsStore.on(
|
this.context.voiceBroadcastRecordingsStore.on(
|
||||||
VoiceBroadcastRecordingsStoreEvent.CurrentChanged,
|
VoiceBroadcastRecordingsStoreEvent.CurrentChanged,
|
||||||
this.onCurrentVoiceBroadcastRecordingChanged,
|
this.onCurrentVoiceBroadcastRecordingChanged,
|
||||||
@ -127,7 +127,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
|
|||||||
this.themeWatcherRef = SettingsStore.watchSetting("theme", null, this.onThemeChanged);
|
this.themeWatcherRef = SettingsStore.watchSetting("theme", null, this.onThemeChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentWillUnmount() {
|
public componentWillUnmount(): void {
|
||||||
if (this.themeWatcherRef) SettingsStore.unwatchSetting(this.themeWatcherRef);
|
if (this.themeWatcherRef) SettingsStore.unwatchSetting(this.themeWatcherRef);
|
||||||
if (this.dndWatcherRef) SettingsStore.unwatchSetting(this.dndWatcherRef);
|
if (this.dndWatcherRef) SettingsStore.unwatchSetting(this.dndWatcherRef);
|
||||||
if (this.dispatcherRef) defaultDispatcher.unregister(this.dispatcherRef);
|
if (this.dispatcherRef) defaultDispatcher.unregister(this.dispatcherRef);
|
||||||
@ -163,26 +163,26 @@ export default class UserMenu extends React.Component<IProps, IState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private onProfileUpdate = async () => {
|
private onProfileUpdate = async (): Promise<void> => {
|
||||||
// the store triggered an update, so force a layout update. We don't
|
// the store triggered an update, so force a layout update. We don't
|
||||||
// have any state to store here for that to magically happen.
|
// have any state to store here for that to magically happen.
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
};
|
};
|
||||||
|
|
||||||
private onSelectedSpaceUpdate = async () => {
|
private onSelectedSpaceUpdate = async (): Promise<void> => {
|
||||||
this.setState({
|
this.setState({
|
||||||
selectedSpace: SpaceStore.instance.activeSpaceRoom,
|
selectedSpace: SpaceStore.instance.activeSpaceRoom,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
private onThemeChanged = () => {
|
private onThemeChanged = (): void => {
|
||||||
this.setState({
|
this.setState({
|
||||||
isDarkTheme: this.isUserOnDarkTheme(),
|
isDarkTheme: this.isUserOnDarkTheme(),
|
||||||
isHighContrast: this.isUserOnHighContrastTheme(),
|
isHighContrast: this.isUserOnHighContrastTheme(),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
private onAction = (payload: ActionPayload) => {
|
private onAction = (payload: ActionPayload): void => {
|
||||||
switch (payload.action) {
|
switch (payload.action) {
|
||||||
case Action.ToggleUserMenu:
|
case Action.ToggleUserMenu:
|
||||||
if (this.state.contextMenuPosition) {
|
if (this.state.contextMenuPosition) {
|
||||||
@ -194,13 +194,13 @@ export default class UserMenu extends React.Component<IProps, IState> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private onOpenMenuClick = (ev: React.MouseEvent) => {
|
private onOpenMenuClick = (ev: React.MouseEvent): void => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
this.setState({ contextMenuPosition: ev.currentTarget.getBoundingClientRect() });
|
this.setState({ contextMenuPosition: ev.currentTarget.getBoundingClientRect() });
|
||||||
};
|
};
|
||||||
|
|
||||||
private onContextMenu = (ev: React.MouseEvent) => {
|
private onContextMenu = (ev: React.MouseEvent): void => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
this.setState({
|
this.setState({
|
||||||
@ -213,11 +213,11 @@ export default class UserMenu extends React.Component<IProps, IState> {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
private onCloseMenu = () => {
|
private onCloseMenu = (): void => {
|
||||||
this.setState({ contextMenuPosition: null });
|
this.setState({ contextMenuPosition: null });
|
||||||
};
|
};
|
||||||
|
|
||||||
private onSwitchThemeClick = (ev: React.MouseEvent) => {
|
private onSwitchThemeClick = (ev: React.MouseEvent): void => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
|
|
||||||
@ -236,7 +236,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
|
|||||||
SettingsStore.setValue("theme", null, SettingLevel.DEVICE, newTheme); // set at same level as Appearance tab
|
SettingsStore.setValue("theme", null, SettingLevel.DEVICE, newTheme); // set at same level as Appearance tab
|
||||||
};
|
};
|
||||||
|
|
||||||
private onSettingsOpen = (ev: ButtonEvent, tabId: string) => {
|
private onSettingsOpen = (ev: ButtonEvent, tabId: string): void => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
|
|
||||||
@ -245,7 +245,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
|
|||||||
this.setState({ contextMenuPosition: null }); // also close the menu
|
this.setState({ contextMenuPosition: null }); // also close the menu
|
||||||
};
|
};
|
||||||
|
|
||||||
private onProvideFeedback = (ev: ButtonEvent) => {
|
private onProvideFeedback = (ev: ButtonEvent): void => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
|
|
||||||
@ -253,7 +253,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
|
|||||||
this.setState({ contextMenuPosition: null }); // also close the menu
|
this.setState({ contextMenuPosition: null }); // also close the menu
|
||||||
};
|
};
|
||||||
|
|
||||||
private onSignOutClick = async (ev: ButtonEvent) => {
|
private onSignOutClick = async (ev: ButtonEvent): Promise<void> => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
|
|
||||||
@ -268,17 +268,17 @@ export default class UserMenu extends React.Component<IProps, IState> {
|
|||||||
this.setState({ contextMenuPosition: null }); // also close the menu
|
this.setState({ contextMenuPosition: null }); // also close the menu
|
||||||
};
|
};
|
||||||
|
|
||||||
private onSignInClick = () => {
|
private onSignInClick = (): void => {
|
||||||
defaultDispatcher.dispatch({ action: "start_login" });
|
defaultDispatcher.dispatch({ action: "start_login" });
|
||||||
this.setState({ contextMenuPosition: null }); // also close the menu
|
this.setState({ contextMenuPosition: null }); // also close the menu
|
||||||
};
|
};
|
||||||
|
|
||||||
private onRegisterClick = () => {
|
private onRegisterClick = (): void => {
|
||||||
defaultDispatcher.dispatch({ action: "start_registration" });
|
defaultDispatcher.dispatch({ action: "start_registration" });
|
||||||
this.setState({ contextMenuPosition: null }); // also close the menu
|
this.setState({ contextMenuPosition: null }); // also close the menu
|
||||||
};
|
};
|
||||||
|
|
||||||
private onHomeClick = (ev: ButtonEvent) => {
|
private onHomeClick = (ev: ButtonEvent): void => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
|
|
||||||
@ -429,7 +429,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
public render() {
|
public render(): JSX.Element {
|
||||||
const avatarSize = 32; // should match border-radius of the avatar
|
const avatarSize = 32; // should match border-radius of the avatar
|
||||||
|
|
||||||
const userId = MatrixClientPeg.get().getUserId();
|
const userId = MatrixClientPeg.get().getUserId();
|
||||||
|
@ -57,7 +57,7 @@ export default class CompleteSecurity extends React.Component<IProps, IState> {
|
|||||||
store.stop();
|
store.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render(): JSX.Element {
|
||||||
const { phase, lostKeys } = this.state;
|
const { phase, lostKeys } = this.state;
|
||||||
let icon;
|
let icon;
|
||||||
let title;
|
let title;
|
||||||
|
@ -27,7 +27,7 @@ interface IProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default class E2eSetup extends React.Component<IProps> {
|
export default class E2eSetup extends React.Component<IProps> {
|
||||||
public render() {
|
public render(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<AuthPage>
|
<AuthPage>
|
||||||
<CompleteSecurityBody>
|
<CompleteSecurityBody>
|
||||||
|
@ -110,11 +110,11 @@ export default class ForgotPassword extends React.Component<Props, State> {
|
|||||||
this.reset = new PasswordReset(this.props.serverConfig.hsUrl, this.props.serverConfig.isUrl);
|
this.reset = new PasswordReset(this.props.serverConfig.hsUrl, this.props.serverConfig.isUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount() {
|
public componentDidMount(): void {
|
||||||
this.checkServerCapabilities(this.props.serverConfig);
|
this.checkServerCapabilities(this.props.serverConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentDidUpdate(prevProps: Readonly<Props>) {
|
public componentDidUpdate(prevProps: Readonly<Props>): void {
|
||||||
if (
|
if (
|
||||||
prevProps.serverConfig.hsUrl !== this.props.serverConfig.hsUrl ||
|
prevProps.serverConfig.hsUrl !== this.props.serverConfig.hsUrl ||
|
||||||
prevProps.serverConfig.isUrl !== this.props.serverConfig.isUrl
|
prevProps.serverConfig.isUrl !== this.props.serverConfig.isUrl
|
||||||
@ -159,7 +159,7 @@ export default class ForgotPassword extends React.Component<Props, State> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async onPhaseEmailInputSubmit() {
|
private async onPhaseEmailInputSubmit(): Promise<void> {
|
||||||
this.phase = Phase.SendingEmail;
|
this.phase = Phase.SendingEmail;
|
||||||
|
|
||||||
if (await this.sendVerificationMail()) {
|
if (await this.sendVerificationMail()) {
|
||||||
@ -213,7 +213,7 @@ export default class ForgotPassword extends React.Component<Props, State> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async onPhaseEmailSentSubmit() {
|
private async onPhaseEmailSentSubmit(): Promise<void> {
|
||||||
this.setState({
|
this.setState({
|
||||||
phase: Phase.PasswordInput,
|
phase: Phase.PasswordInput,
|
||||||
});
|
});
|
||||||
@ -288,7 +288,7 @@ export default class ForgotPassword extends React.Component<Props, State> {
|
|||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
{
|
{
|
||||||
onBeforeClose: async (reason?: string) => {
|
onBeforeClose: async (reason?: string): Promise<boolean> => {
|
||||||
if (reason === "backgroundClick") {
|
if (reason === "backgroundClick") {
|
||||||
// Modal dismissed by clicking the background.
|
// Modal dismissed by clicking the background.
|
||||||
// Go one phase back.
|
// Go one phase back.
|
||||||
@ -342,7 +342,7 @@ export default class ForgotPassword extends React.Component<Props, State> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private onInputChanged = (stateKey: string, ev: React.FormEvent<HTMLInputElement>) => {
|
private onInputChanged = (stateKey: string, ev: React.FormEvent<HTMLInputElement>): void => {
|
||||||
let value = ev.currentTarget.value;
|
let value = ev.currentTarget.value;
|
||||||
if (stateKey === "email") value = value.trim();
|
if (stateKey === "email") value = value.trim();
|
||||||
this.setState({
|
this.setState({
|
||||||
@ -460,7 +460,7 @@ export default class ForgotPassword extends React.Component<Props, State> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public renderDone() {
|
public renderDone(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<CheckboxIcon className="mx_Icon mx_Icon_32 mx_Icon_accent" />
|
<CheckboxIcon className="mx_Icon mx_Icon_32 mx_Icon_accent" />
|
||||||
@ -484,7 +484,7 @@ export default class ForgotPassword extends React.Component<Props, State> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render(): JSX.Element {
|
||||||
let resetPasswordJsx: JSX.Element;
|
let resetPasswordJsx: JSX.Element;
|
||||||
|
|
||||||
switch (this.state.phase) {
|
switch (this.state.phase) {
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user