Merge branch 'develop' of github.com:matrix-org/matrix-react-sdk into spaces-jump-to-room

This commit is contained in:
Jaiwanth 2021-06-30 11:20:01 +05:30
commit a99b24ef83
666 changed files with 5083 additions and 5561 deletions

View file

@ -1,7 +1,9 @@
module.exports = { module.exports = {
extends: ["matrix-org", "matrix-org/react-legacy"], plugins: ["matrix-org"],
parser: "babel-eslint", extends: [
"plugin:matrix-org/babel",
"plugin:matrix-org/react",
],
env: { env: {
browser: true, browser: true,
node: true, node: true,
@ -15,12 +17,32 @@ module.exports = {
"prefer-promise-reject-errors": "off", "prefer-promise-reject-errors": "off",
"no-async-promise-executor": "off", "no-async-promise-executor": "off",
"quotes": "off", "quotes": "off",
}, "no-extra-boolean-cast": "off",
// Bind or arrow functions in props causes performance issues (but we
// currently use them in some places).
// It's disabled here, but we should using it sparingly.
"react/jsx-no-bind": "off",
"react/jsx-key": ["error"],
},
overrides: [{ overrides: [{
"files": ["src/**/*.{ts,tsx}", "test/**/*.{ts,tsx}"], files: [
"extends": ["matrix-org/ts"], "src/**/*.{ts,tsx}",
"rules": { "test/**/*.{ts,tsx}",
],
extends: [
"plugin:matrix-org/typescript",
"plugin:matrix-org/react",
],
rules: {
// Things we do that break the ideal style
"prefer-promise-reject-errors": "off",
"quotes": "off",
"no-extra-boolean-cast": "off",
// Remove Babel things manually due to override limitations
"@babel/no-invalid-this": ["off"],
// We're okay being explicit at the moment // We're okay being explicit at the moment
"@typescript-eslint/no-empty-interface": "off", "@typescript-eslint/no-empty-interface": "off",
// We disable this while we're transitioning // We disable this while we're transitioning
@ -28,8 +50,6 @@ module.exports = {
// We'd rather not do this but we do // We'd rather not do this but we do
"@typescript-eslint/ban-ts-comment": "off", "@typescript-eslint/ban-ts-comment": "off",
"quotes": "off",
"no-extra-boolean-cast": "off",
"no-restricted-properties": [ "no-restricted-properties": [
"error", "error",
...buildRestrictedPropertiesOptions( ...buildRestrictedPropertiesOptions(

View file

@ -1,6 +0,0 @@
[include]
src/**/*.js
test/**/*.js
[ignore]
node_modules/

View file

@ -10,7 +10,6 @@ module.exports = {
], ],
}], }],
"@babel/preset-typescript", "@babel/preset-typescript",
"@babel/preset-flow",
"@babel/preset-react", "@babel/preset-react",
], ],
"plugins": [ "plugins": [
@ -19,7 +18,6 @@ module.exports = {
"@babel/plugin-proposal-numeric-separator", "@babel/plugin-proposal-numeric-separator",
"@babel/plugin-proposal-class-properties", "@babel/plugin-proposal-class-properties",
"@babel/plugin-proposal-object-rest-spread", "@babel/plugin-proposal-object-rest-spread",
"@babel/plugin-transform-flow-comments",
"@babel/plugin-syntax-dynamic-import", "@babel/plugin-syntax-dynamic-import",
"@babel/plugin-transform-runtime", "@babel/plugin-transform-runtime",
], ],

View file

@ -104,16 +104,16 @@
"devDependencies": { "devDependencies": {
"@babel/cli": "^7.12.10", "@babel/cli": "^7.12.10",
"@babel/core": "^7.12.10", "@babel/core": "^7.12.10",
"@babel/eslint-parser": "^7.12.10",
"@babel/eslint-plugin": "^7.12.10",
"@babel/parser": "^7.12.11", "@babel/parser": "^7.12.11",
"@babel/plugin-proposal-class-properties": "^7.12.1", "@babel/plugin-proposal-class-properties": "^7.12.1",
"@babel/plugin-proposal-decorators": "^7.12.12", "@babel/plugin-proposal-decorators": "^7.12.12",
"@babel/plugin-proposal-export-default-from": "^7.12.1", "@babel/plugin-proposal-export-default-from": "^7.12.1",
"@babel/plugin-proposal-numeric-separator": "^7.12.7", "@babel/plugin-proposal-numeric-separator": "^7.12.7",
"@babel/plugin-proposal-object-rest-spread": "^7.12.1", "@babel/plugin-proposal-object-rest-spread": "^7.12.1",
"@babel/plugin-transform-flow-comments": "^7.12.1",
"@babel/plugin-transform-runtime": "^7.12.10", "@babel/plugin-transform-runtime": "^7.12.10",
"@babel/preset-env": "^7.12.11", "@babel/preset-env": "^7.12.11",
"@babel/preset-flow": "^7.12.1",
"@babel/preset-react": "^7.12.10", "@babel/preset-react": "^7.12.10",
"@babel/preset-typescript": "^7.12.7", "@babel/preset-typescript": "^7.12.7",
"@babel/register": "^7.12.10", "@babel/register": "^7.12.10",
@ -139,18 +139,16 @@
"@types/react-transition-group": "^4.4.0", "@types/react-transition-group": "^4.4.0",
"@types/sanitize-html": "^2.3.1", "@types/sanitize-html": "^2.3.1",
"@types/zxcvbn": "^4.4.0", "@types/zxcvbn": "^4.4.0",
"@typescript-eslint/eslint-plugin": "^4.14.0", "@typescript-eslint/eslint-plugin": "^4.17.0",
"@typescript-eslint/parser": "^4.14.0", "@typescript-eslint/parser": "^4.17.0",
"@wojtekmaj/enzyme-adapter-react-17": "^0.6.1", "@wojtekmaj/enzyme-adapter-react-17": "^0.6.1",
"babel-eslint": "^10.1.0",
"babel-jest": "^26.6.3", "babel-jest": "^26.6.3",
"chokidar": "^3.5.1", "chokidar": "^3.5.1",
"concurrently": "^5.3.0", "concurrently": "^5.3.0",
"enzyme": "^3.11.0", "enzyme": "^3.11.0",
"eslint": "7.18.0", "eslint": "7.18.0",
"eslint-config-matrix-org": "^0.2.0", "eslint-config-google": "^0.14.0",
"eslint-plugin-babel": "^5.3.1", "eslint-plugin-matrix-org": "github:matrix-org/eslint-plugin-matrix-org#main",
"eslint-plugin-flowtype": "^5.2.0",
"eslint-plugin-react": "^7.22.0", "eslint-plugin-react": "^7.22.0",
"eslint-plugin-react-hooks": "^4.2.0", "eslint-plugin-react-hooks": "^4.2.0",
"glob": "^7.1.6", "glob": "^7.1.6",

View file

@ -134,12 +134,15 @@ limitations under the License.
.mx_Toast_buttons { .mx_Toast_buttons {
float: right; float: right;
display: flex; display: flex;
gap: 5px;
.mx_AccessibleButton { .mx_AccessibleButton {
min-width: 96px; min-width: 96px;
box-sizing: border-box; box-sizing: border-box;
} }
.mx_AccessibleButton + .mx_AccessibleButton {
margin-left: 5px;
}
} }
.mx_Toast_description { .mx_Toast_description {

View file

@ -17,7 +17,7 @@ limitations under the License.
import { JSXElementConstructor } from "react"; import { JSXElementConstructor } from "react";
// Based on https://stackoverflow.com/a/53229857/3532235 // Based on https://stackoverflow.com/a/53229857/3532235
export type Without<T, U> = {[P in Exclude<keyof T, keyof U>] ? : never}; export type Without<T, U> = {[P in Exclude<keyof T, keyof U>]?: never};
export type XOR<T, U> = (T | U) extends object ? (Without<T, U> & U) | (Without<U, T> & T) : T | U; export type XOR<T, U> = (T | U) extends object ? (Without<T, U> & U) | (Without<U, T> & T) : T | U;
export type Writeable<T> = { -readonly [P in keyof T]: T[P] }; export type Writeable<T> = { -readonly [P in keyof T]: T[P] };

View file

@ -16,6 +16,7 @@ limitations under the License.
import "matrix-js-sdk/src/@types/global"; // load matrix-js-sdk's type extensions first import "matrix-js-sdk/src/@types/global"; // load matrix-js-sdk's type extensions first
import * as ModernizrStatic from "modernizr"; import * as ModernizrStatic from "modernizr";
import ContentMessages from "../ContentMessages"; import ContentMessages from "../ContentMessages";
import { IMatrixClientPeg } from "../MatrixClientPeg"; import { IMatrixClientPeg } from "../MatrixClientPeg";
import ToastStore from "../stores/ToastStore"; import ToastStore from "../stores/ToastStore";
@ -23,25 +24,25 @@ import DeviceListener from "../DeviceListener";
import { RoomListStoreClass } from "../stores/room-list/RoomListStore"; import { RoomListStoreClass } from "../stores/room-list/RoomListStore";
import { PlatformPeg } from "../PlatformPeg"; import { PlatformPeg } from "../PlatformPeg";
import RoomListLayoutStore from "../stores/room-list/RoomListLayoutStore"; import RoomListLayoutStore from "../stores/room-list/RoomListLayoutStore";
import {IntegrationManagers} from "../integrations/IntegrationManagers"; import { IntegrationManagers } from "../integrations/IntegrationManagers";
import {ModalManager} from "../Modal"; import { ModalManager } from "../Modal";
import SettingsStore from "../settings/SettingsStore"; import SettingsStore from "../settings/SettingsStore";
import {ActiveRoomObserver} from "../ActiveRoomObserver"; import { ActiveRoomObserver } from "../ActiveRoomObserver";
import {Notifier} from "../Notifier"; import { Notifier } from "../Notifier";
import type {Renderer} from "react-dom"; import type { Renderer } from "react-dom";
import RightPanelStore from "../stores/RightPanelStore"; import RightPanelStore from "../stores/RightPanelStore";
import WidgetStore from "../stores/WidgetStore"; import WidgetStore from "../stores/WidgetStore";
import CallHandler from "../CallHandler"; import CallHandler from "../CallHandler";
import {Analytics} from "../Analytics"; import { Analytics } from "../Analytics";
import CountlyAnalytics from "../CountlyAnalytics"; import CountlyAnalytics from "../CountlyAnalytics";
import UserActivity from "../UserActivity"; import UserActivity from "../UserActivity";
import {ModalWidgetStore} from "../stores/ModalWidgetStore"; import { ModalWidgetStore } from "../stores/ModalWidgetStore";
import { WidgetLayoutStore } from "../stores/widgets/WidgetLayoutStore"; import { WidgetLayoutStore } from "../stores/widgets/WidgetLayoutStore";
import VoipUserMapper from "../VoipUserMapper"; import VoipUserMapper from "../VoipUserMapper";
import {SpaceStoreClass} from "../stores/SpaceStore"; import { SpaceStoreClass } from "../stores/SpaceStore";
import TypingStore from "../stores/TypingStore"; import TypingStore from "../stores/TypingStore";
import { EventIndexPeg } from "../indexing/EventIndexPeg"; import { EventIndexPeg } from "../indexing/EventIndexPeg";
import {VoiceRecordingStore} from "../stores/VoiceRecordingStore"; import { VoiceRecordingStore } from "../stores/VoiceRecordingStore";
import PerformanceMonitor from "../performance"; import PerformanceMonitor from "../performance";
import UIStore from "../stores/UIStore"; import UIStore from "../stores/UIStore";
import { SetupEncryptionStore } from "../stores/SetupEncryptionStore"; import { SetupEncryptionStore } from "../stores/SetupEncryptionStore";
@ -127,11 +128,24 @@ declare global {
setSinkId(outputId: string); setSinkId(outputId: string);
} }
// Add Chrome-specific `instant` ScrollBehaviour
type _ScrollBehavior = ScrollBehavior | "instant";
interface _ScrollOptions {
behavior?: _ScrollBehavior;
}
interface _ScrollIntoViewOptions extends _ScrollOptions {
block?: ScrollLogicalPosition;
inline?: ScrollLogicalPosition;
}
interface Element { interface Element {
// Safari & IE11 only have this prefixed: we used prefixed versions // Safari & IE11 only have this prefixed: we used prefixed versions
// previously so let's continue to support them for now // previously so let's continue to support them for now
webkitRequestFullScreen(options?: FullscreenOptions): Promise<void>; webkitRequestFullScreen(options?: FullscreenOptions): Promise<void>;
msRequestFullscreen(options?: FullscreenOptions): Promise<void>; msRequestFullscreen(options?: FullscreenOptions): Promise<void>;
scrollIntoView(arg?: boolean | _ScrollIntoViewOptions): void;
} }
interface Error { interface Error {

View file

@ -16,12 +16,12 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {MatrixClientPeg} from './MatrixClientPeg'; import { MatrixClientPeg } from './MatrixClientPeg';
import * as sdk from './index'; import * as sdk from './index';
import Modal from './Modal'; import Modal from './Modal';
import { _t } from './languageHandler'; import { _t } from './languageHandler';
import IdentityAuthClient from './IdentityAuthClient'; import IdentityAuthClient from './IdentityAuthClient';
import {SSOAuthEntry} from "./components/views/auth/InteractiveAuthEntryComponents"; import { SSOAuthEntry } from "./components/views/auth/InteractiveAuthEntryComponents";
function getIdServerDomain() { function getIdServerDomain() {
return MatrixClientPeg.get().idBaseUrl.split("://")[1]; return MatrixClientPeg.get().idBaseUrl.split("://")[1];
@ -189,7 +189,6 @@ export default class AddThreepid {
// pop up an interactive auth dialog // pop up an interactive auth dialog
const InteractiveAuthDialog = sdk.getComponent("dialogs.InteractiveAuthDialog"); const InteractiveAuthDialog = sdk.getComponent("dialogs.InteractiveAuthDialog");
const dialogAesthetics = { const dialogAesthetics = {
[SSOAuthEntry.PHASE_PREAUTH]: { [SSOAuthEntry.PHASE_PREAUTH]: {
title: _t("Use Single Sign On to continue"), title: _t("Use Single Sign On to continue"),

View file

@ -17,7 +17,7 @@ limitations under the License.
import React from 'react'; import React from 'react';
import {getCurrentLanguage, _t, _td, IVariables} from './languageHandler'; import { getCurrentLanguage, _t, _td, IVariables } from './languageHandler';
import PlatformPeg from './PlatformPeg'; import PlatformPeg from './PlatformPeg';
import SdkConfig from './SdkConfig'; import SdkConfig from './SdkConfig';
import Modal from './Modal'; import Modal from './Modal';

View file

@ -17,16 +17,16 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {MatrixClient} from "matrix-js-sdk/src/client"; import { MatrixClient } from "matrix-js-sdk/src/client";
import {encodeUnpaddedBase64} from "matrix-js-sdk/src/crypto/olmlib"; import { encodeUnpaddedBase64 } from "matrix-js-sdk/src/crypto/olmlib";
import dis from './dispatcher/dispatcher'; import dis from './dispatcher/dispatcher';
import BaseEventIndexManager from './indexing/BaseEventIndexManager'; import BaseEventIndexManager from './indexing/BaseEventIndexManager';
import {ActionPayload} from "./dispatcher/payloads"; import { ActionPayload } from "./dispatcher/payloads";
import {CheckUpdatesPayload} from "./dispatcher/payloads/CheckUpdatesPayload"; import { CheckUpdatesPayload } from "./dispatcher/payloads/CheckUpdatesPayload";
import {Action} from "./dispatcher/actions"; import { Action } from "./dispatcher/actions";
import {hideToast as hideUpdateToast} from "./toasts/UpdateToast"; import { hideToast as hideUpdateToast } from "./toasts/UpdateToast";
import {MatrixClientPeg} from "./MatrixClientPeg"; import { MatrixClientPeg } from "./MatrixClientPeg";
import {idbLoad, idbSave, idbDelete} from "./utils/StorageManager"; import { idbLoad, idbSave, idbDelete } from "./utils/StorageManager";
export const SSO_HOMESERVER_URL_KEY = "mx_sso_hs_url"; export const SSO_HOMESERVER_URL_KEY = "mx_sso_hs_url";
export const SSO_ID_SERVER_URL_KEY = "mx_sso_is_url"; export const SSO_ID_SERVER_URL_KEY = "mx_sso_is_url";
@ -335,7 +335,7 @@ export default abstract class BasePlatform {
try { try {
const key = await crypto.subtle.decrypt( const key = await crypto.subtle.decrypt(
{name: "AES-GCM", iv: data.iv, additionalData}, data.cryptoKey, { name: "AES-GCM", iv: data.iv, additionalData }, data.cryptoKey,
data.encrypted, data.encrypted,
); );
return encodeUnpaddedBase64(key); return encodeUnpaddedBase64(key);
@ -348,7 +348,7 @@ export default abstract class BasePlatform {
/** /**
* Create and store a pickle key for encrypting libolm objects. * Create and store a pickle key for encrypting libolm objects.
* @param {string} userId the user ID for the user that the pickle key is for. * @param {string} userId the user ID for the user that the pickle key is for.
* @param {string} userId the device ID that the pickle key is for. * @param {string} deviceId the device ID that the pickle key is for.
* @returns {string|null} the pickle key, or null if the platform does not * @returns {string|null} the pickle key, or null if the platform does not
* support storing pickle keys. * support storing pickle keys.
*/ */
@ -360,7 +360,7 @@ export default abstract class BasePlatform {
const randomArray = new Uint8Array(32); const randomArray = new Uint8Array(32);
crypto.getRandomValues(randomArray); crypto.getRandomValues(randomArray);
const cryptoKey = await crypto.subtle.generateKey( const cryptoKey = await crypto.subtle.generateKey(
{name: "AES-GCM", length: 256}, false, ["encrypt", "decrypt"], { name: "AES-GCM", length: 256 }, false, ["encrypt", "decrypt"],
); );
const iv = new Uint8Array(32); const iv = new Uint8Array(32);
crypto.getRandomValues(iv); crypto.getRandomValues(iv);
@ -375,11 +375,11 @@ export default abstract class BasePlatform {
} }
const encrypted = await crypto.subtle.encrypt( const encrypted = await crypto.subtle.encrypt(
{name: "AES-GCM", iv, additionalData}, cryptoKey, randomArray, { name: "AES-GCM", iv, additionalData }, cryptoKey, randomArray,
); );
try { try {
await idbSave("pickleKey", [userId, deviceId], {encrypted, iv, cryptoKey}); await idbSave("pickleKey", [userId, deviceId], { encrypted, iv, cryptoKey });
} catch (e) { } catch (e) {
return null; return null;
} }

View file

@ -55,7 +55,7 @@ limitations under the License.
import React from 'react'; import React from 'react';
import {MatrixClientPeg} from './MatrixClientPeg'; import { MatrixClientPeg } from './MatrixClientPeg';
import PlatformPeg from './PlatformPeg'; import PlatformPeg from './PlatformPeg';
import Modal from './Modal'; import Modal from './Modal';
import { _t } from './languageHandler'; import { _t } from './languageHandler';
@ -63,11 +63,11 @@ import dis from './dispatcher/dispatcher';
import WidgetUtils from './utils/WidgetUtils'; import WidgetUtils from './utils/WidgetUtils';
import WidgetEchoStore from './stores/WidgetEchoStore'; import WidgetEchoStore from './stores/WidgetEchoStore';
import SettingsStore from './settings/SettingsStore'; import SettingsStore from './settings/SettingsStore';
import {Jitsi} from "./widgets/Jitsi"; import { Jitsi } from "./widgets/Jitsi";
import {WidgetType} from "./widgets/WidgetType"; import { WidgetType } from "./widgets/WidgetType";
import {SettingLevel} from "./settings/SettingLevel"; import { SettingLevel } from "./settings/SettingLevel";
import { ActionPayload } from "./dispatcher/payloads"; import { ActionPayload } from "./dispatcher/payloads";
import {base32} from "rfc4648"; import { base32 } from "rfc4648";
import QuestionDialog from "./components/views/dialogs/QuestionDialog"; import QuestionDialog from "./components/views/dialogs/QuestionDialog";
import ErrorDialog from "./components/views/dialogs/ErrorDialog"; import ErrorDialog from "./components/views/dialogs/ErrorDialog";
@ -77,10 +77,10 @@ import { ElementWidgetActions } from "./stores/widgets/ElementWidgetActions";
import { MatrixCall, CallErrorCode, CallState, CallEvent, CallParty, CallType } from "matrix-js-sdk/src/webrtc/call"; import { MatrixCall, CallErrorCode, CallState, CallEvent, CallParty, CallType } from "matrix-js-sdk/src/webrtc/call";
import Analytics from './Analytics'; import Analytics from './Analytics';
import CountlyAnalytics from "./CountlyAnalytics"; import CountlyAnalytics from "./CountlyAnalytics";
import {UIFeature} from "./settings/UIFeature"; import { UIFeature } from "./settings/UIFeature";
import { CallError } from "matrix-js-sdk/src/webrtc/call"; import { CallError } from "matrix-js-sdk/src/webrtc/call";
import { logger } from 'matrix-js-sdk/src/logger'; import { logger } from 'matrix-js-sdk/src/logger';
import DesktopCapturerSourcePicker from "./components/views/elements/DesktopCapturerSourcePicker" import DesktopCapturerSourcePicker from "./components/views/elements/DesktopCapturerSourcePicker";
import { Action } from './dispatcher/actions'; import { Action } from './dispatcher/actions';
import VoipUserMapper from './VoipUserMapper'; import VoipUserMapper from './VoipUserMapper';
import { addManagedHybridWidget, isManagedHybridWidgetEnabled } from './widgets/ManagedHybrid'; import { addManagedHybridWidget, isManagedHybridWidgetEnabled } from './widgets/ManagedHybrid';
@ -166,7 +166,7 @@ export default class CallHandler extends EventEmitter {
static sharedInstance() { static sharedInstance() {
if (!window.mxCallHandler) { if (!window.mxCallHandler) {
window.mxCallHandler = new CallHandler() window.mxCallHandler = new CallHandler();
} }
return window.mxCallHandler; return window.mxCallHandler;
@ -185,7 +185,7 @@ export default class CallHandler extends EventEmitter {
const nativeUser = this.assertedIdentityNativeUsers[call.callId]; const nativeUser = this.assertedIdentityNativeUsers[call.callId];
if (nativeUser) { if (nativeUser) {
const room = findDMForUser(MatrixClientPeg.get(), nativeUser); const room = findDMForUser(MatrixClientPeg.get(), nativeUser);
if (room) return room.roomId if (room) return room.roomId;
} }
} }
@ -238,7 +238,7 @@ export default class CallHandler extends EventEmitter {
this.supportsPstnProtocol = null; this.supportsPstnProtocol = null;
} }
dis.dispatch({action: Action.PstnSupportUpdated}); dis.dispatch({ action: Action.PstnSupportUpdated });
if (protocols[PROTOCOL_SIP_NATIVE] !== undefined && protocols[PROTOCOL_SIP_VIRTUAL] !== undefined) { if (protocols[PROTOCOL_SIP_NATIVE] !== undefined && protocols[PROTOCOL_SIP_VIRTUAL] !== undefined) {
this.supportsSipNativeVirtual = Boolean( this.supportsSipNativeVirtual = Boolean(
@ -246,7 +246,7 @@ export default class CallHandler extends EventEmitter {
); );
} }
dis.dispatch({action: Action.VirtualRoomSupportUpdated}); dis.dispatch({ action: Action.VirtualRoomSupportUpdated });
} catch (e) { } catch (e) {
if (maxTries === 1) { if (maxTries === 1) {
console.log("Failed to check for protocol support and no retries remain: assuming no support", e); console.log("Failed to check for protocol support and no retries remain: assuming no support", e);
@ -299,7 +299,7 @@ export default class CallHandler extends EventEmitter {
action: 'incoming_call', action: 'incoming_call',
call: call, call: call,
}, true); }, true);
} };
getCallForRoom(roomId: string): MatrixCall { getCallForRoom(roomId: string): MatrixCall {
return this.calls.get(roomId) || null; return this.calls.get(roomId) || null;
@ -711,7 +711,7 @@ export default class CallHandler extends EventEmitter {
call.placeScreenSharingCall( call.placeScreenSharingCall(
async (): Promise<DesktopCapturerSource> => { async (): Promise<DesktopCapturerSource> => {
const {finished} = Modal.createDialog(DesktopCapturerSourcePicker); const { finished } = Modal.createDialog(DesktopCapturerSourcePicker);
const [source] = await finished; const [source] = await finished;
return source; return source;
}, },
@ -816,7 +816,7 @@ export default class CallHandler extends EventEmitter {
Analytics.trackEvent('voip', 'receiveCall', 'type', call.type); Analytics.trackEvent('voip', 'receiveCall', 'type', call.type);
console.log("Adding call for room ", mappedRoomId); console.log("Adding call for room ", mappedRoomId);
this.calls.set(mappedRoomId, call) this.calls.set(mappedRoomId, call);
this.emit(CallHandlerEvent.CallsChanged, this.calls); this.emit(CallHandlerEvent.CallsChanged, this.calls);
this.setCallListeners(call); this.setCallListeners(call);
@ -872,7 +872,7 @@ export default class CallHandler extends EventEmitter {
this.dialNumber(payload.number); this.dialNumber(payload.number);
break; break;
} }
} };
private async dialNumber(number: string) { private async dialNumber(number: string) {
const results = await this.pstnLookup(number); const results = await this.pstnLookup(number);
@ -966,7 +966,7 @@ export default class CallHandler extends EventEmitter {
confId = 'Jitsi' + random; confId = 'Jitsi' + random;
} }
let widgetUrl = WidgetUtils.getLocalJitsiWrapperUrl({auth: jitsiAuth}); let widgetUrl = WidgetUtils.getLocalJitsiWrapperUrl({ auth: jitsiAuth });
// TODO: Remove URL hacks when the mobile clients eventually support v2 widgets // TODO: Remove URL hacks when the mobile clients eventually support v2 widgets
const parsedUrl = new URL(widgetUrl); const parsedUrl = new URL(widgetUrl);

View file

@ -18,8 +18,8 @@ limitations under the License.
import React from "react"; import React from "react";
import dis from './dispatcher/dispatcher'; import dis from './dispatcher/dispatcher';
import {MatrixClientPeg} from './MatrixClientPeg'; import { MatrixClientPeg } from './MatrixClientPeg';
import {MatrixClient} from "matrix-js-sdk/src/client"; import { MatrixClient } from "matrix-js-sdk/src/client";
import * as sdk from './index'; import * as sdk from './index';
import { _t } from './languageHandler'; import { _t } from './languageHandler';
import Modal from './Modal'; import Modal from './Modal';
@ -37,7 +37,7 @@ import {
UploadProgressPayload, UploadProgressPayload,
UploadStartedPayload, UploadStartedPayload,
} from "./dispatcher/payloads/UploadPayload"; } from "./dispatcher/payloads/UploadPayload";
import {IUpload} from "./models/IUpload"; import { IUpload } from "./models/IUpload";
import { IImageInfo } from "matrix-js-sdk/src/@types/partials"; import { IImageInfo } from "matrix-js-sdk/src/@types/partials";
const MAX_WIDTH = 800; const MAX_WIDTH = 800;
@ -189,7 +189,7 @@ async function loadImageElement(imageFile: File) {
const [hidpi] = await Promise.all([parsePromise, imgPromise]); const [hidpi] = await Promise.all([parsePromise, imgPromise]);
const width = hidpi ? (img.width >> 1) : img.width; const width = hidpi ? (img.width >> 1) : img.width;
const height = hidpi ? (img.height >> 1) : img.height; const height = hidpi ? (img.height >> 1) : img.height;
return {width, height, img}; return { width, height, img };
} }
/** /**
@ -343,7 +343,7 @@ export function uploadFile(
if (file.type) { if (file.type) {
encryptInfo.mimetype = file.type; encryptInfo.mimetype = file.type;
} }
return {"file": encryptInfo}; return { "file": encryptInfo };
}); });
(prom as IAbortablePromise<any>).abort = () => { (prom as IAbortablePromise<any>).abort = () => {
canceled = true; canceled = true;
@ -357,7 +357,7 @@ export function uploadFile(
const promise1 = basePromise.then(function(url) { const promise1 = basePromise.then(function(url) {
if (canceled) throw new UploadCanceledError(); if (canceled) throw new UploadCanceledError();
// If the attachment isn't encrypted then include the URL directly. // If the attachment isn't encrypted then include the URL directly.
return {"url": url}; return { "url": url };
}); });
(promise1 as any).abort = () => { (promise1 as any).abort = () => {
canceled = true; canceled = true;
@ -377,7 +377,7 @@ export default class ContentMessages {
console.warn(`Failed to send content with URL ${url} to room ${roomId}`, e); console.warn(`Failed to send content with URL ${url} to room ${roomId}`, e);
throw e; throw e;
}); });
CountlyAnalytics.instance.trackSendMessage(startTime, prom, roomId, false, false, {msgtype: "m.sticker"}); CountlyAnalytics.instance.trackSendMessage(startTime, prom, roomId, false, false, { msgtype: "m.sticker" });
return prom; return prom;
} }
@ -391,14 +391,14 @@ export default class ContentMessages {
async sendContentListToRoom(files: File[], roomId: string, matrixClient: MatrixClient) { async sendContentListToRoom(files: File[], roomId: string, matrixClient: MatrixClient) {
if (matrixClient.isGuest()) { if (matrixClient.isGuest()) {
dis.dispatch({action: 'require_registration'}); dis.dispatch({ action: 'require_registration' });
return; return;
} }
const isQuoting = Boolean(RoomViewStore.getQuotingEvent()); const isQuoting = Boolean(RoomViewStore.getQuotingEvent());
if (isQuoting) { if (isQuoting) {
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
const {finished} = Modal.createTrackedDialog<[boolean]>('Upload Reply Warning', '', QuestionDialog, { const { finished } = Modal.createTrackedDialog<[boolean]>('Upload Reply Warning', '', QuestionDialog, {
title: _t('Replying With Files'), title: _t('Replying With Files'),
description: ( description: (
<div>{_t( <div>{_t(
@ -432,7 +432,7 @@ export default class ContentMessages {
if (tooBigFiles.length > 0) { if (tooBigFiles.length > 0) {
const UploadFailureDialog = sdk.getComponent("dialogs.UploadFailureDialog"); const UploadFailureDialog = sdk.getComponent("dialogs.UploadFailureDialog");
const {finished} = Modal.createTrackedDialog<[boolean]>('Upload Failure', '', UploadFailureDialog, { const { finished } = Modal.createTrackedDialog<[boolean]>('Upload Failure', '', UploadFailureDialog, {
badFiles: tooBigFiles, badFiles: tooBigFiles,
totalFiles: files.length, totalFiles: files.length,
contentMessages: this, contentMessages: this,
@ -449,7 +449,7 @@ export default class ContentMessages {
for (let i = 0; i < okFiles.length; ++i) { for (let i = 0; i < okFiles.length; ++i) {
const file = okFiles[i]; const file = okFiles[i];
if (!uploadAll) { if (!uploadAll) {
const {finished} = Modal.createTrackedDialog<[boolean, boolean]>('Upload Files confirmation', const { finished } = Modal.createTrackedDialog<[boolean, boolean]>('Upload Files confirmation',
'', UploadConfirmDialog, { '', UploadConfirmDialog, {
file, file,
currentIndex: i, currentIndex: i,
@ -481,7 +481,7 @@ export default class ContentMessages {
if (upload) { if (upload) {
upload.canceled = true; upload.canceled = true;
MatrixClientPeg.get().cancelUpload(upload.promise); MatrixClientPeg.get().cancelUpload(upload.promise);
dis.dispatch<UploadCanceledPayload>({action: Action.UploadCanceled, upload}); dis.dispatch<UploadCanceledPayload>({ action: Action.UploadCanceled, upload });
} }
} }
@ -542,7 +542,7 @@ export default class ContentMessages {
promise: prom, promise: prom,
}; };
this.inprogress.push(upload); this.inprogress.push(upload);
dis.dispatch<UploadStartedPayload>({action: Action.UploadStarted, upload}); dis.dispatch<UploadStartedPayload>({ action: Action.UploadStarted, upload });
// Focus the composer view // Focus the composer view
dis.fire(Action.FocusComposer); dis.fire(Action.FocusComposer);
@ -550,7 +550,7 @@ export default class ContentMessages {
function onProgress(ev) { function onProgress(ev) {
upload.total = ev.total; upload.total = ev.total;
upload.loaded = ev.loaded; upload.loaded = ev.loaded;
dis.dispatch<UploadProgressPayload>({action: Action.UploadProgress, upload}); dis.dispatch<UploadProgressPayload>({ action: Action.UploadProgress, upload });
} }
let error; let error;
@ -577,11 +577,11 @@ export default class ContentMessages {
}, function(err) { }, function(err) {
error = err; error = err;
if (!upload.canceled) { if (!upload.canceled) {
let desc = _t("The file '%(fileName)s' failed to upload.", {fileName: upload.fileName}); let desc = _t("The file '%(fileName)s' failed to upload.", { fileName: upload.fileName });
if (err.http_status === 413) { if (err.http_status === 413) {
desc = _t( desc = _t(
"The file '%(fileName)s' exceeds this homeserver's size limit for uploads", "The file '%(fileName)s' exceeds this homeserver's size limit for uploads",
{fileName: upload.fileName}, { fileName: upload.fileName },
); );
} }
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
@ -604,10 +604,10 @@ export default class ContentMessages {
if (error && error.http_status === 413) { if (error && error.http_status === 413) {
this.mediaConfig = null; this.mediaConfig = null;
} }
dis.dispatch<UploadErrorPayload>({action: Action.UploadFailed, upload, error}); dis.dispatch<UploadErrorPayload>({ action: Action.UploadFailed, upload, error });
} else { } else {
dis.dispatch<UploadFinishedPayload>({action: Action.UploadFinished, upload}); dis.dispatch<UploadFinishedPayload>({ action: Action.UploadFinished, upload });
dis.dispatch({action: 'message_sent'}); dis.dispatch({ action: 'message_sent' });
} }
}); });
} }

View file

@ -14,13 +14,13 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {randomString} from "matrix-js-sdk/src/randomstring"; import { randomString } from "matrix-js-sdk/src/randomstring";
import {getCurrentLanguage} from './languageHandler'; import { getCurrentLanguage } from './languageHandler';
import PlatformPeg from './PlatformPeg'; import PlatformPeg from './PlatformPeg';
import SdkConfig from './SdkConfig'; import SdkConfig from './SdkConfig';
import {MatrixClientPeg} from "./MatrixClientPeg"; import { MatrixClientPeg } from "./MatrixClientPeg";
import {sleep} from "./utils/promise"; import { sleep } from "./utils/promise";
import RoomViewStore from "./stores/RoomViewStore"; import RoomViewStore from "./stores/RoomViewStore";
import { Action } from "./dispatcher/actions"; import { Action } from "./dispatcher/actions";
@ -338,8 +338,8 @@ const getRoomStats = (roomId: string) => {
"is_encrypted": cli?.isRoomEncrypted(roomId), "is_encrypted": cli?.isRoomEncrypted(roomId),
// eslint-disable-next-line camelcase // eslint-disable-next-line camelcase
"is_public": room?.currentState.getStateEvents("m.room.join_rules", "")?.getContent()?.join_rule === "public", "is_public": room?.currentState.getStateEvents("m.room.join_rules", "")?.getContent()?.join_rule === "public",
} };
} };
// async wrapper for regex-powered String.prototype.replace // async wrapper for regex-powered String.prototype.replace
const strReplaceAsync = async (str: string, regex: RegExp, fn: (...args: string[]) => Promise<string>) => { const strReplaceAsync = async (str: string, regex: RegExp, fn: (...args: string[]) => Promise<string>) => {
@ -414,7 +414,7 @@ export default class CountlyAnalytics {
this.anonymous = anonymous; this.anonymous = anonymous;
if (anonymous) { if (anonymous) {
await this.changeUserKey(randomString(64)) await this.changeUserKey(randomString(64));
} else { } else {
await this.changeUserKey(await hashHex(MatrixClientPeg.get().getUserId()), true); await this.changeUserKey(await hashHex(MatrixClientPeg.get().getUserId()), true);
} }
@ -438,7 +438,7 @@ export default class CountlyAnalytics {
await this.track("Opt-Out" ); await this.track("Opt-Out" );
this.endSession(); this.endSession();
window.clearInterval(this.heartbeatIntervalId); window.clearInterval(this.heartbeatIntervalId);
window.clearTimeout(this.activityIntervalId) window.clearTimeout(this.activityIntervalId);
this.baseUrl = null; this.baseUrl = null;
// remove listeners bound in trackSessions() // remove listeners bound in trackSessions()
window.removeEventListener("beforeunload", this.endSession); window.removeEventListener("beforeunload", this.endSession);
@ -662,14 +662,14 @@ export default class CountlyAnalytics {
} }
private queue(args: Omit<IEvent, "timestamp" | "hour" | "dow" | "count"> & Partial<Pick<IEvent, "count">>) { private queue(args: Omit<IEvent, "timestamp" | "hour" | "dow" | "count"> & Partial<Pick<IEvent, "count">>) {
const {count = 1, ...rest} = args; const { count = 1, ...rest } = args;
const ev = { const ev = {
...this.getTimeParams(), ...this.getTimeParams(),
...rest, ...rest,
count, count,
platform: this.appPlatform, platform: this.appPlatform,
app_version: this.appVersion, app_version: this.appVersion,
} };
this.pendingEvents.push(ev); this.pendingEvents.push(ev);
if (this.pendingEvents.length > MAX_PENDING_EVENTS) { if (this.pendingEvents.length > MAX_PENDING_EVENTS) {
@ -680,7 +680,7 @@ export default class CountlyAnalytics {
private getOrientation = (): Orientation => { private getOrientation = (): Orientation => {
return window.matchMedia("(orientation: landscape)").matches return window.matchMedia("(orientation: landscape)").matches
? Orientation.Landscape ? Orientation.Landscape
: Orientation.Portrait : Orientation.Portrait;
}; };
private reportOrientation = () => { private reportOrientation = () => {
@ -749,7 +749,7 @@ export default class CountlyAnalytics {
const request: Parameters<typeof CountlyAnalytics.prototype.request>[0] = { const request: Parameters<typeof CountlyAnalytics.prototype.request>[0] = {
begin_session: 1, begin_session: 1,
user_details: JSON.stringify(userDetails), user_details: JSON.stringify(userDetails),
} };
const metrics = this.getMetrics(); const metrics = this.getMetrics();
if (metrics) { if (metrics) {
@ -773,7 +773,7 @@ export default class CountlyAnalytics {
private endSession = () => { private endSession = () => {
if (this.sessionStarted) { if (this.sessionStarted) {
window.removeEventListener("resize", this.reportOrientation) window.removeEventListener("resize", this.reportOrientation);
this.reportViewDuration(); this.reportViewDuration();
this.request({ this.request({

View file

@ -168,7 +168,7 @@ export class DecryptionFailureTracker {
const trackedEventIds = [...dedupedFailuresMap.keys()]; const trackedEventIds = [...dedupedFailuresMap.keys()];
this.trackedEventHashMap = trackedEventIds.reduce( this.trackedEventHashMap = trackedEventIds.reduce(
(result, eventId) => ({...result, [eventId]: true}), (result, eventId) => ({ ...result, [eventId]: true }),
this.trackedEventHashMap, this.trackedEventHashMap,
); );

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {MatrixClientPeg} from './MatrixClientPeg'; import { MatrixClientPeg } from './MatrixClientPeg';
import dis from "./dispatcher/dispatcher"; import dis from "./dispatcher/dispatcher";
import { import {
hideToast as hideBulkUnverifiedSessionsToast, hideToast as hideBulkUnverifiedSessionsToast,

View file

@ -19,7 +19,7 @@ import Modal from './Modal';
import * as sdk from './'; import * as sdk from './';
import MultiInviter from './utils/MultiInviter'; import MultiInviter from './utils/MultiInviter';
import { _t } from './languageHandler'; import { _t } from './languageHandler';
import {MatrixClientPeg} from './MatrixClientPeg'; import { MatrixClientPeg } from './MatrixClientPeg';
import GroupStore from './stores/GroupStore'; import GroupStore from './stores/GroupStore';
import StyledCheckbox from './components/views/elements/StyledCheckbox'; import StyledCheckbox from './components/views/elements/StyledCheckbox';
@ -103,7 +103,7 @@ function _onGroupInviteFinished(groupId, addrs) {
if (errorList.length > 0) { if (errorList.length > 0) {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog('Failed to invite the following users to the group', '', ErrorDialog, { Modal.createTrackedDialog('Failed to invite the following users to the group', '', ErrorDialog, {
title: _t("Failed to invite the following users to %(groupId)s:", {groupId: groupId}), title: _t("Failed to invite the following users to %(groupId)s:", { groupId: groupId }),
description: errorList.join(", "), description: errorList.join(", "),
}); });
} }
@ -111,7 +111,7 @@ function _onGroupInviteFinished(groupId, addrs) {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog('Failed to invite users to group', '', ErrorDialog, { Modal.createTrackedDialog('Failed to invite users to group', '', ErrorDialog, {
title: _t("Failed to invite users to community"), title: _t("Failed to invite users to community"),
description: _t("Failed to invite users to %(groupId)s", {groupId: groupId}), description: _t("Failed to invite users to %(groupId)s", { groupId: groupId }),
}); });
}); });
} }
@ -137,7 +137,7 @@ function _onGroupAddRoomFinished(groupId, addrs, addRoomsPublicly) {
// Add this group as related // Add this group as related
if (!groups.includes(groupId)) { if (!groups.includes(groupId)) {
groups.push(groupId); groups.push(groupId);
return MatrixClientPeg.get().sendStateEvent(roomId, 'm.room.related_groups', {groups}, ''); return MatrixClientPeg.get().sendStateEvent(roomId, 'm.room.related_groups', { groups }, '');
} }
}); });
})).then(() => { })).then(() => {
@ -152,7 +152,7 @@ function _onGroupAddRoomFinished(groupId, addrs, addRoomsPublicly) {
{ {
title: _t( title: _t(
"Failed to add the following rooms to %(groupId)s:", "Failed to add the following rooms to %(groupId)s:",
{groupId}, { groupId },
), ),
description: errorList.join(", "), description: errorList.join(", "),
}, },

View file

@ -138,7 +138,7 @@ export function getHtmlText(insaneHtml: string): string {
selfClosing: [], selfClosing: [],
allowedSchemes: [], allowedSchemes: [],
disallowedTagsMode: 'discard', disallowedTagsMode: 'discard',
}) });
} }
/** /**
@ -183,7 +183,7 @@ const transformTags: IExtendedSanitizeOptions["transformTags"] = { // custom to
// images" preference is disabled. Future work might expose some UI to reveal them // images" preference is disabled. Future work might expose some UI to reveal them
// like standalone image events have. // like standalone image events have.
if (!attribs.src || !attribs.src.startsWith('mxc://') || !SettingsStore.getValue("showImages")) { if (!attribs.src || !attribs.src.startsWith('mxc://') || !SettingsStore.getValue("showImages")) {
return { tagName, attribs: {}}; return { tagName, attribs: {} };
} }
const width = Number(attribs.width) || 800; const width = Number(attribs.width) || 800;
const height = Number(attribs.height) || 600; const height = Number(attribs.height) || 600;

View file

@ -17,7 +17,7 @@ limitations under the License.
import { SERVICE_TYPES } from 'matrix-js-sdk/src/service-types'; import { SERVICE_TYPES } from 'matrix-js-sdk/src/service-types';
import { createClient } from 'matrix-js-sdk/src/matrix'; import { createClient } from 'matrix-js-sdk/src/matrix';
import {MatrixClientPeg} from './MatrixClientPeg'; import { MatrixClientPeg } from './MatrixClientPeg';
import Modal from './Modal'; import Modal from './Modal';
import * as sdk from './index'; import * as sdk from './index';
import { _t } from './languageHandler'; import { _t } from './languageHandler';

View file

@ -156,7 +156,7 @@ const messageComposerBindings = (): KeyBinding<MessageComposerAction>[] => {
} }
} }
return bindings; return bindings;
} };
const autocompleteBindings = (): KeyBinding<AutocompleteAction>[] => { const autocompleteBindings = (): KeyBinding<AutocompleteAction>[] => {
return [ return [
@ -207,7 +207,7 @@ const autocompleteBindings = (): KeyBinding<AutocompleteAction>[] => {
}, },
}, },
]; ];
} };
const roomListBindings = (): KeyBinding<RoomListAction>[] => { const roomListBindings = (): KeyBinding<RoomListAction>[] => {
return [ return [
@ -248,7 +248,7 @@ const roomListBindings = (): KeyBinding<RoomListAction>[] => {
}, },
}, },
]; ];
} };
const roomBindings = (): KeyBinding<RoomAction>[] => { const roomBindings = (): KeyBinding<RoomAction>[] => {
const bindings: KeyBinding<RoomAction>[] = [ const bindings: KeyBinding<RoomAction>[] = [
@ -312,7 +312,7 @@ const roomBindings = (): KeyBinding<RoomAction>[] => {
} }
return bindings; return bindings;
} };
const navigationBindings = (): KeyBinding<NavigationAction>[] => { const navigationBindings = (): KeyBinding<NavigationAction>[] => {
return [ return [
@ -396,7 +396,7 @@ const navigationBindings = (): KeyBinding<NavigationAction>[] => {
}, },
}, },
]; ];
} };
export const defaultBindingsProvider: IKeyBindingsProvider = { export const defaultBindingsProvider: IKeyBindingsProvider = {
getMessageComposerBindings: messageComposerBindings, getMessageComposerBindings: messageComposerBindings,
@ -404,4 +404,4 @@ export const defaultBindingsProvider: IKeyBindingsProvider = {
getRoomListBindings: roomListBindings, getRoomListBindings: roomListBindings,
getRoomBindings: roomBindings, getRoomBindings: roomBindings,
getNavigationBindings: navigationBindings, getNavigationBindings: navigationBindings,
} };

View file

@ -140,12 +140,12 @@ export type KeyCombo = {
ctrlKey?: boolean; ctrlKey?: boolean;
metaKey?: boolean; metaKey?: boolean;
shiftKey?: boolean; shiftKey?: boolean;
} };
export type KeyBinding<T extends string> = { export type KeyBinding<T extends string> = {
action: T; action: T;
keyCombo: KeyCombo; keyCombo: KeyCombo;
} };
/** /**
* Helper method to check if a KeyboardEvent matches a KeyCombo * Helper method to check if a KeyboardEvent matches a KeyCombo

View file

@ -20,9 +20,9 @@ limitations under the License.
import { createClient } from 'matrix-js-sdk/src/matrix'; import { createClient } from 'matrix-js-sdk/src/matrix';
import { InvalidStoreError } from "matrix-js-sdk/src/errors"; import { InvalidStoreError } from "matrix-js-sdk/src/errors";
import { MatrixClient } from "matrix-js-sdk/src/client"; import { MatrixClient } from "matrix-js-sdk/src/client";
import {decryptAES, encryptAES} from "matrix-js-sdk/src/crypto/aes"; import { decryptAES, encryptAES, IEncryptedPayload } from "matrix-js-sdk/src/crypto/aes";
import {IMatrixClientCreds, MatrixClientPeg} from './MatrixClientPeg'; import { IMatrixClientCreds, MatrixClientPeg } from './MatrixClientPeg';
import SecurityCustomisations from "./customisations/Security"; import SecurityCustomisations from "./customisations/Security";
import EventIndexPeg from './indexing/EventIndexPeg'; import EventIndexPeg from './indexing/EventIndexPeg';
import createMatrixClient from './utils/createMatrixClient'; import createMatrixClient from './utils/createMatrixClient';
@ -41,17 +41,17 @@ import * as StorageManager from './utils/StorageManager';
import SettingsStore from "./settings/SettingsStore"; import SettingsStore from "./settings/SettingsStore";
import TypingStore from "./stores/TypingStore"; import TypingStore from "./stores/TypingStore";
import ToastStore from "./stores/ToastStore"; import ToastStore from "./stores/ToastStore";
import {IntegrationManagers} from "./integrations/IntegrationManagers"; import { IntegrationManagers } from "./integrations/IntegrationManagers";
import {Mjolnir} from "./mjolnir/Mjolnir"; import { Mjolnir } from "./mjolnir/Mjolnir";
import DeviceListener from "./DeviceListener"; import DeviceListener from "./DeviceListener";
import {Jitsi} from "./widgets/Jitsi"; import { Jitsi } from "./widgets/Jitsi";
import {SSO_HOMESERVER_URL_KEY, SSO_ID_SERVER_URL_KEY, SSO_IDP_ID_KEY} from "./BasePlatform"; import { SSO_HOMESERVER_URL_KEY, SSO_ID_SERVER_URL_KEY, SSO_IDP_ID_KEY } from "./BasePlatform";
import ThreepidInviteStore from "./stores/ThreepidInviteStore"; import ThreepidInviteStore from "./stores/ThreepidInviteStore";
import CountlyAnalytics from "./CountlyAnalytics"; import CountlyAnalytics from "./CountlyAnalytics";
import CallHandler from './CallHandler'; import CallHandler from './CallHandler';
import LifecycleCustomisations from "./customisations/Lifecycle"; import LifecycleCustomisations from "./customisations/Lifecycle";
import ErrorDialog from "./components/views/dialogs/ErrorDialog"; import ErrorDialog from "./components/views/dialogs/ErrorDialog";
import {_t} from "./languageHandler"; import { _t } from "./languageHandler";
const HOMESERVER_URL_KEY = "mx_hs_url"; const HOMESERVER_URL_KEY = "mx_hs_url";
const ID_SERVER_URL_KEY = "mx_is_url"; const ID_SERVER_URL_KEY = "mx_is_url";
@ -154,7 +154,7 @@ export async function loadSession(opts: ILoadSessionOpts = {}): Promise<boolean>
* return [null, null]. * return [null, null].
*/ */
export async function getStoredSessionOwner(): Promise<[string, boolean]> { export async function getStoredSessionOwner(): Promise<[string, boolean]> {
const {hsUrl, userId, hasAccessToken, isGuest} = await getStoredSessionVars(); const { hsUrl, userId, hasAccessToken, isGuest } = await getStoredSessionVars();
return hsUrl && userId && hasAccessToken ? [userId, isGuest] : [null, null]; return hsUrl && userId && hasAccessToken ? [userId, isGuest] : [null, null];
} }
@ -303,7 +303,7 @@ export interface IStoredSession {
hsUrl: string; hsUrl: string;
isUrl: string; isUrl: string;
hasAccessToken: boolean; hasAccessToken: boolean;
accessToken: string | object; accessToken: string | IEncryptedPayload;
userId: string; userId: string;
deviceId: string; deviceId: string;
isGuest: boolean; isGuest: boolean;
@ -346,11 +346,11 @@ export async function getStoredSessionVars(): Promise<IStoredSession> {
isGuest = localStorage.getItem("matrix-is-guest") === "true"; isGuest = localStorage.getItem("matrix-is-guest") === "true";
} }
return {hsUrl, isUrl, hasAccessToken, accessToken, userId, deviceId, isGuest}; return { hsUrl, isUrl, hasAccessToken, accessToken, userId, deviceId, isGuest };
} }
// The pickle key is a string of unspecified length and format. For AES, we // The pickle key is a string of unspecified length and format. For AES, we
// need a 256-bit Uint8Array. So we HKDF the pickle key to generate the AES // need a 256-bit Uint8Array. So we HKDF the pickle key to generate the AES
// key. The AES key should be zeroed after it is used. // key. The AES key should be zeroed after it is used.
async function pickleKeyToAesKey(pickleKey: string): Promise<Uint8Array> { async function pickleKeyToAesKey(pickleKey: string): Promise<Uint8Array> {
const pickleKeyBuffer = new Uint8Array(pickleKey.length); const pickleKeyBuffer = new Uint8Array(pickleKey.length);
@ -402,7 +402,7 @@ export async function restoreFromLocalStorage(opts?: { ignoreGuest?: boolean }):
return false; return false;
} }
const {hsUrl, isUrl, hasAccessToken, accessToken, userId, deviceId, isGuest} = await getStoredSessionVars(); const { hsUrl, isUrl, hasAccessToken, accessToken, userId, deviceId, isGuest } = await getStoredSessionVars();
if (hasAccessToken && !accessToken) { if (hasAccessToken && !accessToken) {
abortLogin(); abortLogin();
@ -495,7 +495,7 @@ export async function setLoggedIn(credentials: IMatrixClientCreds): Promise<Matr
console.log("Pickle key not created"); console.log("Pickle key not created");
} }
return doSetLoggedIn(Object.assign({}, credentials, {pickleKey}), true); return doSetLoggedIn(Object.assign({}, credentials, { pickleKey }), true);
} }
/** /**
@ -562,7 +562,7 @@ async function doSetLoggedIn(
// //
// we fire it *synchronously* to make sure it fires before on_logged_in. // we fire it *synchronously* to make sure it fires before on_logged_in.
// (dis.dispatch uses `setTimeout`, which does not guarantee ordering.) // (dis.dispatch uses `setTimeout`, which does not guarantee ordering.)
dis.dispatch({action: 'on_logging_in'}, true); dis.dispatch({ action: 'on_logging_in' }, true);
if (clearStorageEnabled) { if (clearStorageEnabled) {
await clearStorage(); await clearStorage();
@ -745,7 +745,7 @@ export function softLogout(): void {
// Ensure that we dispatch a view change **before** stopping the client so // Ensure that we dispatch a view change **before** stopping the client so
// so that React components unmount first. This avoids React soft crashes // so that React components unmount first. This avoids React soft crashes
// that can occur when components try to use a null client. // that can occur when components try to use a null client.
dis.dispatch({action: 'on_client_not_viable'}); // generic version of on_logged_out dis.dispatch({ action: 'on_client_not_viable' }); // generic version of on_logged_out
stopMatrixClient(/*unsetClient=*/false); stopMatrixClient(/*unsetClient=*/false);
// DO NOT CALL LOGOUT. A soft logout preserves data, logout does not. // DO NOT CALL LOGOUT. A soft logout preserves data, logout does not.
@ -772,7 +772,7 @@ async function startMatrixClient(startSyncing = true): Promise<void> {
// to add listeners for the 'sync' event so otherwise we'd have // to add listeners for the 'sync' event so otherwise we'd have
// a race condition (and we need to dispatch synchronously for this // a race condition (and we need to dispatch synchronously for this
// to work). // to work).
dis.dispatch({action: 'will_start_client'}, true); dis.dispatch({ action: 'will_start_client' }, true);
// reset things first just in case // reset things first just in case
TypingStore.sharedInstance().reset(); TypingStore.sharedInstance().reset();
@ -814,7 +814,7 @@ async function startMatrixClient(startSyncing = true): Promise<void> {
// dispatch that we finished starting up to wire up any other bits // dispatch that we finished starting up to wire up any other bits
// of the matrix client that cannot be set prior to starting up. // of the matrix client that cannot be set prior to starting up.
dis.dispatch({action: 'client_started'}); dis.dispatch({ action: 'client_started' });
if (isSoftLogout()) { if (isSoftLogout()) {
softLogout(); softLogout();
@ -830,9 +830,9 @@ export async function onLoggedOut(): Promise<void> {
// Ensure that we dispatch a view change **before** stopping the client so // Ensure that we dispatch a view change **before** stopping the client so
// so that React components unmount first. This avoids React soft crashes // so that React components unmount first. This avoids React soft crashes
// that can occur when components try to use a null client. // that can occur when components try to use a null client.
dis.dispatch({action: 'on_logged_out'}, true); dis.dispatch({ action: 'on_logged_out' }, true);
stopMatrixClient(); stopMatrixClient();
await clearStorage({deleteEverything: true}); await clearStorage({ deleteEverything: true });
LifecycleCustomisations.onLoggedOutAndStorageCleared?.(); LifecycleCustomisations.onLoggedOutAndStorageCleared?.();
} }

View file

@ -16,7 +16,7 @@ limitations under the License.
*/ */
// @ts-ignore - XXX: tsc doesn't like this: our js-sdk imports are complex so this isn't surprising // @ts-ignore - XXX: tsc doesn't like this: our js-sdk imports are complex so this isn't surprising
import {createClient} from "matrix-js-sdk/src/matrix"; import { createClient } from "matrix-js-sdk/src/matrix";
import { MatrixClient } from "matrix-js-sdk/src/client"; import { MatrixClient } from "matrix-js-sdk/src/client";
import { IMatrixClientCreds } from "./MatrixClientPeg"; import { IMatrixClientCreds } from "./MatrixClientPeg";
import SecurityCustomisations from "./customisations/Security"; import SecurityCustomisations from "./customisations/Security";
@ -190,7 +190,6 @@ export default class Login {
} }
} }
/** /**
* Send a login request to the given server, and format the response * Send a login request to the given server, and format the response
* as a MatrixClientCreds * as a MatrixClientCreds

View file

@ -18,22 +18,22 @@ limitations under the License.
*/ */
import { ICreateClientOpts } from 'matrix-js-sdk/src/matrix'; import { ICreateClientOpts } from 'matrix-js-sdk/src/matrix';
import {MatrixClient} from 'matrix-js-sdk/src/client'; import { MatrixClient } from 'matrix-js-sdk/src/client';
import {MemoryStore} from 'matrix-js-sdk/src/store/memory'; import { MemoryStore } from 'matrix-js-sdk/src/store/memory';
import * as utils from 'matrix-js-sdk/src/utils'; import * as utils from 'matrix-js-sdk/src/utils';
import {EventTimeline} from 'matrix-js-sdk/src/models/event-timeline'; import { EventTimeline } from 'matrix-js-sdk/src/models/event-timeline';
import {EventTimelineSet} from 'matrix-js-sdk/src/models/event-timeline-set'; import { EventTimelineSet } from 'matrix-js-sdk/src/models/event-timeline-set';
import * as sdk from './index'; import * as sdk from './index';
import createMatrixClient from './utils/createMatrixClient'; import createMatrixClient from './utils/createMatrixClient';
import SettingsStore from './settings/SettingsStore'; import SettingsStore from './settings/SettingsStore';
import MatrixActionCreators from './actions/MatrixActionCreators'; import MatrixActionCreators from './actions/MatrixActionCreators';
import Modal from './Modal'; import Modal from './Modal';
import {verificationMethods} from 'matrix-js-sdk/src/crypto'; import { verificationMethods } from 'matrix-js-sdk/src/crypto';
import MatrixClientBackedSettingsHandler from "./settings/handlers/MatrixClientBackedSettingsHandler"; import MatrixClientBackedSettingsHandler from "./settings/handlers/MatrixClientBackedSettingsHandler";
import * as StorageManager from './utils/StorageManager'; import * as StorageManager from './utils/StorageManager';
import IdentityAuthClient from './IdentityAuthClient'; import IdentityAuthClient from './IdentityAuthClient';
import { crossSigningCallbacks, tryToUnlockSecretStorageWithDehydrationKey } from './SecurityManager'; import { crossSigningCallbacks, tryToUnlockSecretStorageWithDehydrationKey } from './SecurityManager';
import {SHOW_QR_CODE_METHOD} from "matrix-js-sdk/src/crypto/verification/QRCode"; import { SHOW_QR_CODE_METHOD } from "matrix-js-sdk/src/crypto/verification/QRCode";
import SecurityCustomisations from "./customisations/Security"; import SecurityCustomisations from "./customisations/Security";
export interface IMatrixClientCreds { export interface IMatrixClientCreds {

View file

@ -15,14 +15,13 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import classNames from 'classnames'; import classNames from 'classnames';
import Analytics from './Analytics'; import Analytics from './Analytics';
import dis from './dispatcher/dispatcher'; import dis from './dispatcher/dispatcher';
import {defer} from './utils/promise'; import { defer } from './utils/promise';
import AsyncWrapper from './AsyncWrapper'; import AsyncWrapper from './AsyncWrapper';
const DIALOG_CONTAINER_ID = "mx_Dialog_Container"; const DIALOG_CONTAINER_ID = "mx_Dialog_Container";
@ -193,7 +192,7 @@ export class ModalManager {
modal.elem = <AsyncWrapper key={modalCount} prom={prom} {...props} onFinished={closeDialog} />; modal.elem = <AsyncWrapper key={modalCount} prom={prom} {...props} onFinished={closeDialog} />;
modal.close = closeDialog; modal.close = closeDialog;
return {modal, closeDialog, onFinishedProm}; return { modal, closeDialog, onFinishedProm };
} }
private getCloseFn<T extends any[]>( private getCloseFn<T extends any[]>(
@ -282,7 +281,7 @@ export class ModalManager {
isStaticModal = false, isStaticModal = false,
options: IOptions<T> = {}, options: IOptions<T> = {},
): IHandle<T> { ): IHandle<T> {
const {modal, closeDialog, onFinishedProm} = this.buildModal<T>(prom, props, className, options); const { modal, closeDialog, onFinishedProm } = this.buildModal<T>(prom, props, className, options);
if (isPriorityModal) { if (isPriorityModal) {
// XXX: This is destructive // XXX: This is destructive
this.priorityModal = modal; this.priorityModal = modal;
@ -305,7 +304,7 @@ export class ModalManager {
props?: IProps<T>, props?: IProps<T>,
className?: string, className?: string,
): IHandle<T> { ): IHandle<T> {
const {modal, closeDialog, onFinishedProm} = this.buildModal<T>(prom, props, className, {}); const { modal, closeDialog, onFinishedProm } = this.buildModal<T>(prom, props, className, {});
this.modals.push(modal); this.modals.push(modal);
this.reRender(); this.reRender();

View file

@ -32,11 +32,11 @@ import { _t } from './languageHandler';
import Modal from './Modal'; import Modal from './Modal';
import SettingsStore from "./settings/SettingsStore"; import SettingsStore from "./settings/SettingsStore";
import { hideToast as hideNotificationsToast } from "./toasts/DesktopNotificationsToast"; import { hideToast as hideNotificationsToast } from "./toasts/DesktopNotificationsToast";
import {SettingLevel} from "./settings/SettingLevel"; import { SettingLevel } from "./settings/SettingLevel";
import {isPushNotifyDisabled} from "./settings/controllers/NotificationControllers"; import { isPushNotifyDisabled } from "./settings/controllers/NotificationControllers";
import RoomViewStore from "./stores/RoomViewStore"; import RoomViewStore from "./stores/RoomViewStore";
import UserActivity from "./UserActivity"; import UserActivity from "./UserActivity";
import {mediaFromMxc} from "./customisations/Media"; import { mediaFromMxc } from "./customisations/Media";
/* /*
* Dispatches: * Dispatches:

View file

@ -16,10 +16,10 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {MatrixClientPeg} from "./MatrixClientPeg"; import { MatrixClientPeg } from "./MatrixClientPeg";
import dis from "./dispatcher/dispatcher"; import dis from "./dispatcher/dispatcher";
import Timer from './utils/Timer'; import Timer from './utils/Timer';
import {ActionPayload} from "./dispatcher/payloads"; import { ActionPayload } from "./dispatcher/payloads";
// Time in ms after that a user is considered as unavailable/away // Time in ms after that a user is considered as unavailable/away
const UNAVAILABLE_TIME_MS = 3 * 60 * 1000; // 3 mins const UNAVAILABLE_TIME_MS = 3 * 60 * 1000; // 3 mins
@ -78,7 +78,7 @@ class Presence {
this.setState(State.Online); this.setState(State.Online);
this.unavailableTimer.restart(); this.unavailableTimer.restart();
} }
} };
/** /**
* Set the presence state. * Set the presence state.
@ -98,7 +98,7 @@ class Presence {
} }
try { try {
await MatrixClientPeg.get().setPresence({presence: this.state}); await MatrixClientPeg.get().setPresence({ presence: this.state });
console.info("Presence:", newState); console.info("Presence:", newState);
} catch (err) { } catch (err) {
console.error("Failed to set presence:", err); console.error("Failed to set presence:", err);

View file

@ -53,16 +53,16 @@ export async function startAnyRegistrationFlow(options) {
extraButtons: [ extraButtons: [
<button key="start_login" onClick={() => { <button key="start_login" onClick={() => {
modal.close(); modal.close();
dis.dispatch({action: 'start_login', screenAfterLogin: options.screen_after}); dis.dispatch({ action: 'start_login', screenAfterLogin: options.screen_after });
}}>{ _t('Sign In') }</button>, }}>{ _t('Sign In') }</button>,
], ],
onFinished: (proceed) => { onFinished: (proceed) => {
if (proceed) { if (proceed) {
dis.dispatch({action: 'start_registration', screenAfterLogin: options.screen_after}); dis.dispatch({ action: 'start_registration', screenAfterLogin: options.screen_after });
} else if (options.go_home_on_cancel) { } else if (options.go_home_on_cancel) {
dis.dispatch({action: 'view_home_page'}); dis.dispatch({ action: 'view_home_page' });
} else if (options.go_welcome_on_cancel) { } else if (options.go_welcome_on_cancel) {
dis.dispatch({action: 'view_welcome_page'}); dis.dispatch({ action: 'view_welcome_page' });
} }
}, },
}); });

View file

@ -31,6 +31,6 @@ export function textualPowerLevel(level: number, usersDefault: number): string {
if (LEVEL_ROLE_MAP[level]) { if (LEVEL_ROLE_MAP[level]) {
return LEVEL_ROLE_MAP[level]; return LEVEL_ROLE_MAP[level];
} else { } else {
return _t("Custom (%(level)s)", {level}); return _t("Custom (%(level)s)", { level });
} }
} }

View file

@ -53,7 +53,7 @@ export function showStartChatInviteDialog(initialText = ""): void {
// This dialog handles the room creation internally - we don't need to worry about it. // This dialog handles the room creation internally - we don't need to worry about it.
const InviteDialog = sdk.getComponent("dialogs.InviteDialog"); const InviteDialog = sdk.getComponent("dialogs.InviteDialog");
Modal.createTrackedDialog( Modal.createTrackedDialog(
'Start DM', '', InviteDialog, {kind: KIND_DM, initialText}, 'Start DM', '', InviteDialog, { kind: KIND_DM, initialText },
/*className=*/null, /*isPriority=*/false, /*isStatic=*/true, /*className=*/null, /*isPriority=*/false, /*isStatic=*/true,
); );
} }
@ -72,7 +72,7 @@ export function showRoomInviteDialog(roomId: string, initialText = ""): void {
export function showCommunityRoomInviteDialog(roomId: string, communityName: string): void { export function showCommunityRoomInviteDialog(roomId: string, communityName: string): void {
Modal.createTrackedDialog( Modal.createTrackedDialog(
'Invite Users to Community', '', CommunityPrototypeInviteDialog, {communityName, roomId}, 'Invite Users to Community', '', CommunityPrototypeInviteDialog, { communityName, roomId },
/*className=*/null, /*isPriority=*/false, /*isStatic=*/true, /*className=*/null, /*isPriority=*/false, /*isStatic=*/true,
); );
} }
@ -133,7 +133,7 @@ export function showAnyInviteErrors(
// pointless for us to list who failed exactly. // pointless for us to list who failed exactly.
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog('Failed to invite users to the room', '', ErrorDialog, { Modal.createTrackedDialog('Failed to invite users to the room', '', ErrorDialog, {
title: _t("Failed to invite users to the room:", {roomName: room.name}), title: _t("Failed to invite users to the room:", { roomName: room.name }),
description: inviter.getErrorText(failedUsers[0]), description: inviter.getErrorText(failedUsers[0]),
}); });
return false; return false;

View file

@ -15,8 +15,8 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {MatrixClientPeg} from './MatrixClientPeg'; import { MatrixClientPeg } from './MatrixClientPeg';
import {PushProcessor} from 'matrix-js-sdk/src/pushprocessor'; import { PushProcessor } from 'matrix-js-sdk/src/pushprocessor';
export const ALL_MESSAGES_LOUD = 'all_messages_loud'; export const ALL_MESSAGES_LOUD = 'all_messages_loud';
export const ALL_MESSAGES = 'all_messages'; export const ALL_MESSAGES = 'all_messages';
@ -52,7 +52,7 @@ export function aggregateNotificationCount(rooms) {
} }
} }
return result; return result;
}, {count: 0, highlight: false}); }, { count: 0, highlight: false });
} }
export function getRoomHasBadge(room) { export function getRoomHasBadge(room) {

View file

@ -104,7 +104,6 @@ export function setDMRoom(roomId: string, userId: string): Promise<void> {
dmRoomMap[userId] = roomList; dmRoomMap[userId] = roomList;
} }
return MatrixClientPeg.get().setAccountData('m.direct', dmRoomMap); return MatrixClientPeg.get().setAccountData('m.direct', dmRoomMap);
} }

View file

@ -17,12 +17,12 @@ limitations under the License.
import url from 'url'; import url from 'url';
import SettingsStore from "./settings/SettingsStore"; import SettingsStore from "./settings/SettingsStore";
import { Service, startTermsFlow, TermsInteractionCallback, TermsNotSignedError } from './Terms'; import { Service, startTermsFlow, TermsInteractionCallback, TermsNotSignedError } from './Terms';
import {MatrixClientPeg} from "./MatrixClientPeg"; import { MatrixClientPeg } from "./MatrixClientPeg";
import request from "browser-request"; import request from "browser-request";
import SdkConfig from "./SdkConfig"; import SdkConfig from "./SdkConfig";
import {WidgetType} from "./widgets/WidgetType"; import { WidgetType } from "./widgets/WidgetType";
import {SERVICE_TYPES} from "matrix-js-sdk/src/service-types"; import { SERVICE_TYPES } from "matrix-js-sdk/src/service-types";
import { Room } from "matrix-js-sdk/src/models/room"; import { Room } from "matrix-js-sdk/src/models/room";
// The version of the integration manager API we're intending to work with // The version of the integration manager API we're intending to work with
@ -109,7 +109,7 @@ export default class ScalarAuthClient {
request({ request({
method: "GET", method: "GET",
uri: url, uri: url,
qs: {scalar_token: token, v: imApiVersion}, qs: { scalar_token: token, v: imApiVersion },
json: true, json: true,
}, (err, response, body) => { }, (err, response, body) => {
if (err) { if (err) {
@ -189,7 +189,7 @@ export default class ScalarAuthClient {
request({ request({
method: 'POST', method: 'POST',
uri: scalarRestUrl + '/register', uri: scalarRestUrl + '/register',
qs: {v: imApiVersion}, qs: { v: imApiVersion },
body: openidTokenObject, body: openidTokenObject,
json: true, json: true,
}, (err, response, body) => { }, (err, response, body) => {

View file

@ -208,7 +208,6 @@ Example:
] ]
} }
membership_state AND bot_options membership_state AND bot_options
-------------------------------- --------------------------------
Get the content of the "m.room.member" or "m.room.bot.options" state event respectively. Get the content of the "m.room.member" or "m.room.bot.options" state event respectively.
@ -236,15 +235,15 @@ Example:
} }
*/ */
import {MatrixClientPeg} from './MatrixClientPeg'; import { MatrixClientPeg } from './MatrixClientPeg';
import { MatrixEvent } from 'matrix-js-sdk/src/models/event'; import { MatrixEvent } from 'matrix-js-sdk/src/models/event';
import dis from './dispatcher/dispatcher'; import dis from './dispatcher/dispatcher';
import WidgetUtils from './utils/WidgetUtils'; import WidgetUtils from './utils/WidgetUtils';
import RoomViewStore from './stores/RoomViewStore'; import RoomViewStore from './stores/RoomViewStore';
import { _t } from './languageHandler'; import { _t } from './languageHandler';
import {IntegrationManagers} from "./integrations/IntegrationManagers"; import { IntegrationManagers } from "./integrations/IntegrationManagers";
import {WidgetType} from "./widgets/WidgetType"; import { WidgetType } from "./widgets/WidgetType";
import {objectClone} from "./utils/objects"; import { objectClone } from "./utils/objects";
function sendResponse(event, res) { function sendResponse(event, res) {
const data = objectClone(event.data); const data = objectClone(event.data);
@ -608,7 +607,7 @@ const onMessage = function(event) {
} }
if (roomId !== RoomViewStore.getRoomId()) { if (roomId !== RoomViewStore.getRoomId()) {
sendError(event, _t('Room %(roomId)s not visible', {roomId: roomId})); sendError(event, _t('Room %(roomId)s not visible', { roomId: roomId }));
return; return;
} }

View file

@ -15,7 +15,7 @@ limitations under the License.
*/ */
import EventIndexPeg from "./indexing/EventIndexPeg"; import EventIndexPeg from "./indexing/EventIndexPeg";
import {MatrixClientPeg} from "./MatrixClientPeg"; import { MatrixClientPeg } from "./MatrixClientPeg";
const SEARCH_LIMIT = 10; const SEARCH_LIMIT = 10;
@ -43,7 +43,7 @@ async function serverSideSearch(term, roomId = undefined) {
}, },
}; };
const response = await client.search({body: body}); const response = await client.search({ body: body });
const result = { const result = {
response: response, response: response,
@ -498,7 +498,7 @@ async function combinedPagination(searchResult) {
// Fetch events from the server if we have a token for it and if it's the // Fetch events from the server if we have a token for it and if it's the
// local indexes turn or the local index has exhausted its results. // local indexes turn or the local index has exhausted its results.
if (searchResult.serverSideNextBatch && (oldestEventFrom === "local" || !searchArgs.next_batch)) { if (searchResult.serverSideNextBatch && (oldestEventFrom === "local" || !searchArgs.next_batch)) {
const body = {body: searchResult._query, next_batch: searchResult.serverSideNextBatch}; const body = { body: searchResult._query, next_batch: searchResult.serverSideNextBatch };
serverSideResult = await client.search(body); serverSideResult = await client.search(body);
} }

View file

@ -18,7 +18,7 @@ import { ICryptoCallbacks, ISecretStorageKeyInfo } from 'matrix-js-sdk/src/matri
import { MatrixClient } from 'matrix-js-sdk/src/client'; import { MatrixClient } from 'matrix-js-sdk/src/client';
import Modal from './Modal'; import Modal from './Modal';
import * as sdk from './index'; import * as sdk from './index';
import {MatrixClientPeg} from './MatrixClientPeg'; import { MatrixClientPeg } from './MatrixClientPeg';
import { deriveKey } from 'matrix-js-sdk/src/crypto/key_passphrase'; import { deriveKey } from 'matrix-js-sdk/src/crypto/key_passphrase';
import { decodeRecoveryKey } from 'matrix-js-sdk/src/crypto/recoverykey'; import { decodeRecoveryKey } from 'matrix-js-sdk/src/crypto/recoverykey';
import { _t } from './languageHandler'; import { _t } from './languageHandler';
@ -135,7 +135,7 @@ async function getSecretStorageKey(
const keyFromCustomisations = SecurityCustomisations.getSecretStorageKey?.(); const keyFromCustomisations = SecurityCustomisations.getSecretStorageKey?.();
if (keyFromCustomisations) { if (keyFromCustomisations) {
console.log("Using key from security customisations (secret storage)") console.log("Using key from security customisations (secret storage)");
cacheSecretStorageKey(keyId, keyInfo, keyFromCustomisations); cacheSecretStorageKey(keyId, keyInfo, keyFromCustomisations);
return [keyId, keyFromCustomisations]; return [keyId, keyFromCustomisations];
} }
@ -185,7 +185,7 @@ export async function getDehydrationKey(
): Promise<Uint8Array> { ): Promise<Uint8Array> {
const keyFromCustomisations = SecurityCustomisations.getSecretStorageKey?.(); const keyFromCustomisations = SecurityCustomisations.getSecretStorageKey?.();
if (keyFromCustomisations) { if (keyFromCustomisations) {
console.log("Using key from security customisations (dehydration)") console.log("Using key from security customisations (dehydration)");
return keyFromCustomisations; return keyFromCustomisations;
} }
@ -224,7 +224,7 @@ export async function getDehydrationKey(
const key = await inputToKey(input); const key = await inputToKey(input);
// need to copy the key because rehydration (unpickling) will clobber it // need to copy the key because rehydration (unpickling) will clobber it
dehydrationCache = {key: new Uint8Array(key), keyInfo}; dehydrationCache = { key: new Uint8Array(key), keyInfo };
return key; return key;
} }

View file

@ -1,4 +1,3 @@
//@flow
/* /*
Copyright 2017 Aviral Dasgupta Copyright 2017 Aviral Dasgupta
@ -15,10 +14,10 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {clamp} from "lodash"; import { clamp } from "lodash";
import {MatrixEvent} from "matrix-js-sdk/src/models/event"; import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import {SerializedPart} from "./editor/parts"; import { SerializedPart } from "./editor/parts";
import EditorModel from "./editor/model"; import EditorModel from "./editor/model";
interface IHistoryItem { interface IHistoryItem {

View file

@ -21,21 +21,21 @@ import * as React from 'react';
import { User } from "matrix-js-sdk/src/models/user"; import { User } from "matrix-js-sdk/src/models/user";
import * as ContentHelpers from 'matrix-js-sdk/src/content-helpers'; import * as ContentHelpers from 'matrix-js-sdk/src/content-helpers';
import {MatrixClientPeg} from './MatrixClientPeg'; import { MatrixClientPeg } from './MatrixClientPeg';
import dis from './dispatcher/dispatcher'; import dis from './dispatcher/dispatcher';
import * as sdk from './index'; import * as sdk from './index';
import {_t, _td} from './languageHandler'; import { _t, _td } from './languageHandler';
import Modal from './Modal'; import Modal from './Modal';
import MultiInviter from './utils/MultiInviter'; import MultiInviter from './utils/MultiInviter';
import { linkifyAndSanitizeHtml } from './HtmlUtils'; import { linkifyAndSanitizeHtml } from './HtmlUtils';
import QuestionDialog from "./components/views/dialogs/QuestionDialog"; import QuestionDialog from "./components/views/dialogs/QuestionDialog";
import WidgetUtils from "./utils/WidgetUtils"; import WidgetUtils from "./utils/WidgetUtils";
import {textToHtmlRainbow} from "./utils/colour"; import { textToHtmlRainbow } from "./utils/colour";
import { getAddressType } from './UserAddress'; import { getAddressType } from './UserAddress';
import { abbreviateUrl } from './utils/UrlUtils'; import { abbreviateUrl } from './utils/UrlUtils';
import { getDefaultIdentityServerUrl, useDefaultIdentityServer } from './utils/IdentityServerUtils'; import { getDefaultIdentityServerUrl, useDefaultIdentityServer } from './utils/IdentityServerUtils';
import {isPermalinkHost, parsePermalink} from "./utils/permalinks/Permalinks"; import { isPermalinkHost, parsePermalink } from "./utils/permalinks/Permalinks";
import {inviteUsersToRoom} from "./RoomInvite"; import { inviteUsersToRoom } from "./RoomInvite";
import { WidgetType } from "./widgets/WidgetType"; import { WidgetType } from "./widgets/WidgetType";
import { Jitsi } from "./widgets/Jitsi"; import { Jitsi } from "./widgets/Jitsi";
import { parseFragment as parseHtml, Element as ChildElement } from "parse5"; import { parseFragment as parseHtml, Element as ChildElement } from "parse5";
@ -46,10 +46,10 @@ import { Action } from "./dispatcher/actions";
import { EffectiveMembership, getEffectiveMembership, leaveRoomBehaviour } from "./utils/membership"; import { EffectiveMembership, getEffectiveMembership, leaveRoomBehaviour } from "./utils/membership";
import SdkConfig from "./SdkConfig"; import SdkConfig from "./SdkConfig";
import SettingsStore from "./settings/SettingsStore"; import SettingsStore from "./settings/SettingsStore";
import {UIFeature} from "./settings/UIFeature"; import { UIFeature } from "./settings/UIFeature";
import {CHAT_EFFECTS} from "./effects" import { CHAT_EFFECTS } from "./effects";
import CallHandler from "./CallHandler"; import CallHandler from "./CallHandler";
import {guessAndSetDMRoom} from "./Rooms"; import { guessAndSetDMRoom } from "./Rooms";
// XXX: workaround for https://github.com/microsoft/TypeScript/issues/31816 // XXX: workaround for https://github.com/microsoft/TypeScript/issues/31816
interface HTMLInputEvent extends Event { interface HTMLInputEvent extends Event {
@ -143,11 +143,11 @@ export class Command {
} }
function reject(error) { function reject(error) {
return {error}; return { error };
} }
function success(promise?: Promise<any>) { function success(promise?: Promise<any>) {
return {promise}; return { promise };
} }
function successSync(value: any) { function successSync(value: any) {
@ -271,8 +271,8 @@ export const Commands = [
const RoomUpgradeWarningDialog = sdk.getComponent("dialogs.RoomUpgradeWarningDialog"); const RoomUpgradeWarningDialog = sdk.getComponent("dialogs.RoomUpgradeWarningDialog");
const {finished} = Modal.createTrackedDialog('Slash Commands', 'upgrade room confirmation', const { finished } = Modal.createTrackedDialog('Slash Commands', 'upgrade room confirmation',
RoomUpgradeWarningDialog, {roomId: roomId, targetVersion: args}, /*className=*/null, RoomUpgradeWarningDialog, { roomId: roomId, targetVersion: args }, /*className=*/null,
/*isPriority=*/false, /*isStatic=*/true); /*isPriority=*/false, /*isStatic=*/true);
return success(finished.then(async ([resp]) => { return success(finished.then(async ([resp]) => {
@ -288,7 +288,7 @@ export const Commands = [
if (resp.invite) { if (resp.invite) {
checkForUpgradeFn = async (newRoom) => { checkForUpgradeFn = async (newRoom) => {
// The upgradePromise should be done by the time we await it here. // The upgradePromise should be done by the time we await it here.
const {replacement_room: newRoomId} = await upgradePromise; const { replacement_room: newRoomId } = await upgradePromise;
if (newRoom.roomId !== newRoomId) return; if (newRoom.roomId !== newRoomId) return;
const toInvite = [ const toInvite = [
@ -370,7 +370,7 @@ export const Commands = [
return success(promise.then((url) => { return success(promise.then((url) => {
if (!url) return; if (!url) return;
return MatrixClientPeg.get().sendStateEvent(roomId, 'm.room.avatar', {url}, ''); return MatrixClientPeg.get().sendStateEvent(roomId, 'm.room.avatar', { url }, '');
})); }));
}, },
category: CommandCategories.actions, category: CommandCategories.actions,
@ -741,7 +741,7 @@ export const Commands = [
Modal.createTrackedDialog('Slash Commands', 'User ignored', InfoDialog, { Modal.createTrackedDialog('Slash Commands', 'User ignored', InfoDialog, {
title: _t('Ignored user'), title: _t('Ignored user'),
description: <div> description: <div>
<p>{ _t('You are now ignoring %(userId)s', {userId}) }</p> <p>{ _t('You are now ignoring %(userId)s', { userId }) }</p>
</div>, </div>,
}); });
}), }),
@ -772,7 +772,7 @@ export const Commands = [
Modal.createTrackedDialog('Slash Commands', 'User unignored', InfoDialog, { Modal.createTrackedDialog('Slash Commands', 'User unignored', InfoDialog, {
title: _t('Unignored user'), title: _t('Unignored user'),
description: <div> description: <div>
<p>{ _t('You are no longer ignoring %(userId)s', {userId}) }</p> <p>{ _t('You are no longer ignoring %(userId)s', { userId }) }</p>
</div>, </div>,
}); });
}), }),
@ -839,7 +839,7 @@ export const Commands = [
description: _td('Opens the Developer Tools dialog'), description: _td('Opens the Developer Tools dialog'),
runFn: function(roomId) { runFn: function(roomId) {
const DevtoolsDialog = sdk.getComponent('dialogs.DevtoolsDialog'); const DevtoolsDialog = sdk.getComponent('dialogs.DevtoolsDialog');
Modal.createDialog(DevtoolsDialog, {roomId}); Modal.createDialog(DevtoolsDialog, { roomId });
return success(); return success();
}, },
category: CommandCategories.advanced, category: CommandCategories.advanced,
@ -951,7 +951,7 @@ export const Commands = [
{ {
_t('The signing key you provided matches the signing key you received ' + _t('The signing key you provided matches the signing key you received ' +
'from %(userId)s\'s session %(deviceId)s. Session marked as verified.', 'from %(userId)s\'s session %(deviceId)s. Session marked as verified.',
{userId, deviceId}) { userId, deviceId })
} }
</p> </p>
</div>, </div>,
@ -1172,11 +1172,11 @@ export const Commands = [
}; };
MatrixClientPeg.get().sendMessage(roomId, content); MatrixClientPeg.get().sendMessage(roomId, content);
} }
dis.dispatch({action: `effects.${effect.command}`}); dis.dispatch({ action: `effects.${effect.command}` });
})()); })());
}, },
category: CommandCategories.effects, category: CommandCategories.effects,
}) });
}), }),
]; ];
@ -1205,7 +1205,7 @@ export function parseCommandString(input: string) {
cmd = input; cmd = input;
} }
return {cmd, args}; return { cmd, args };
} }
/** /**
@ -1217,7 +1217,7 @@ export function parseCommandString(input: string) {
* Returns null if the input didn't match a command. * Returns null if the input didn't match a command.
*/ */
export function getCommand(input: string) { export function getCommand(input: string) {
const {cmd, args} = parseCommandString(input); const { cmd, args } = parseCommandString(input);
if (CommandMap.has(cmd) && CommandMap.get(cmd).isEnabled()) { if (CommandMap.has(cmd) && CommandMap.get(cmd).isEnabled()) {
return { return {

View file

@ -16,7 +16,7 @@ limitations under the License.
import classNames from 'classnames'; import classNames from 'classnames';
import {MatrixClientPeg} from './MatrixClientPeg'; import { MatrixClientPeg } from './MatrixClientPeg';
import * as sdk from '.'; import * as sdk from '.';
import Modal from './Modal'; import Modal from './Modal';
@ -117,7 +117,7 @@ export async function startTermsFlow(
// but that is not a thing the API supports, so probably best to just show // but that is not a thing the API supports, so probably best to just show
// things they've not agreed to yet. // things they've not agreed to yet.
const unagreedPoliciesAndServicePairs = []; const unagreedPoliciesAndServicePairs = [];
for (const {service, policies} of policiesAndServicePairs) { for (const { service, policies } of policiesAndServicePairs) {
const unagreedPolicies = {}; const unagreedPolicies = {};
for (const [policyName, policy] of Object.entries(policies)) { for (const [policyName, policy] of Object.entries(policies)) {
let policyAgreed = false; let policyAgreed = false;
@ -131,7 +131,7 @@ export async function startTermsFlow(
if (!policyAgreed) unagreedPolicies[policyName] = policy; if (!policyAgreed) unagreedPolicies[policyName] = policy;
} }
if (Object.keys(unagreedPolicies).length > 0) { if (Object.keys(unagreedPolicies).length > 0) {
unagreedPoliciesAndServicePairs.push({service, policies: unagreedPolicies}); unagreedPoliciesAndServicePairs.push({ service, policies: unagreedPolicies });
} }
} }
@ -148,7 +148,7 @@ export async function startTermsFlow(
// We only ever add to the set of URLs, so if anything has changed then we'd see a different length // We only ever add to the set of URLs, so if anything has changed then we'd see a different length
if (agreedUrlSet.size !== numAcceptedBeforeAgreement) { if (agreedUrlSet.size !== numAcceptedBeforeAgreement) {
const newAcceptedTerms = {accepted: Array.from(agreedUrlSet)}; const newAcceptedTerms = { accepted: Array.from(agreedUrlSet) };
await MatrixClientPeg.get().setAccountData('m.accepted_terms', newAcceptedTerms); await MatrixClientPeg.get().setAccountData('m.accepted_terms', newAcceptedTerms);
} }

View file

@ -15,13 +15,13 @@ limitations under the License.
*/ */
import React from 'react'; import React from 'react';
import {MatrixClientPeg} from './MatrixClientPeg'; import { MatrixClientPeg } from './MatrixClientPeg';
import { _t } from './languageHandler'; import { _t } from './languageHandler';
import * as Roles from './Roles'; import * as Roles from './Roles';
import {isValid3pidInvite} from "./RoomInvite"; import { isValid3pidInvite } from "./RoomInvite";
import SettingsStore from "./settings/SettingsStore"; import SettingsStore from "./settings/SettingsStore";
import {ALL_RULE_TYPES, ROOM_RULE_TYPES, SERVER_RULE_TYPES, USER_RULE_TYPES} from "./mjolnir/BanList"; import { ALL_RULE_TYPES, ROOM_RULE_TYPES, SERVER_RULE_TYPES, USER_RULE_TYPES } from "./mjolnir/BanList";
import {WIDGET_LAYOUT_EVENT_TYPE} from "./stores/widgets/WidgetLayoutStore"; import { WIDGET_LAYOUT_EVENT_TYPE } from "./stores/widgets/WidgetLayoutStore";
import { RightPanelPhases } from './stores/RightPanelStorePhases'; import { RightPanelPhases } from './stores/RightPanelStorePhases';
import { Action } from './dispatcher/actions'; import { Action } from './dispatcher/actions';
import defaultDispatcher from './dispatcher/dispatcher'; import defaultDispatcher from './dispatcher/dispatcher';
@ -112,7 +112,7 @@ function textForMemberEvent(ev): () => string | null {
targetName, targetName,
reason, reason,
}) })
: _t('%(senderName)s withdrew %(targetName)s\'s invitation', { senderName, targetName }) : _t('%(senderName)s withdrew %(targetName)s\'s invitation', { senderName, targetName });
} else if (prevContent.membership === "join") { } else if (prevContent.membership === "join") {
return () => reason return () => reason
? _t('%(senderName)s kicked %(targetName)s: %(reason)s', { ? _t('%(senderName)s kicked %(targetName)s: %(reason)s', {
@ -139,7 +139,7 @@ function textForRoomNameEvent(ev): () => string | null {
const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender(); const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
if (!ev.getContent().name || ev.getContent().name.trim().length === 0) { if (!ev.getContent().name || ev.getContent().name.trim().length === 0) {
return () => _t('%(senderDisplayName)s removed the room name.', {senderDisplayName}); return () => _t('%(senderDisplayName)s removed the room name.', { senderDisplayName });
} }
if (ev.getPrevContent().name) { if (ev.getPrevContent().name) {
return () => _t('%(senderDisplayName)s changed the room name from %(oldRoomName)s to %(newRoomName)s.', { return () => _t('%(senderDisplayName)s changed the room name from %(oldRoomName)s to %(newRoomName)s.', {
@ -156,7 +156,7 @@ function textForRoomNameEvent(ev): () => string | null {
function textForTombstoneEvent(ev): () => string | null { function textForTombstoneEvent(ev): () => string | null {
const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender(); const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
return () => _t('%(senderDisplayName)s upgraded this room.', {senderDisplayName}); return () => _t('%(senderDisplayName)s upgraded this room.', { senderDisplayName });
} }
function textForJoinRulesEvent(ev): () => string | null { function textForJoinRulesEvent(ev): () => string | null {
@ -183,9 +183,9 @@ function textForGuestAccessEvent(ev): () => string | null {
const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender(); const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
switch (ev.getContent().guest_access) { switch (ev.getContent().guest_access) {
case "can_join": case "can_join":
return () => _t('%(senderDisplayName)s has allowed guests to join the room.', {senderDisplayName}); return () => _t('%(senderDisplayName)s has allowed guests to join the room.', { senderDisplayName });
case "forbidden": case "forbidden":
return () => _t('%(senderDisplayName)s has prevented guests from joining the room.', {senderDisplayName}); return () => _t('%(senderDisplayName)s has prevented guests from joining the room.', { senderDisplayName });
default: default:
// There's no other options we can expect, however just for safety's sake we'll do this. // There's no other options we can expect, however just for safety's sake we'll do this.
return () => _t('%(senderDisplayName)s changed guest access to %(rule)s', { return () => _t('%(senderDisplayName)s changed guest access to %(rule)s', {
@ -237,9 +237,9 @@ function textForServerACLEvent(ev): () => string | null {
let getText = null; let getText = null;
if (prev.deny.length === 0 && prev.allow.length === 0) { if (prev.deny.length === 0 && prev.allow.length === 0) {
getText = () => _t("%(senderDisplayName)s set the server ACLs for this room.", {senderDisplayName}); getText = () => _t("%(senderDisplayName)s set the server ACLs for this room.", { senderDisplayName });
} else { } else {
getText = () => _t("%(senderDisplayName)s changed the server ACLs for this room.", {senderDisplayName}); getText = () => _t("%(senderDisplayName)s changed the server ACLs for this room.", { senderDisplayName });
} }
if (!Array.isArray(current.allow)) { if (!Array.isArray(current.allow)) {
@ -262,7 +262,7 @@ function textForMessageEvent(ev): () => string | null {
if (ev.getContent().msgtype === "m.emote") { if (ev.getContent().msgtype === "m.emote") {
message = "* " + senderDisplayName + " " + message; message = "* " + senderDisplayName + " " + message;
} else if (ev.getContent().msgtype === "m.image") { } else if (ev.getContent().msgtype === "m.image") {
message = _t('%(senderDisplayName)s sent an image.', {senderDisplayName}); message = _t('%(senderDisplayName)s sent an image.', { senderDisplayName });
} }
return message; return message;
}; };
@ -323,7 +323,7 @@ function textForCallAnswerEvent(event): () => string | null {
return () => { return () => {
const senderName = event.sender ? event.sender.name : _t('Someone'); const senderName = event.sender ? event.sender.name : _t('Someone');
const supported = MatrixClientPeg.get().supportsVoip() ? '' : _t('(not supported by this browser)'); const supported = MatrixClientPeg.get().supportsVoip() ? '' : _t('(not supported by this browser)');
return _t('%(senderName)s answered the call.', {senderName}) + ' ' + supported; return _t('%(senderName)s answered the call.', { senderName }) + ' ' + supported;
}; };
} }
@ -358,16 +358,16 @@ function textForCallHangupEvent(event): () => string | null {
// Also the correct hangup code as of VoIP v1 (with underscore) // Also the correct hangup code as of VoIP v1 (with underscore)
getReason = () => ''; getReason = () => '';
} else { } else {
getReason = () => _t('(unknown failure: %(reason)s)', {reason: eventContent.reason}); getReason = () => _t('(unknown failure: %(reason)s)', { reason: eventContent.reason });
} }
} }
return () => _t('%(senderName)s ended the call.', {senderName: getSenderName()}) + ' ' + getReason(); return () => _t('%(senderName)s ended the call.', { senderName: getSenderName() }) + ' ' + getReason();
} }
function textForCallRejectEvent(event): () => string | null { function textForCallRejectEvent(event): () => string | null {
return () => { return () => {
const senderName = event.sender ? event.sender.name : _t('Someone'); const senderName = event.sender ? event.sender.name : _t('Someone');
return _t('%(senderName)s declined the call.', {senderName}); return _t('%(senderName)s declined the call.', { senderName });
}; };
} }
@ -424,14 +424,14 @@ function textForHistoryVisibilityEvent(event): () => string | null {
switch (event.getContent().history_visibility) { switch (event.getContent().history_visibility) {
case 'invited': case 'invited':
return () => _t('%(senderName)s made future room history visible to all room members, ' return () => _t('%(senderName)s made future room history visible to all room members, '
+ 'from the point they are invited.', {senderName}); + 'from the point they are invited.', { senderName });
case 'joined': case 'joined':
return () => _t('%(senderName)s made future room history visible to all room members, ' return () => _t('%(senderName)s made future room history visible to all room members, '
+ 'from the point they joined.', {senderName}); + 'from the point they joined.', { senderName });
case 'shared': case 'shared':
return () => _t('%(senderName)s made future room history visible to all room members.', {senderName}); return () => _t('%(senderName)s made future room history visible to all room members.', { senderName });
case 'world_readable': case 'world_readable':
return () => _t('%(senderName)s made future room history visible to anyone.', {senderName}); return () => _t('%(senderName)s made future room history visible to anyone.', { senderName });
default: default:
return () => _t('%(senderName)s made future room history visible to unknown (%(visibility)s).', { return () => _t('%(senderName)s made future room history visible to unknown (%(visibility)s).', {
senderName, senderName,
@ -492,7 +492,7 @@ const onPinnedMessagesClick = (): void => {
phase: RightPanelPhases.PinnedMessages, phase: RightPanelPhases.PinnedMessages,
allowClose: false, allowClose: false,
}); });
} };
function textForPinnedEvent(event: MatrixEvent, allowJSX: boolean): () => string | JSX.Element | null { function textForPinnedEvent(event: MatrixEvent, allowJSX: boolean): () => string | JSX.Element | null {
if (!SettingsStore.getValue("feature_pinning")) return null; if (!SettingsStore.getValue("feature_pinning")) return null;
@ -517,8 +517,8 @@ function textForPinnedEvent(event: MatrixEvent, allowJSX: boolean): () => string
function textForWidgetEvent(event): () => string | null { function textForWidgetEvent(event): () => string | null {
const senderName = event.getSender(); const senderName = event.getSender();
const {name: prevName, type: prevType, url: prevUrl} = event.getPrevContent(); const { name: prevName, type: prevType, url: prevUrl } = event.getPrevContent();
const {name, type, url} = event.getContent() || {}; const { name, type, url } = event.getContent() || {};
let widgetName = name || prevName || type || prevType || ''; let widgetName = name || prevName || type || prevType || '';
// Apply sentence case to widget name // Apply sentence case to widget name
@ -547,68 +547,68 @@ function textForWidgetEvent(event): () => string | null {
function textForWidgetLayoutEvent(event): () => string | null { function textForWidgetLayoutEvent(event): () => string | null {
const senderName = event.sender?.name || event.getSender(); const senderName = event.sender?.name || event.getSender();
return () => _t("%(senderName)s has updated the widget layout", {senderName}); return () => _t("%(senderName)s has updated the widget layout", { senderName });
} }
function textForMjolnirEvent(event): () => string | null { function textForMjolnirEvent(event): () => string | null {
const senderName = event.getSender(); const senderName = event.getSender();
const {entity: prevEntity} = event.getPrevContent(); const { entity: prevEntity } = event.getPrevContent();
const {entity, recommendation, reason} = event.getContent(); const { entity, recommendation, reason } = event.getContent();
// Rule removed // Rule removed
if (!entity) { if (!entity) {
if (USER_RULE_TYPES.includes(event.getType())) { if (USER_RULE_TYPES.includes(event.getType())) {
return () => _t("%(senderName)s removed the rule banning users matching %(glob)s", return () => _t("%(senderName)s removed the rule banning users matching %(glob)s",
{senderName, glob: prevEntity}); { senderName, glob: prevEntity });
} else if (ROOM_RULE_TYPES.includes(event.getType())) { } else if (ROOM_RULE_TYPES.includes(event.getType())) {
return () => _t("%(senderName)s removed the rule banning rooms matching %(glob)s", return () => _t("%(senderName)s removed the rule banning rooms matching %(glob)s",
{senderName, glob: prevEntity}); { senderName, glob: prevEntity });
} else if (SERVER_RULE_TYPES.includes(event.getType())) { } else if (SERVER_RULE_TYPES.includes(event.getType())) {
return () => _t("%(senderName)s removed the rule banning servers matching %(glob)s", return () => _t("%(senderName)s removed the rule banning servers matching %(glob)s",
{senderName, glob: prevEntity}); { senderName, glob: prevEntity });
} }
// Unknown type. We'll say something, but we shouldn't end up here. // Unknown type. We'll say something, but we shouldn't end up here.
return () => _t("%(senderName)s removed a ban rule matching %(glob)s", {senderName, glob: prevEntity}); return () => _t("%(senderName)s removed a ban rule matching %(glob)s", { senderName, glob: prevEntity });
} }
// Invalid rule // Invalid rule
if (!recommendation || !reason) return () => _t(`%(senderName)s updated an invalid ban rule`, {senderName}); if (!recommendation || !reason) return () => _t(`%(senderName)s updated an invalid ban rule`, { senderName });
// Rule updated // Rule updated
if (entity === prevEntity) { if (entity === prevEntity) {
if (USER_RULE_TYPES.includes(event.getType())) { if (USER_RULE_TYPES.includes(event.getType())) {
return () => _t("%(senderName)s updated the rule banning users matching %(glob)s for %(reason)s", return () => _t("%(senderName)s updated the rule banning users matching %(glob)s for %(reason)s",
{senderName, glob: entity, reason}); { senderName, glob: entity, reason });
} else if (ROOM_RULE_TYPES.includes(event.getType())) { } else if (ROOM_RULE_TYPES.includes(event.getType())) {
return () => _t("%(senderName)s updated the rule banning rooms matching %(glob)s for %(reason)s", return () => _t("%(senderName)s updated the rule banning rooms matching %(glob)s for %(reason)s",
{senderName, glob: entity, reason}); { senderName, glob: entity, reason });
} else if (SERVER_RULE_TYPES.includes(event.getType())) { } else if (SERVER_RULE_TYPES.includes(event.getType())) {
return () => _t("%(senderName)s updated the rule banning servers matching %(glob)s for %(reason)s", return () => _t("%(senderName)s updated the rule banning servers matching %(glob)s for %(reason)s",
{senderName, glob: entity, reason}); { senderName, glob: entity, reason });
} }
// Unknown type. We'll say something but we shouldn't end up here. // Unknown type. We'll say something but we shouldn't end up here.
return () => _t("%(senderName)s updated a ban rule matching %(glob)s for %(reason)s", return () => _t("%(senderName)s updated a ban rule matching %(glob)s for %(reason)s",
{senderName, glob: entity, reason}); { senderName, glob: entity, reason });
} }
// New rule // New rule
if (!prevEntity) { if (!prevEntity) {
if (USER_RULE_TYPES.includes(event.getType())) { if (USER_RULE_TYPES.includes(event.getType())) {
return () => _t("%(senderName)s created a rule banning users matching %(glob)s for %(reason)s", return () => _t("%(senderName)s created a rule banning users matching %(glob)s for %(reason)s",
{senderName, glob: entity, reason}); { senderName, glob: entity, reason });
} else if (ROOM_RULE_TYPES.includes(event.getType())) { } else if (ROOM_RULE_TYPES.includes(event.getType())) {
return () => _t("%(senderName)s created a rule banning rooms matching %(glob)s for %(reason)s", return () => _t("%(senderName)s created a rule banning rooms matching %(glob)s for %(reason)s",
{senderName, glob: entity, reason}); { senderName, glob: entity, reason });
} else if (SERVER_RULE_TYPES.includes(event.getType())) { } else if (SERVER_RULE_TYPES.includes(event.getType())) {
return () => _t("%(senderName)s created a rule banning servers matching %(glob)s for %(reason)s", return () => _t("%(senderName)s created a rule banning servers matching %(glob)s for %(reason)s",
{senderName, glob: entity, reason}); { senderName, glob: entity, reason });
} }
// Unknown type. We'll say something but we shouldn't end up here. // Unknown type. We'll say something but we shouldn't end up here.
return () => _t("%(senderName)s created a ban rule matching %(glob)s for %(reason)s", return () => _t("%(senderName)s created a ban rule matching %(glob)s for %(reason)s",
{senderName, glob: entity, reason}); { senderName, glob: entity, reason });
} }
// else the entity !== prevEntity - count as a removal & add // else the entity !== prevEntity - count as a removal & add
@ -616,25 +616,25 @@ function textForMjolnirEvent(event): () => string | null {
return () => _t( return () => _t(
"%(senderName)s changed a rule that was banning users matching %(oldGlob)s to matching " + "%(senderName)s changed a rule that was banning users matching %(oldGlob)s to matching " +
"%(newGlob)s for %(reason)s", "%(newGlob)s for %(reason)s",
{senderName, oldGlob: prevEntity, newGlob: entity, reason}, { senderName, oldGlob: prevEntity, newGlob: entity, reason },
); );
} else if (ROOM_RULE_TYPES.includes(event.getType())) { } else if (ROOM_RULE_TYPES.includes(event.getType())) {
return () => _t( return () => _t(
"%(senderName)s changed a rule that was banning rooms matching %(oldGlob)s to matching " + "%(senderName)s changed a rule that was banning rooms matching %(oldGlob)s to matching " +
"%(newGlob)s for %(reason)s", "%(newGlob)s for %(reason)s",
{senderName, oldGlob: prevEntity, newGlob: entity, reason}, { senderName, oldGlob: prevEntity, newGlob: entity, reason },
); );
} else if (SERVER_RULE_TYPES.includes(event.getType())) { } else if (SERVER_RULE_TYPES.includes(event.getType())) {
return () => _t( return () => _t(
"%(senderName)s changed a rule that was banning servers matching %(oldGlob)s to matching " + "%(senderName)s changed a rule that was banning servers matching %(oldGlob)s to matching " +
"%(newGlob)s for %(reason)s", "%(newGlob)s for %(reason)s",
{senderName, oldGlob: prevEntity, newGlob: entity, reason}, { senderName, oldGlob: prevEntity, newGlob: entity, reason },
); );
} }
// Unknown type. We'll say something but we shouldn't end up here. // Unknown type. We'll say something but we shouldn't end up here.
return () => _t("%(senderName)s updated a ban rule that was matching %(oldGlob)s to matching %(newGlob)s " + return () => _t("%(senderName)s updated a ban rule that was matching %(oldGlob)s to matching %(newGlob)s " +
"for %(reason)s", {senderName, oldGlob: prevEntity, newGlob: entity, reason}); "for %(reason)s", { senderName, oldGlob: prevEntity, newGlob: entity, reason });
} }
interface IHandlers { interface IHandlers {

View file

@ -1,458 +0,0 @@
/*
Copyright 2015 OpenMarket Ltd
Copyright 2017 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
const DEBUG = 0;
// utility to turn #rrggbb or rgb(r,g,b) into [red,green,blue]
function colorToRgb(color) {
if (!color) {
return [0, 0, 0];
}
if (color[0] === '#') {
color = color.slice(1);
if (color.length === 3) {
color = color[0] + color[0] +
color[1] + color[1] +
color[2] + color[2];
}
const val = parseInt(color, 16);
const r = (val >> 16) & 255;
const g = (val >> 8) & 255;
const b = val & 255;
return [r, g, b];
} else {
const match = color.match(/rgb\((.*?),(.*?),(.*?)\)/);
if (match) {
return [
parseInt(match[1]),
parseInt(match[2]),
parseInt(match[3]),
];
}
}
return [0, 0, 0];
}
// utility to turn [red,green,blue] into #rrggbb
function rgbToColor(rgb) {
const val = (rgb[0] << 16) | (rgb[1] << 8) | rgb[2];
return '#' + (0x1000000 + val).toString(16).slice(1);
}
class Tinter {
constructor() {
// The default colour keys to be replaced as referred to in CSS
// (should be overridden by .mx_theme_accentColor and .mx_theme_secondaryAccentColor)
this.keyRgb = [
"rgb(118, 207, 166)", // Vector Green
"rgb(234, 245, 240)", // Vector Light Green
"rgb(211, 239, 225)", // roomsublist-label-bg-color (20% Green overlaid on Light Green)
];
// Some algebra workings for calculating the tint % of Vector Green & Light Green
// x * 118 + (1 - x) * 255 = 234
// x * 118 + 255 - 255 * x = 234
// x * 118 - x * 255 = 234 - 255
// (255 - 118) x = 255 - 234
// x = (255 - 234) / (255 - 118) = 0.16
// The colour keys to be replaced as referred to in SVGs
this.keyHex = [
"#76CFA6", // Vector Green
"#EAF5F0", // Vector Light Green
"#D3EFE1", // roomsublist-label-bg-color (20% Green overlaid on Light Green)
"#FFFFFF", // white highlights of the SVGs (for switching to dark theme)
"#000000", // black lowlights of the SVGs (for switching to dark theme)
];
// track the replacement colours actually being used
// defaults to our keys.
this.colors = [
this.keyHex[0],
this.keyHex[1],
this.keyHex[2],
this.keyHex[3],
this.keyHex[4],
];
// track the most current tint request inputs (which may differ from the
// end result stored in this.colors
this.currentTint = [
undefined,
undefined,
undefined,
undefined,
undefined,
];
this.cssFixups = [
// { theme: {
// style: a style object that should be fixed up taken from a stylesheet
// attr: name of the attribute to be clobbered, e.g. 'color'
// index: ordinal of primary, secondary or tertiary
// },
// }
];
// CSS attributes to be fixed up
this.cssAttrs = [
"color",
"backgroundColor",
"borderColor",
"borderTopColor",
"borderBottomColor",
"borderLeftColor",
];
this.svgAttrs = [
"fill",
"stroke",
];
// List of functions to call when the tint changes.
this.tintables = [];
// the currently loaded theme (if any)
this.theme = undefined;
// whether to force a tint (e.g. after changing theme)
this.forceTint = false;
}
/**
* Register a callback to fire when the tint changes.
* This is used to rewrite the tintable SVGs with the new tint.
*
* It's not possible to unregister a tintable callback. So this can only be
* used to register a static callback. If a set of tintables will change
* over time then the best bet is to register a single callback for the
* entire set.
*
* To ensure the tintable work happens at least once, it is also called as
* part of registration.
*
* @param {Function} tintable Function to call when the tint changes.
*/
registerTintable(tintable) {
this.tintables.push(tintable);
tintable();
}
getKeyRgb() {
return this.keyRgb;
}
tint(primaryColor, secondaryColor, tertiaryColor) {
return;
// eslint-disable-next-line no-unreachable
this.currentTint[0] = primaryColor;
this.currentTint[1] = secondaryColor;
this.currentTint[2] = tertiaryColor;
this.calcCssFixups();
if (DEBUG) {
console.log("Tinter.tint(" + primaryColor + ", " +
secondaryColor + ", " +
tertiaryColor + ")");
}
if (!primaryColor) {
primaryColor = this.keyRgb[0];
secondaryColor = this.keyRgb[1];
tertiaryColor = this.keyRgb[2];
}
if (!secondaryColor) {
const x = 0.16; // average weighting factor calculated from vector green & light green
const rgb = colorToRgb(primaryColor);
rgb[0] = x * rgb[0] + (1 - x) * 255;
rgb[1] = x * rgb[1] + (1 - x) * 255;
rgb[2] = x * rgb[2] + (1 - x) * 255;
secondaryColor = rgbToColor(rgb);
}
if (!tertiaryColor) {
const x = 0.19;
const rgb1 = colorToRgb(primaryColor);
const rgb2 = colorToRgb(secondaryColor);
rgb1[0] = x * rgb1[0] + (1 - x) * rgb2[0];
rgb1[1] = x * rgb1[1] + (1 - x) * rgb2[1];
rgb1[2] = x * rgb1[2] + (1 - x) * rgb2[2];
tertiaryColor = rgbToColor(rgb1);
}
if (this.forceTint == false &&
this.colors[0] === primaryColor &&
this.colors[1] === secondaryColor &&
this.colors[2] === tertiaryColor) {
return;
}
this.forceTint = false;
this.colors[0] = primaryColor;
this.colors[1] = secondaryColor;
this.colors[2] = tertiaryColor;
if (DEBUG) {
console.log("Tinter.tint final: (" + primaryColor + ", " +
secondaryColor + ", " +
tertiaryColor + ")");
}
// go through manually fixing up the stylesheets.
this.applyCssFixups();
// tell all the SVGs to go fix themselves up
// we don't do this as a dispatch otherwise it will visually lag
this.tintables.forEach(function(tintable) {
tintable();
});
}
tintSvgWhite(whiteColor) {
this.currentTint[3] = whiteColor;
if (!whiteColor) {
whiteColor = this.colors[3];
}
if (this.colors[3] === whiteColor) {
return;
}
this.colors[3] = whiteColor;
this.tintables.forEach(function(tintable) {
tintable();
});
}
tintSvgBlack(blackColor) {
this.currentTint[4] = blackColor;
if (!blackColor) {
blackColor = this.colors[4];
}
if (this.colors[4] === blackColor) {
return;
}
this.colors[4] = blackColor;
this.tintables.forEach(function(tintable) {
tintable();
});
}
setTheme(theme) {
this.theme = theme;
// update keyRgb from the current theme CSS itself, if it defines it
if (document.getElementById('mx_theme_accentColor')) {
this.keyRgb[0] = window.getComputedStyle(
document.getElementById('mx_theme_accentColor')).color;
}
if (document.getElementById('mx_theme_secondaryAccentColor')) {
this.keyRgb[1] = window.getComputedStyle(
document.getElementById('mx_theme_secondaryAccentColor')).color;
}
if (document.getElementById('mx_theme_tertiaryAccentColor')) {
this.keyRgb[2] = window.getComputedStyle(
document.getElementById('mx_theme_tertiaryAccentColor')).color;
}
this.calcCssFixups();
this.forceTint = true;
this.tint(this.currentTint[0], this.currentTint[1], this.currentTint[2]);
if (theme === 'dark') {
// abuse the tinter to change all the SVG's #fff to #2d2d2d
// XXX: obviously this shouldn't be hardcoded here.
this.tintSvgWhite('#2d2d2d');
this.tintSvgBlack('#dddddd');
} else {
this.tintSvgWhite('#ffffff');
this.tintSvgBlack('#000000');
}
}
calcCssFixups() {
// cache our fixups
if (this.cssFixups[this.theme]) return;
if (DEBUG) {
console.debug("calcCssFixups start for " + this.theme + " (checking " +
document.styleSheets.length +
" stylesheets)");
}
this.cssFixups[this.theme] = [];
for (let i = 0; i < document.styleSheets.length; i++) {
const ss = document.styleSheets[i];
try {
if (!ss) continue; // well done safari >:(
// Chromium apparently sometimes returns null here; unsure why.
// see $14534907369972FRXBx:matrix.org in HQ
// ...ah, it's because there's a third party extension like
// privacybadger inserting its own stylesheet in there with a
// resource:// URI or something which results in a XSS error.
// See also #vector:matrix.org/$145357669685386ebCfr:matrix.org
// ...except some browsers apparently return stylesheets without
// hrefs, which we have no choice but ignore right now
// XXX seriously? we are hardcoding the name of vector's CSS file in
// here?
//
// Why do we need to limit it to vector's CSS file anyway - if there
// are other CSS files affecting the doc don't we want to apply the
// same transformations to them?
//
// Iterating through the CSS looking for matches to hack on feels
// pretty horrible anyway. And what if the application skin doesn't use
// Vector Green as its primary color?
// --richvdh
// Yes, tinting assumes that you are using the Element skin for now.
// The right solution will be to move the CSS over to react-sdk.
// And yes, the default assets for the base skin might as well use
// Vector Green as any other colour.
// --matthew
// stylesheets we don't have permission to access (eg. ones from extensions) have a null
// href and will throw exceptions if we try to access their rules.
if (!ss.href || !ss.href.match(new RegExp('/theme-' + this.theme + '.css$'))) continue;
if (ss.disabled) continue;
if (!ss.cssRules) continue;
if (DEBUG) console.debug("calcCssFixups checking " + ss.cssRules.length + " rules for " + ss.href);
for (let j = 0; j < ss.cssRules.length; j++) {
const rule = ss.cssRules[j];
if (!rule.style) continue;
if (rule.selectorText && rule.selectorText.match(/#mx_theme/)) continue;
for (let k = 0; k < this.cssAttrs.length; k++) {
const attr = this.cssAttrs[k];
for (let l = 0; l < this.keyRgb.length; l++) {
if (rule.style[attr] === this.keyRgb[l]) {
this.cssFixups[this.theme].push({
style: rule.style,
attr: attr,
index: l,
});
}
}
}
}
} catch (e) {
// Catch any random exceptions that happen here: all sorts of things can go
// wrong with this (nulls, SecurityErrors) and mostly it's for other
// stylesheets that we don't want to proces anyway. We should not propagate an
// exception out since this will cause the app to fail to start.
console.log("Failed to calculate CSS fixups for a stylesheet: " + ss.href, e);
}
}
if (DEBUG) {
console.log("calcCssFixups end (" +
this.cssFixups[this.theme].length +
" fixups)");
}
}
applyCssFixups() {
if (DEBUG) {
console.log("applyCssFixups start (" +
this.cssFixups[this.theme].length +
" fixups)");
}
for (let i = 0; i < this.cssFixups[this.theme].length; i++) {
const cssFixup = this.cssFixups[this.theme][i];
try {
cssFixup.style[cssFixup.attr] = this.colors[cssFixup.index];
} catch (e) {
// Firefox Quantum explodes if you manually edit the CSS in the
// inspector and then try to do a tint, as apparently all the
// fixups are then stale.
console.error("Failed to apply cssFixup in Tinter! ", e.name);
}
}
if (DEBUG) console.log("applyCssFixups end");
}
// XXX: we could just move this all into TintableSvg, but as it's so similar
// to the CSS fixup stuff in Tinter (just that the fixups are stored in TintableSvg)
// keeping it here for now.
calcSvgFixups(svgs) {
// go through manually fixing up SVG colours.
// we could do this by stylesheets, but keeping the stylesheets
// updated would be a PITA, so just brute-force search for the
// key colour; cache the element and apply.
if (DEBUG) console.log("calcSvgFixups start for " + svgs);
const fixups = [];
for (let i = 0; i < svgs.length; i++) {
let svgDoc;
try {
svgDoc = svgs[i].contentDocument;
} catch (e) {
let msg = 'Failed to get svg.contentDocument of ' + svgs[i].toString();
if (e.message) {
msg += e.message;
}
if (e.stack) {
msg += ' | stack: ' + e.stack;
}
console.error(msg);
}
if (!svgDoc) continue;
const tags = svgDoc.getElementsByTagName("*");
for (let j = 0; j < tags.length; j++) {
const tag = tags[j];
for (let k = 0; k < this.svgAttrs.length; k++) {
const attr = this.svgAttrs[k];
for (let l = 0; l < this.keyHex.length; l++) {
if (tag.getAttribute(attr) &&
tag.getAttribute(attr).toUpperCase() === this.keyHex[l]) {
fixups.push({
node: tag,
attr: attr,
index: l,
});
}
}
}
}
}
if (DEBUG) console.log("calcSvgFixups end");
return fixups;
}
applySvgFixups(fixups) {
if (DEBUG) console.log("applySvgFixups start for " + fixups);
for (let i = 0; i < fixups.length; i++) {
const svgFixup = fixups[i];
svgFixup.node.setAttribute(svgFixup.attr, this.colors[svgFixup.index]);
}
if (DEBUG) console.log("applySvgFixups end");
}
}
if (global.singletonTinter === undefined) {
global.singletonTinter = new Tinter();
}
export default global.singletonTinter;

View file

@ -191,10 +191,10 @@ export default class UserActivity {
this.lastScreenY = event.screenY; this.lastScreenY = event.screenY;
} }
dis.dispatch({action: 'user_activity'}); dis.dispatch({ action: 'user_activity' });
if (!this.activeNowTimeout.isRunning()) { if (!this.activeNowTimeout.isRunning()) {
this.activeNowTimeout.start(); this.activeNowTimeout.start();
dis.dispatch({action: 'user_activity_start'}); dis.dispatch({ action: 'user_activity_start' });
UserActivity.runTimersUntilTimeout(this.attachedActiveNowTimers, this.activeNowTimeout); UserActivity.runTimersUntilTimeout(this.attachedActiveNowTimers, this.activeNowTimeout);
} else { } else {

View file

@ -14,10 +14,10 @@ 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 { Room } from "matrix-js-sdk/src/models/room";
import {RoomMember} from "matrix-js-sdk/src/models/room-member"; import { RoomMember } from "matrix-js-sdk/src/models/room-member";
import {MatrixClientPeg} from "./MatrixClientPeg"; import { MatrixClientPeg } from "./MatrixClientPeg";
import { _t } from './languageHandler'; import { _t } from './languageHandler';
export function usersTypingApartFromMeAndIgnored(room: Room): RoomMember[] { export function usersTypingApartFromMeAndIgnored(room: Room): RoomMember[] {
@ -61,7 +61,7 @@ export function whoIsTypingString(whoIsTyping: RoomMember[], limit: number): str
if (whoIsTyping.length === 0) { if (whoIsTyping.length === 0) {
return ''; return '';
} else if (whoIsTyping.length === 1) { } else if (whoIsTyping.length === 1) {
return _t('%(displayName)s is typing …', {displayName: whoIsTyping[0].name}); return _t('%(displayName)s is typing …', { displayName: whoIsTyping[0].name });
} }
const names = whoIsTyping.map(m => m.name); const names = whoIsTyping.map(m => m.name);
@ -73,6 +73,6 @@ export function whoIsTypingString(whoIsTyping: RoomMember[], limit: number): str
}); });
} else { } else {
const lastPerson = names.pop(); const lastPerson = names.pop();
return _t('%(names)s and %(lastPerson)s are typing …', {names: names.join(', '), lastPerson: lastPerson}); return _t('%(names)s and %(lastPerson)s are typing …', { names: names.join(', '), lastPerson: lastPerson });
} }
} }

View file

@ -20,7 +20,7 @@ import classNames from "classnames";
import * as sdk from "../index"; import * as sdk from "../index";
import Modal from "../Modal"; import Modal from "../Modal";
import { _t, _td } from "../languageHandler"; import { _t, _td } from "../languageHandler";
import {isMac, Key} from "../Keyboard"; import { isMac, Key } from "../Keyboard";
// TS: once languageHandler is TS we can probably inline this into the enum // TS: once languageHandler is TS we can probably inline this into the enum
_td("Navigation"); _td("Navigation");
@ -332,7 +332,7 @@ const keyIcon: Record<string, string> = {
const Shortcut: React.FC<{ const Shortcut: React.FC<{
shortcut: IShortcut; shortcut: IShortcut;
}> = ({shortcut}) => { }> = ({ shortcut }) => {
const classes = classNames({ const classes = classNames({
"mx_KeyboardShortcutsDialog_inline": shortcut.keybinds.every(k => !k.modifiers || k.modifiers.length === 0), "mx_KeyboardShortcutsDialog_inline": shortcut.keybinds.every(k => !k.modifiers || k.modifiers.length === 0),
}); });

View file

@ -26,8 +26,8 @@ import React, {
Dispatch, Dispatch,
} from "react"; } from "react";
import {Key} from "../Keyboard"; import { Key } from "../Keyboard";
import {FocusHandler, Ref} from "./roving/types"; import { FocusHandler, Ref } from "./roving/types";
/** /**
* Module to simplify implementing the Roving TabIndex accessibility technique * Module to simplify implementing the Roving TabIndex accessibility technique
@ -156,13 +156,13 @@ interface IProps {
onKeyDown?(ev: React.KeyboardEvent, state: IState); onKeyDown?(ev: React.KeyboardEvent, state: IState);
} }
export const RovingTabIndexProvider: React.FC<IProps> = ({children, handleHomeEnd, onKeyDown}) => { export const RovingTabIndexProvider: React.FC<IProps> = ({ children, handleHomeEnd, onKeyDown }) => {
const [state, dispatch] = useReducer<Reducer<IState, IAction>>(reducer, { const [state, dispatch] = useReducer<Reducer<IState, IAction>>(reducer, {
activeRef: null, activeRef: null,
refs: [], refs: [],
}); });
const context = useMemo<IContext>(() => ({state, dispatch}), [state]); const context = useMemo<IContext>(() => ({ state, dispatch }), [state]);
const onKeyDownHandler = useCallback((ev) => { const onKeyDownHandler = useCallback((ev) => {
let handled = false; let handled = false;
@ -196,7 +196,7 @@ export const RovingTabIndexProvider: React.FC<IProps> = ({children, handleHomeEn
}, [context.state, onKeyDown, handleHomeEnd]); }, [context.state, onKeyDown, handleHomeEnd]);
return <RovingTabIndexContext.Provider value={context}> return <RovingTabIndexContext.Provider value={context}>
{ children({onKeyDownHandler}) } { children({ onKeyDownHandler }) }
</RovingTabIndexContext.Provider>; </RovingTabIndexContext.Provider>;
}; };
@ -218,13 +218,13 @@ export const useRovingTabIndex = (inputRef?: Ref): [FocusHandler, boolean, Ref]
useLayoutEffect(() => { useLayoutEffect(() => {
context.dispatch({ context.dispatch({
type: Type.Register, type: Type.Register,
payload: {ref}, payload: { ref },
}); });
// teardown // teardown
return () => { return () => {
context.dispatch({ context.dispatch({
type: Type.Unregister, type: Type.Unregister,
payload: {ref}, payload: { ref },
}); });
}; };
}, []); // eslint-disable-line react-hooks/exhaustive-deps }, []); // eslint-disable-line react-hooks/exhaustive-deps
@ -232,7 +232,7 @@ export const useRovingTabIndex = (inputRef?: Ref): [FocusHandler, boolean, Ref]
const onFocus = useCallback(() => { const onFocus = useCallback(() => {
context.dispatch({ context.dispatch({
type: Type.SetFocus, type: Type.SetFocus,
payload: {ref}, payload: { ref },
}); });
}, [ref, context]); }, [ref, context]);
@ -241,6 +241,6 @@ export const useRovingTabIndex = (inputRef?: Ref): [FocusHandler, boolean, Ref]
}; };
// re-export the semantic helper components for simplicity // re-export the semantic helper components for simplicity
export {RovingTabIndexWrapper} from "./roving/RovingTabIndexWrapper"; export { RovingTabIndexWrapper } from "./roving/RovingTabIndexWrapper";
export {RovingAccessibleButton} from "./roving/RovingAccessibleButton"; export { RovingAccessibleButton } from "./roving/RovingAccessibleButton";
export {RovingAccessibleTooltipButton} from "./roving/RovingAccessibleTooltipButton"; export { RovingAccessibleTooltipButton } from "./roving/RovingAccessibleTooltipButton";

View file

@ -16,8 +16,8 @@ limitations under the License.
import React from "react"; import React from "react";
import {IState, RovingTabIndexProvider} from "./RovingTabIndex"; import { IState, RovingTabIndexProvider } from "./RovingTabIndex";
import {Key} from "../Keyboard"; import { Key } from "../Keyboard";
interface IProps extends Omit<React.HTMLProps<HTMLDivElement>, "onKeyDown"> { interface IProps extends Omit<React.HTMLProps<HTMLDivElement>, "onKeyDown"> {
} }
@ -25,7 +25,7 @@ interface IProps extends Omit<React.HTMLProps<HTMLDivElement>, "onKeyDown"> {
// This component implements the Toolbar design pattern from the WAI-ARIA Authoring Practices guidelines. // This component implements the Toolbar design pattern from the WAI-ARIA Authoring Practices guidelines.
// 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, state: IState) => { const onKeyDown = (ev: React.KeyboardEvent, state: IState) => {
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
@ -62,7 +62,7 @@ const Toolbar: React.FC<IProps> = ({children, ...props}) => {
}; };
return <RovingTabIndexProvider handleHomeEnd={true} onKeyDown={onKeyDown}> return <RovingTabIndexProvider handleHomeEnd={true} onKeyDown={onKeyDown}>
{({onKeyDownHandler}) => <div {...props} onKeyDown={onKeyDownHandler} role="toolbar"> {({ onKeyDownHandler }) => <div {...props} onKeyDown={onKeyDownHandler} role="toolbar">
{ children } { children }
</div>} </div>}
</RovingTabIndexProvider>; </RovingTabIndexProvider>;

View file

@ -23,7 +23,7 @@ interface IProps extends React.HTMLAttributes<HTMLDivElement> {
} }
// Semantic component for representing a role=group for grouping menu radios/checkboxes // Semantic component for representing a role=group for grouping menu radios/checkboxes
export const MenuGroup: React.FC<IProps> = ({children, label, ...props}) => { export const MenuGroup: React.FC<IProps> = ({ children, label, ...props }) => {
return <div {...props} role="group" aria-label={label}> return <div {...props} role="group" aria-label={label}>
{ children } { children }
</div>; </div>;

View file

@ -27,7 +27,7 @@ interface IProps extends React.ComponentProps<typeof AccessibleButton> {
} }
// Semantic component for representing a role=menuitem // Semantic component for representing a role=menuitem
export const MenuItem: React.FC<IProps> = ({children, label, tooltip, ...props}) => { export const MenuItem: React.FC<IProps> = ({ children, label, tooltip, ...props }) => {
const ariaLabel = props["aria-label"] || label; const ariaLabel = props["aria-label"] || label;
if (tooltip) { if (tooltip) {

View file

@ -26,7 +26,7 @@ interface IProps extends React.ComponentProps<typeof AccessibleButton> {
} }
// Semantic component for representing a role=menuitemcheckbox // Semantic component for representing a role=menuitemcheckbox
export const MenuItemCheckbox: React.FC<IProps> = ({children, label, active, disabled, ...props}) => { export const MenuItemCheckbox: React.FC<IProps> = ({ children, label, active, disabled, ...props }) => {
return ( return (
<AccessibleButton <AccessibleButton
{...props} {...props}

View file

@ -26,7 +26,7 @@ interface IProps extends React.ComponentProps<typeof AccessibleButton> {
} }
// Semantic component for representing a role=menuitemradio // Semantic component for representing a role=menuitemradio
export const MenuItemRadio: React.FC<IProps> = ({children, label, active, disabled, ...props}) => { export const MenuItemRadio: React.FC<IProps> = ({ children, label, active, disabled, ...props }) => {
return ( return (
<AccessibleButton <AccessibleButton
{...props} {...props}

View file

@ -18,7 +18,7 @@ limitations under the License.
import React from "react"; import React from "react";
import {Key} from "../../Keyboard"; import { Key } from "../../Keyboard";
import StyledCheckbox from "../../components/views/elements/StyledCheckbox"; import StyledCheckbox from "../../components/views/elements/StyledCheckbox";
interface IProps extends React.ComponentProps<typeof StyledCheckbox> { interface IProps extends React.ComponentProps<typeof StyledCheckbox> {
@ -28,7 +28,7 @@ interface IProps extends React.ComponentProps<typeof StyledCheckbox> {
} }
// Semantic component for representing a styled role=menuitemcheckbox // Semantic component for representing a styled role=menuitemcheckbox
export const StyledMenuItemCheckbox: React.FC<IProps> = ({children, label, onChange, onClose, ...props}) => { export const StyledMenuItemCheckbox: React.FC<IProps> = ({ children, label, onChange, onClose, ...props }) => {
const onKeyDown = (e: React.KeyboardEvent) => { const onKeyDown = (e: React.KeyboardEvent) => {
if (e.key === Key.ENTER || e.key === Key.SPACE) { if (e.key === Key.ENTER || e.key === Key.SPACE) {
e.stopPropagation(); e.stopPropagation();

View file

@ -18,7 +18,7 @@ limitations under the License.
import React from "react"; import React from "react";
import {Key} from "../../Keyboard"; import { Key } from "../../Keyboard";
import StyledRadioButton from "../../components/views/elements/StyledRadioButton"; import StyledRadioButton from "../../components/views/elements/StyledRadioButton";
interface IProps extends React.ComponentProps<typeof StyledRadioButton> { interface IProps extends React.ComponentProps<typeof StyledRadioButton> {
@ -28,7 +28,7 @@ interface IProps extends React.ComponentProps<typeof StyledRadioButton> {
} }
// Semantic component for representing a styled role=menuitemradio // Semantic component for representing a styled role=menuitemradio
export const StyledMenuItemRadio: React.FC<IProps> = ({children, label, onChange, onClose, ...props}) => { export const StyledMenuItemRadio: React.FC<IProps> = ({ children, label, onChange, onClose, ...props }) => {
const onKeyDown = (e: React.KeyboardEvent) => { const onKeyDown = (e: React.KeyboardEvent) => {
if (e.key === Key.ENTER || e.key === Key.SPACE) { if (e.key === Key.ENTER || e.key === Key.SPACE) {
e.stopPropagation(); e.stopPropagation();

View file

@ -17,15 +17,15 @@ limitations under the License.
import React from "react"; import React from "react";
import AccessibleButton from "../../components/views/elements/AccessibleButton"; import AccessibleButton from "../../components/views/elements/AccessibleButton";
import {useRovingTabIndex} from "../RovingTabIndex"; import { useRovingTabIndex } from "../RovingTabIndex";
import {Ref} from "./types"; import { Ref } from "./types";
interface IProps extends Omit<React.ComponentProps<typeof AccessibleButton>, "onFocus" | "inputRef" | "tabIndex"> { interface IProps extends Omit<React.ComponentProps<typeof AccessibleButton>, "onFocus" | "inputRef" | "tabIndex"> {
inputRef?: Ref; inputRef?: Ref;
} }
// Wrapper to allow use of useRovingTabIndex for simple AccessibleButtons outside of React Functional Components. // Wrapper to allow use of useRovingTabIndex for simple AccessibleButtons outside of React Functional Components.
export const RovingAccessibleButton: React.FC<IProps> = ({inputRef, ...props}) => { export const RovingAccessibleButton: React.FC<IProps> = ({ inputRef, ...props }) => {
const [onFocus, isActive, ref] = useRovingTabIndex(inputRef); const [onFocus, isActive, ref] = useRovingTabIndex(inputRef);
return <AccessibleButton {...props} onFocus={onFocus} inputRef={ref} tabIndex={isActive ? 0 : -1} />; return <AccessibleButton {...props} onFocus={onFocus} inputRef={ref} tabIndex={isActive ? 0 : -1} />;
}; };

View file

@ -17,8 +17,8 @@ limitations under the License.
import React from "react"; import React from "react";
import AccessibleTooltipButton from "../../components/views/elements/AccessibleTooltipButton"; import AccessibleTooltipButton from "../../components/views/elements/AccessibleTooltipButton";
import {useRovingTabIndex} from "../RovingTabIndex"; import { useRovingTabIndex } from "../RovingTabIndex";
import {Ref} from "./types"; import { Ref } from "./types";
type ATBProps = React.ComponentProps<typeof AccessibleTooltipButton>; type ATBProps = React.ComponentProps<typeof AccessibleTooltipButton>;
interface IProps extends Omit<ATBProps, "onFocus" | "inputRef" | "tabIndex"> { interface IProps extends Omit<ATBProps, "onFocus" | "inputRef" | "tabIndex"> {
@ -26,7 +26,7 @@ interface IProps extends Omit<ATBProps, "onFocus" | "inputRef" | "tabIndex"> {
} }
// Wrapper to allow use of useRovingTabIndex for simple AccessibleTooltipButtons outside of React Functional Components. // Wrapper to allow use of useRovingTabIndex for simple AccessibleTooltipButtons outside of React Functional Components.
export const RovingAccessibleTooltipButton: React.FC<IProps> = ({inputRef, ...props}) => { export const RovingAccessibleTooltipButton: React.FC<IProps> = ({ inputRef, ...props }) => {
const [onFocus, isActive, ref] = useRovingTabIndex(inputRef); const [onFocus, isActive, ref] = useRovingTabIndex(inputRef);
return <AccessibleTooltipButton {...props} onFocus={onFocus} inputRef={ref} tabIndex={isActive ? 0 : -1} />; return <AccessibleTooltipButton {...props} onFocus={onFocus} inputRef={ref} tabIndex={isActive ? 0 : -1} />;
}; };

View file

@ -16,8 +16,8 @@ limitations under the License.
import React from "react"; import React from "react";
import {useRovingTabIndex} from "../RovingTabIndex"; import { useRovingTabIndex } from "../RovingTabIndex";
import {FocusHandler, Ref} from "./types"; import { FocusHandler, Ref } from "./types";
interface IProps { interface IProps {
inputRef?: Ref; inputRef?: Ref;
@ -29,7 +29,7 @@ interface IProps {
} }
// Wrapper to allow use of useRovingTabIndex outside of React Functional Components. // Wrapper to allow use of useRovingTabIndex outside of React Functional Components.
export const RovingTabIndexWrapper: React.FC<IProps> = ({children, inputRef}) => { export const RovingTabIndexWrapper: React.FC<IProps> = ({ children, inputRef }) => {
const [onFocus, isActive, ref] = useRovingTabIndex(inputRef); const [onFocus, isActive, ref] = useRovingTabIndex(inputRef);
return children({onFocus, isActive, ref}); return children({ onFocus, isActive, ref });
}; };

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {RefObject} from "react"; import { RefObject } from "react";
export type Ref = RefObject<HTMLElement>; export type Ref = RefObject<HTMLElement>;

View file

@ -20,7 +20,7 @@ import { Room } from "matrix-js-sdk/src/models/room";
import { EventTimeline } from "matrix-js-sdk/src/models/event-timeline"; import { EventTimeline } from "matrix-js-sdk/src/models/event-timeline";
import dis from "../dispatcher/dispatcher"; import dis from "../dispatcher/dispatcher";
import {ActionPayload} from "../dispatcher/payloads"; import { ActionPayload } from "../dispatcher/payloads";
// TODO: migrate from sync_state to MatrixActions.sync so that more js-sdk events // TODO: migrate from sync_state to MatrixActions.sync so that more js-sdk events
// become dispatches in the same place. // become dispatches in the same place.

View file

@ -112,7 +112,7 @@ export default class RoomListActions {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
console.error("Failed to remove tag " + oldTag + " from room: " + err); console.error("Failed to remove tag " + oldTag + " from room: " + err);
Modal.createTrackedDialog('Failed to remove tag from room', '', ErrorDialog, { Modal.createTrackedDialog('Failed to remove tag from room', '', ErrorDialog, {
title: _t('Failed to remove tag %(tagName)s from room', {tagName: oldTag}), title: _t('Failed to remove tag %(tagName)s from room', { tagName: oldTag }),
description: ((err && err.message) ? err.message : _t('Operation failed')), description: ((err && err.message) ? err.message : _t('Operation failed')),
}); });
}); });
@ -132,7 +132,7 @@ export default class RoomListActions {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
console.error("Failed to add tag " + newTag + " to room: " + err); console.error("Failed to add tag " + newTag + " to room: " + err);
Modal.createTrackedDialog('Failed to add tag to room', '', ErrorDialog, { Modal.createTrackedDialog('Failed to add tag to room', '', ErrorDialog, {
title: _t('Failed to add tag %(tagName)s to room', {tagName: newTag}), title: _t('Failed to add tag %(tagName)s to room', { tagName: newTag }),
description: ((err && err.message) ? err.message : _t('Operation failed')), description: ((err && err.message) ? err.message : _t('Operation failed')),
}); });

View file

@ -53,11 +53,11 @@ export default class TagOrderActions {
Analytics.trackEvent('TagOrderActions', 'commitTagOrdering'); Analytics.trackEvent('TagOrderActions', 'commitTagOrdering');
return matrixClient.setAccountData( return matrixClient.setAccountData(
'im.vector.web.tag_ordering', 'im.vector.web.tag_ordering',
{tags, removedTags, _storeId: storeId}, { tags, removedTags, _storeId: storeId },
); );
}, () => { }, () => {
// For an optimistic update // For an optimistic update
return {tags, removedTags}; return { tags, removedTags };
}); });
} }
@ -100,11 +100,11 @@ export default class TagOrderActions {
Analytics.trackEvent('TagOrderActions', 'removeTag'); Analytics.trackEvent('TagOrderActions', 'removeTag');
return matrixClient.setAccountData( return matrixClient.setAccountData(
'im.vector.web.tag_ordering', 'im.vector.web.tag_ordering',
{tags, removedTags, _storeId: storeId}, { tags, removedTags, _storeId: storeId },
); );
}, () => { }, () => {
// For an optimistic update // For an optimistic update
return {removedTags}; return { removedTags };
}); });
} }
} }

View file

@ -51,9 +51,9 @@ export function asyncAction(id: string, fn: () => Promise<any>, pendingFn: () =>
request: typeof pendingFn === 'function' ? pendingFn() : undefined, request: typeof pendingFn === 'function' ? pendingFn() : undefined,
}); });
fn().then((result) => { fn().then((result) => {
dispatch({action: id + '.success', result}); dispatch({ action: id + '.success', result });
}).catch((err) => { }).catch((err) => {
dispatch({action: id + '.failure', err}); dispatch({ action: id + '.failure', err });
}); });
}; };
return new AsyncActionPayload(helper); return new AsyncActionPayload(helper);

View file

@ -22,8 +22,8 @@ import { _t } from '../../../../languageHandler';
import SettingsStore from "../../../../settings/SettingsStore"; import SettingsStore from "../../../../settings/SettingsStore";
import EventIndexPeg from "../../../../indexing/EventIndexPeg"; import EventIndexPeg from "../../../../indexing/EventIndexPeg";
import {Action} from "../../../../dispatcher/actions"; import { Action } from "../../../../dispatcher/actions";
import {SettingLevel} from "../../../../settings/SettingLevel"; import { SettingLevel } from "../../../../settings/SettingLevel";
/* /*
* Allows the user to disable the Event Index. * Allows the user to disable the Event Index.

View file

@ -21,9 +21,9 @@ import SdkConfig from '../../../../SdkConfig';
import SettingsStore from "../../../../settings/SettingsStore"; import SettingsStore from "../../../../settings/SettingsStore";
import Modal from '../../../../Modal'; import Modal from '../../../../Modal';
import {formatBytes, formatCountLong} from "../../../../utils/FormattingUtils"; import { formatBytes, formatCountLong } from "../../../../utils/FormattingUtils";
import EventIndexPeg from "../../../../indexing/EventIndexPeg"; import EventIndexPeg from "../../../../indexing/EventIndexPeg";
import {SettingLevel} from "../../../../settings/SettingLevel"; import { SettingLevel } from "../../../../settings/SettingLevel";
interface IProps { interface IProps {
onFinished: (confirmed: boolean) => void; onFinished: (confirmed: boolean) => void;
@ -139,7 +139,7 @@ export default class ManageEventIndexDialog extends React.Component<IProps, ISta
}; };
private onCrawlerSleepTimeChange = (e) => { private onCrawlerSleepTimeChange = (e) => {
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);
}; };

View file

@ -15,15 +15,15 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React, {createRef} from 'react'; import React, { createRef } from 'react';
import FileSaver from 'file-saver'; import FileSaver from 'file-saver';
import * as sdk from '../../../../index'; import * as sdk from '../../../../index';
import {MatrixClientPeg} from '../../../../MatrixClientPeg'; import { MatrixClientPeg } from '../../../../MatrixClientPeg';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import {_t, _td} from '../../../../languageHandler'; import { _t, _td } from '../../../../languageHandler';
import { accessSecretStorage } from '../../../../SecurityManager'; import { accessSecretStorage } from '../../../../SecurityManager';
import AccessibleButton from "../../../../components/views/elements/AccessibleButton"; import AccessibleButton from "../../../../components/views/elements/AccessibleButton";
import {copyNode} from "../../../../utils/strings"; import { copyNode } from "../../../../utils/strings";
import PassphraseField from "../../../../components/views/auth/PassphraseField"; import PassphraseField from "../../../../components/views/auth/PassphraseField";
const PHASE_PASSPHRASE = 0; const PHASE_PASSPHRASE = 0;
@ -152,11 +152,11 @@ export default class CreateKeyBackupDialog extends React.PureComponent {
} }
_onOptOutClick = () => { _onOptOutClick = () => {
this.setState({phase: PHASE_OPTOUT_CONFIRM}); this.setState({ phase: PHASE_OPTOUT_CONFIRM });
} }
_onSetUpClick = () => { _onSetUpClick = () => {
this.setState({phase: PHASE_PASSPHRASE}); this.setState({ phase: PHASE_PASSPHRASE });
} }
_onSkipPassPhraseClick = async () => { _onSkipPassPhraseClick = async () => {
@ -179,7 +179,7 @@ export default class CreateKeyBackupDialog extends React.PureComponent {
return; return;
} }
this.setState({phase: PHASE_PASSPHRASE_CONFIRM}); this.setState({ phase: PHASE_PASSPHRASE_CONFIRM });
}; };
_onPassPhraseConfirmNextClick = async (e) => { _onPassPhraseConfirmNextClick = async (e) => {
@ -370,21 +370,21 @@ export default class CreateKeyBackupDialog extends React.PureComponent {
if (this.state.copied) { if (this.state.copied) {
introText = _t( introText = _t(
"Your Security Key has been <b>copied to your clipboard</b>, paste it to:", "Your Security Key has been <b>copied to your clipboard</b>, paste it to:",
{}, {b: s => <b>{s}</b>}, {}, { b: s => <b>{s}</b> },
); );
} else if (this.state.downloaded) { } else if (this.state.downloaded) {
introText = _t( introText = _t(
"Your Security Key is in your <b>Downloads</b> folder.", "Your Security Key is in your <b>Downloads</b> folder.",
{}, {b: s => <b>{s}</b>}, {}, { b: s => <b>{s}</b> },
); );
} }
const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
return <div> return <div>
{introText} {introText}
<ul> <ul>
<li>{_t("<b>Print it</b> and store it somewhere safe", {}, {b: s => <b>{s}</b>})}</li> <li>{_t("<b>Print it</b> and store it somewhere safe", {}, { b: s => <b>{s}</b> })}</li>
<li>{_t("<b>Save it</b> on a USB key or backup drive", {}, {b: s => <b>{s}</b>})}</li> <li>{_t("<b>Save it</b> on a USB key or backup drive", {}, { b: s => <b>{s}</b> })}</li>
<li>{_t("<b>Copy it</b> to your personal cloud storage", {}, {b: s => <b>{s}</b>})}</li> <li>{_t("<b>Copy it</b> to your personal cloud storage", {}, { b: s => <b>{s}</b> })}</li>
</ul> </ul>
<DialogButtons primaryButton={_t("Continue")} <DialogButtons primaryButton={_t("Continue")}
onPrimaryButtonClick={this._createBackup} onPrimaryButtonClick={this._createBackup}

View file

@ -15,16 +15,16 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React, {createRef} from 'react'; import React, { createRef } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import * as sdk from '../../../../index'; import * as sdk from '../../../../index';
import {MatrixClientPeg} from '../../../../MatrixClientPeg'; import { MatrixClientPeg } from '../../../../MatrixClientPeg';
import FileSaver from 'file-saver'; import FileSaver from 'file-saver';
import {_t, _td} from '../../../../languageHandler'; import { _t, _td } from '../../../../languageHandler';
import Modal from '../../../../Modal'; import Modal from '../../../../Modal';
import { promptForBackupPassphrase } from '../../../../SecurityManager'; import { promptForBackupPassphrase } from '../../../../SecurityManager';
import {copyNode} from "../../../../utils/strings"; import { copyNode } from "../../../../utils/strings";
import {SSOAuthEntry} from "../../../../components/views/auth/InteractiveAuthEntryComponents"; import { SSOAuthEntry } from "../../../../components/views/auth/InteractiveAuthEntryComponents";
import PassphraseField from "../../../../components/views/auth/PassphraseField"; import PassphraseField from "../../../../components/views/auth/PassphraseField";
import StyledRadioButton from '../../../../components/views/elements/StyledRadioButton'; import StyledRadioButton from '../../../../components/views/elements/StyledRadioButton';
import AccessibleButton from "../../../../components/views/elements/AccessibleButton"; import AccessibleButton from "../../../../components/views/elements/AccessibleButton";
@ -155,7 +155,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
backupSigStatus, backupSigStatus,
}; };
} catch (e) { } catch (e) {
this.setState({phase: PHASE_LOADERROR}); this.setState({ phase: PHASE_LOADERROR });
} }
} }
@ -385,7 +385,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
} }
_onLoadRetryClick = () => { _onLoadRetryClick = () => {
this.setState({phase: PHASE_LOADING}); this.setState({ phase: PHASE_LOADING });
this._fetchBackupInfo(); this._fetchBackupInfo();
} }
@ -394,11 +394,11 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
} }
_onCancelClick = () => { _onCancelClick = () => {
this.setState({phase: PHASE_CONFIRM_SKIP}); this.setState({ phase: PHASE_CONFIRM_SKIP });
} }
_onGoBackClick = () => { _onGoBackClick = () => {
this.setState({phase: PHASE_CHOOSE_KEY_PASSPHRASE}); this.setState({ phase: PHASE_CHOOSE_KEY_PASSPHRASE });
} }
_onPassPhraseNextClick = async (e) => { _onPassPhraseNextClick = async (e) => {
@ -412,7 +412,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
return; return;
} }
this.setState({phase: PHASE_PASSPHRASE_CONFIRM}); this.setState({ phase: PHASE_PASSPHRASE_CONFIRM });
}; };
_onPassPhraseConfirmNextClick = async (e) => { _onPassPhraseConfirmNextClick = async (e) => {

View file

@ -15,7 +15,7 @@ limitations under the License.
*/ */
import FileSaver from 'file-saver'; import FileSaver from 'file-saver';
import React, {createRef} from 'react'; import React, { createRef } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { _t } from '../../../../languageHandler'; import { _t } from '../../../../languageHandler';
@ -55,11 +55,11 @@ export default class ExportE2eKeysDialog extends React.Component {
const passphrase = this._passphrase1.current.value; const passphrase = this._passphrase1.current.value;
if (passphrase !== this._passphrase2.current.value) { if (passphrase !== this._passphrase2.current.value) {
this.setState({errStr: _t('Passphrases must match')}); this.setState({ errStr: _t('Passphrases must match') });
return false; return false;
} }
if (!passphrase) { if (!passphrase) {
this.setState({errStr: _t('Passphrase must not be empty')}); this.setState({ errStr: _t('Passphrase must not be empty') });
return false; return false;
} }

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React, {createRef} from 'react'; import React, { createRef } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { MatrixClient } from 'matrix-js-sdk/src/client'; import { MatrixClient } from 'matrix-js-sdk/src/client';

View file

@ -18,12 +18,12 @@ limitations under the License.
import React from "react"; import React from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import * as sdk from "../../../../index"; import * as sdk from "../../../../index";
import {MatrixClientPeg} from '../../../../MatrixClientPeg'; import { MatrixClientPeg } from '../../../../MatrixClientPeg';
import dis from "../../../../dispatcher/dispatcher"; import dis from "../../../../dispatcher/dispatcher";
import { _t } from "../../../../languageHandler"; import { _t } from "../../../../languageHandler";
import Modal from "../../../../Modal"; import Modal from "../../../../Modal";
import RestoreKeyBackupDialog from "../../../../components/views/dialogs/security/RestoreKeyBackupDialog"; import RestoreKeyBackupDialog from "../../../../components/views/dialogs/security/RestoreKeyBackupDialog";
import {Action} from "../../../../dispatcher/actions"; import { Action } from "../../../../dispatcher/actions";
export default class NewRecoveryMethodDialog extends React.PureComponent { export default class NewRecoveryMethodDialog extends React.PureComponent {
static propTypes = { static propTypes = {

View file

@ -21,7 +21,7 @@ import * as sdk from "../../../../index";
import dis from "../../../../dispatcher/dispatcher"; import dis from "../../../../dispatcher/dispatcher";
import { _t } from "../../../../languageHandler"; import { _t } from "../../../../languageHandler";
import Modal from "../../../../Modal"; import Modal from "../../../../Modal";
import {Action} from "../../../../dispatcher/actions"; import { Action } from "../../../../dispatcher/actions";
export default class RecoveryMethodRemovedDialog extends React.PureComponent { export default class RecoveryMethodRemovedDialog extends React.PureComponent {
static propTypes = { static propTypes = {

View file

@ -17,7 +17,7 @@ limitations under the License.
*/ */
import React from 'react'; import React from 'react';
import type {ICompletion, ISelectionRange} from './Autocompleter'; import type { ICompletion, ISelectionRange } from './Autocompleter';
export interface ICommand { export interface ICommand {
command: string | null; command: string | null;

View file

@ -26,7 +26,7 @@ import UserProvider from './UserProvider';
import EmojiProvider from './EmojiProvider'; import EmojiProvider from './EmojiProvider';
import NotifProvider from './NotifProvider'; import NotifProvider from './NotifProvider';
import { timeout } from "../utils/promise"; import { timeout } from "../utils/promise";
import AutocompleteProvider, {ICommand} from "./AutocompleteProvider"; import AutocompleteProvider, { ICommand } from "./AutocompleteProvider";
import SettingsStore from "../settings/SettingsStore"; import SettingsStore from "../settings/SettingsStore";
import SpaceProvider from "./SpaceProvider"; import SpaceProvider from "./SpaceProvider";

View file

@ -18,12 +18,12 @@ limitations under the License.
*/ */
import React from 'react'; import React from 'react';
import {_t} from '../languageHandler'; import { _t } from '../languageHandler';
import AutocompleteProvider from './AutocompleteProvider'; import AutocompleteProvider from './AutocompleteProvider';
import QueryMatcher from './QueryMatcher'; import QueryMatcher from './QueryMatcher';
import {TextualCompletion} from './Components'; import { TextualCompletion } from './Components';
import {ICompletion, ISelectionRange} from "./Autocompleter"; import { ICompletion, ISelectionRange } from "./Autocompleter";
import {Command, Commands, CommandMap} from '../SlashCommands'; import { Command, Commands, CommandMap } from '../SlashCommands';
const COMMAND_RE = /(^\/\w*)(?: .*)?/g; const COMMAND_RE = /(^\/\w*)(?: .*)?/g;
@ -34,7 +34,7 @@ export default class CommandProvider extends AutocompleteProvider {
super(COMMAND_RE); super(COMMAND_RE);
this.matcher = new QueryMatcher(Commands, { this.matcher = new QueryMatcher(Commands, {
keys: ['command', 'args', 'description'], keys: ['command', 'args', 'description'],
funcs: [({aliases}) => aliases.join(" ")], // aliases funcs: [({ aliases }) => aliases.join(" ")], // aliases
}); });
} }
@ -44,7 +44,7 @@ export default class CommandProvider extends AutocompleteProvider {
force?: boolean, force?: boolean,
limit = -1, limit = -1,
): Promise<ICompletion[]> { ): Promise<ICompletion[]> {
const {command, range} = this.getCurrentCommand(query, selection); const { command, range } = this.getCurrentCommand(query, selection);
if (!command) return []; if (!command) return [];
let matches = []; let matches = [];
@ -68,7 +68,6 @@ export default class CommandProvider extends AutocompleteProvider {
} }
} }
return matches.filter(cmd => cmd.isEnabled()).map((result) => { return matches.filter(cmd => cmd.isEnabled()).map((result) => {
let completion = result.getCommand() + ' '; let completion = result.getCommand() + ' ';
const usedAlias = result.aliases.find(alias => `/${alias}` === command[1]); const usedAlias = result.aliases.find(alias => `/${alias}` === command[1]);

View file

@ -19,15 +19,15 @@ import React from 'react';
import Group from "matrix-js-sdk/src/models/group"; import Group from "matrix-js-sdk/src/models/group";
import { _t } from '../languageHandler'; import { _t } from '../languageHandler';
import AutocompleteProvider from './AutocompleteProvider'; import AutocompleteProvider from './AutocompleteProvider';
import {MatrixClientPeg} from '../MatrixClientPeg'; import { MatrixClientPeg } from '../MatrixClientPeg';
import QueryMatcher from './QueryMatcher'; import QueryMatcher from './QueryMatcher';
import {PillCompletion} from './Components'; import { PillCompletion } from './Components';
import * as sdk from '../index'; import * as sdk from '../index';
import {sortBy} from "lodash"; import { sortBy } from "lodash";
import {makeGroupPermalink} from "../utils/permalinks/Permalinks"; import { makeGroupPermalink } from "../utils/permalinks/Permalinks";
import {ICompletion, ISelectionRange} from "./Autocompleter"; import { ICompletion, ISelectionRange } from "./Autocompleter";
import FlairStore from "../stores/FlairStore"; import FlairStore from "../stores/FlairStore";
import {mediaFromMxc} from "../customisations/Media"; import { mediaFromMxc } from "../customisations/Media";
const COMMUNITY_REGEX = /\B\+\S*/g; const COMMUNITY_REGEX = /\B\+\S*/g;
@ -66,11 +66,11 @@ export default class CommunityProvider extends AutocompleteProvider {
const cli = MatrixClientPeg.get(); const cli = MatrixClientPeg.get();
let completions = []; let completions = [];
const {command, range} = this.getCurrentCommand(query, selection, force); const { command, range } = this.getCurrentCommand(query, selection, force);
if (command) { if (command) {
const joinedGroups = cli.getGroups().filter(({myMembership}) => myMembership === 'join'); const joinedGroups = cli.getGroups().filter(({ myMembership }) => myMembership === 'join');
const groups = (await Promise.all(joinedGroups.map(async ({groupId}) => { const groups = (await Promise.all(joinedGroups.map(async ({ groupId }) => {
try { try {
return FlairStore.getGroupProfileCached(cli, groupId); return FlairStore.getGroupProfileCached(cli, groupId);
} catch (e) { // if FlairStore failed, fall back to just groupId } catch (e) { // if FlairStore failed, fall back to just groupId
@ -90,7 +90,7 @@ export default class CommunityProvider extends AutocompleteProvider {
completions = sortBy(completions, [ completions = sortBy(completions, [
(c) => score(matchedString, c.groupId), (c) => score(matchedString, c.groupId),
(c) => c.groupId.length, (c) => c.groupId.length,
]).map(({avatarUrl, groupId, name}) => ({ ]).map(({ avatarUrl, groupId, name }) => ({
completion: groupId, completion: groupId,
suffix: ' ', suffix: ' ',
type: "community", type: "community",

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React, {forwardRef} from 'react'; import React, { forwardRef } from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
/* These were earlier stateless functional components but had to be converted /* These were earlier stateless functional components but had to be converted
@ -31,7 +31,7 @@ interface ITextualCompletionProps {
} }
export const TextualCompletion = forwardRef<ITextualCompletionProps, any>((props, ref) => { export const TextualCompletion = forwardRef<ITextualCompletionProps, any>((props, ref) => {
const {title, subtitle, description, className, ...restProps} = props; const { title, subtitle, description, className, ...restProps } = props;
return ( return (
<div {...restProps} <div {...restProps}
className={classNames('mx_Autocomplete_Completion_block', className)} className={classNames('mx_Autocomplete_Completion_block', className)}
@ -50,7 +50,7 @@ interface IPillCompletionProps extends ITextualCompletionProps {
} }
export const PillCompletion = forwardRef<IPillCompletionProps, any>((props, ref) => { export const PillCompletion = forwardRef<IPillCompletionProps, any>((props, ref) => {
const {title, subtitle, description, className, children, ...restProps} = props; const { title, subtitle, description, className, children, ...restProps } = props;
return ( return (
<div {...restProps} <div {...restProps}
className={classNames('mx_Autocomplete_Completion_pill', className)} className={classNames('mx_Autocomplete_Completion_pill', className)}

View file

@ -20,8 +20,8 @@ import React from 'react';
import { _t } from '../languageHandler'; import { _t } from '../languageHandler';
import AutocompleteProvider from './AutocompleteProvider'; import AutocompleteProvider from './AutocompleteProvider';
import {TextualCompletion} from './Components'; import { TextualCompletion } from './Components';
import {ICompletion, ISelectionRange} from "./Autocompleter"; import { ICompletion, ISelectionRange } from "./Autocompleter";
const DDG_REGEX = /\/ddg\s+(.+)$/g; const DDG_REGEX = /\/ddg\s+(.+)$/g;
const REFERRER = 'vector'; const REFERRER = 'vector';
@ -42,7 +42,7 @@ export default class DuckDuckGoProvider extends AutocompleteProvider {
force = false, force = false,
limit = -1, limit = -1,
): Promise<ICompletion[]> { ): Promise<ICompletion[]> {
const {command, range} = this.getCurrentCommand(query, selection); const { command, range } = this.getCurrentCommand(query, selection);
if (!query || !command) { if (!query || !command) {
return []; return [];
} }

View file

@ -21,9 +21,9 @@ import React from 'react';
import { _t } from '../languageHandler'; import { _t } from '../languageHandler';
import AutocompleteProvider from './AutocompleteProvider'; import AutocompleteProvider from './AutocompleteProvider';
import QueryMatcher from './QueryMatcher'; import QueryMatcher from './QueryMatcher';
import {PillCompletion} from './Components'; import { PillCompletion } from './Components';
import {ICompletion, ISelectionRange} from './Autocompleter'; import { ICompletion, ISelectionRange } from './Autocompleter';
import {uniq, sortBy} from 'lodash'; import { uniq, sortBy } from 'lodash';
import SettingsStore from "../settings/SettingsStore"; import SettingsStore from "../settings/SettingsStore";
import { shortcodeToUnicode } from '../HtmlUtils'; import { shortcodeToUnicode } from '../HtmlUtils';
import { EMOJI, IEmoji } from '../emoji'; import { EMOJI, IEmoji } from '../emoji';
@ -95,7 +95,7 @@ export default class EmojiProvider extends AutocompleteProvider {
} }
let completions = []; let completions = [];
const {command, range} = this.getCurrentCommand(query, selection); const { command, range } = this.getCurrentCommand(query, selection);
if (command) { if (command) {
const matchedString = command[0]; const matchedString = command[0];
completions = this.matcher.match(matchedString, limit); completions = this.matcher.match(matchedString, limit);
@ -121,7 +121,7 @@ export default class EmojiProvider extends AutocompleteProvider {
sorters.push((c) => c._orderBy); sorters.push((c) => c._orderBy);
completions = sortBy(uniq(completions), sorters); completions = sortBy(uniq(completions), sorters);
completions = completions.map(({shortname}) => { completions = completions.map(({ shortname }) => {
const unicode = shortcodeToUnicode(shortname); const unicode = shortcodeToUnicode(shortname);
return { return {
completion: unicode, completion: unicode,

View file

@ -19,10 +19,10 @@ import { Room } from "matrix-js-sdk/src/models/room";
import AutocompleteProvider from './AutocompleteProvider'; import AutocompleteProvider from './AutocompleteProvider';
import { _t } from '../languageHandler'; import { _t } from '../languageHandler';
import {MatrixClientPeg} from '../MatrixClientPeg'; import { MatrixClientPeg } from '../MatrixClientPeg';
import {PillCompletion} from './Components'; import { PillCompletion } from './Components';
import * as sdk from '../index'; import * as sdk from '../index';
import {ICompletion, ISelectionRange} from "./Autocompleter"; import { ICompletion, ISelectionRange } from "./Autocompleter";
const AT_ROOM_REGEX = /@\S*/g; const AT_ROOM_REGEX = /@\S*/g;
@ -46,7 +46,7 @@ export default class NotifProvider extends AutocompleteProvider {
if (!this.room.currentState.mayTriggerNotifOfType('room', client.credentials.userId)) return []; if (!this.room.currentState.mayTriggerNotifOfType('room', client.credentials.userId)) return [];
const {command, range} = this.getCurrentCommand(query, selection, force); const { command, range } = this.getCurrentCommand(query, selection, force);
if (command && command[0] && '@room'.startsWith(command[0]) && command[0].length > 1) { if (command && command[0] && '@room'.startsWith(command[0]) && command[0].length > 1) {
return [{ return [{
completion: '@room', completion: '@room',

View file

@ -16,8 +16,8 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {at, uniq} from 'lodash'; import { at, uniq } from 'lodash';
import {removeHiddenChars} from "matrix-js-sdk/src/utils"; import { removeHiddenChars } from "matrix-js-sdk/src/utils";
interface IOptions<T extends {}> { interface IOptions<T extends {}> {
keys: Array<string | keyof T>; keys: Array<string | keyof T>;
@ -112,7 +112,7 @@ export default class QueryMatcher<T extends Object> {
const index = resultKey.indexOf(query); const index = resultKey.indexOf(query);
if (index !== -1) { if (index !== -1) {
matches.push( matches.push(
...candidates.map((candidate) => ({index, ...candidate})), ...candidates.map((candidate) => ({ index, ...candidate })),
); );
} }
} }

View file

@ -73,7 +73,7 @@ export default class RoomProvider extends AutocompleteProvider {
limit = -1, limit = -1,
): Promise<ICompletion[]> { ): Promise<ICompletion[]> {
let completions = []; 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
let matcherObjects = this.getRooms().reduce((aliases, room) => { let matcherObjects = this.getRooms().reduce((aliases, room) => {

View file

@ -17,7 +17,7 @@ limitations under the License.
import React from "react"; import React from "react";
import { _t } from '../languageHandler'; import { _t } from '../languageHandler';
import {MatrixClientPeg} from '../MatrixClientPeg'; import { MatrixClientPeg } from '../MatrixClientPeg';
import RoomProvider from "./RoomProvider"; import RoomProvider from "./RoomProvider";
export default class SpaceProvider extends RoomProvider { export default class SpaceProvider extends RoomProvider {

View file

@ -114,7 +114,7 @@ export default class UserProvider extends AutocompleteProvider {
if (!this.users) this._makeUsers(); if (!this.users) this._makeUsers();
let completions = []; let completions = [];
const {command, range} = this.getCurrentCommand(rawQuery, selection, force); const { command, range } = this.getCurrentCommand(rawQuery, selection, force);
if (!command) return completions; if (!command) return completions;
@ -158,7 +158,7 @@ export default class UserProvider extends AutocompleteProvider {
} }
const currentUserId = MatrixClientPeg.get().credentials.userId; const currentUserId = MatrixClientPeg.get().credentials.userId;
this.users = this.room.getJoinedMembers().filter(({userId}) => userId !== currentUserId); this.users = this.room.getJoinedMembers().filter(({ userId }) => userId !== currentUserId);
this.users = this.users.concat(this.room.getMembersWithMembership("invite")); this.users = this.users.concat(this.room.getMembersWithMembership("invite"));
this.users = sortBy(this.users, (member) => 1E20 - lastSpoken[member.userId] || 1E20); this.users = sortBy(this.users, (member) => 1E20 - lastSpoken[member.userId] || 1E20);

View file

@ -15,12 +15,12 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React, { HTMLAttributes } from "react"; import React, { HTMLAttributes, WheelEvent } from "react";
interface IProps extends HTMLAttributes<HTMLDivElement> { interface IProps extends Omit<HTMLAttributes<HTMLDivElement>, "onScroll"> {
className?: string; className?: string;
onScroll?: () => void; onScroll?: (event: Event) => void;
onWheel?: () => void; onWheel?: (event: WheelEvent) => void;
style?: React.CSSProperties style?: React.CSSProperties
tabIndex?: number, tabIndex?: number,
wrappedRef?: (ref: HTMLDivElement) => void; wrappedRef?: (ref: HTMLDivElement) => void;

View file

@ -16,13 +16,13 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React, {CSSProperties, RefObject, useRef, useState} from "react"; import React, { CSSProperties, RefObject, useRef, useState } from "react";
import ReactDOM from "react-dom"; import ReactDOM from "react-dom";
import classNames from "classnames"; import classNames from "classnames";
import {Key} from "../../Keyboard"; import { Key } from "../../Keyboard";
import {Writeable} from "../../@types/common"; import { Writeable } from "../../@types/common";
import {replaceableComponent} from "../../utils/replaceableComponent"; import { replaceableComponent } from "../../utils/replaceableComponent";
import UIStore from "../../stores/UIStore"; import UIStore from "../../stores/UIStore";
// Shamelessly ripped off Modal.js. There's probably a better way // Shamelessly ripped off Modal.js. There's probably a better way
@ -371,7 +371,7 @@ export class ContextMenu extends React.PureComponent<IProps, IState> {
return ( return (
<div <div
className={classNames("mx_ContextualMenu_wrapper", this.props.wrapperClassName)} className={classNames("mx_ContextualMenu_wrapper", this.props.wrapperClassName)}
style={{...position, ...wrapperStyle}} style={{ ...position, ...wrapperStyle }}
onKeyDown={this.onKeyDown} onKeyDown={this.onKeyDown}
onContextMenu={this.onContextMenuPreventBubbling} onContextMenu={this.onContextMenuPreventBubbling}
> >
@ -399,7 +399,7 @@ export const toRightOf = (elementRect: Pick<DOMRect, "right" | "top" | "height">
const left = elementRect.right + window.pageXOffset + 3; const left = elementRect.right + window.pageXOffset + 3;
let top = elementRect.top + (elementRect.height / 2) + window.pageYOffset; let top = elementRect.top + (elementRect.height / 2) + window.pageYOffset;
top -= chevronOffset + 8; // where 8 is half the height of the chevron top -= chevronOffset + 8; // where 8 is half the height of the chevron
return {left, top, chevronOffset}; return { left, top, chevronOffset };
}; };
// 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,
@ -498,15 +498,15 @@ export function createMenu(ElementClass, props) {
ReactDOM.render(menu, getOrCreateContainer()); ReactDOM.render(menu, getOrCreateContainer());
return {close: onFinished}; return { close: onFinished };
} }
// re-export the semantic helper components for simplicity // re-export the semantic helper components for simplicity
export {ContextMenuButton} from "../../accessibility/context_menu/ContextMenuButton"; export { ContextMenuButton } from "../../accessibility/context_menu/ContextMenuButton";
export {ContextMenuTooltipButton} from "../../accessibility/context_menu/ContextMenuTooltipButton"; export { ContextMenuTooltipButton } from "../../accessibility/context_menu/ContextMenuTooltipButton";
export {MenuGroup} from "../../accessibility/context_menu/MenuGroup"; export { MenuGroup } from "../../accessibility/context_menu/MenuGroup";
export {MenuItem} from "../../accessibility/context_menu/MenuItem"; export { MenuItem } from "../../accessibility/context_menu/MenuItem";
export {MenuItemCheckbox} from "../../accessibility/context_menu/MenuItemCheckbox"; export { MenuItemCheckbox } from "../../accessibility/context_menu/MenuItemCheckbox";
export {MenuItemRadio} from "../../accessibility/context_menu/MenuItemRadio"; export { MenuItemRadio } from "../../accessibility/context_menu/MenuItemRadio";
export {StyledMenuItemCheckbox} from "../../accessibility/context_menu/StyledMenuItemCheckbox"; export { StyledMenuItemCheckbox } from "../../accessibility/context_menu/StyledMenuItemCheckbox";
export {StyledMenuItemRadio} from "../../accessibility/context_menu/StyledMenuItemRadio"; export { StyledMenuItemRadio } from "../../accessibility/context_menu/StyledMenuItemRadio";

View file

@ -21,7 +21,7 @@ import * as sdk from '../../index';
import dis from '../../dispatcher/dispatcher'; import dis from '../../dispatcher/dispatcher';
import classNames from 'classnames'; import classNames from 'classnames';
import * as FormattingUtils from '../../utils/FormattingUtils'; import * as FormattingUtils from '../../utils/FormattingUtils';
import {replaceableComponent} from "../../utils/replaceableComponent"; import { replaceableComponent } from "../../utils/replaceableComponent";
@replaceableComponent("structures.CustomRoomTagPanel") @replaceableComponent("structures.CustomRoomTagPanel")
class CustomRoomTagPanel extends React.Component { class CustomRoomTagPanel extends React.Component {
@ -34,7 +34,7 @@ class CustomRoomTagPanel extends React.Component {
componentDidMount() { componentDidMount() {
this._tagStoreToken = CustomRoomTagStore.addListener(() => { this._tagStoreToken = CustomRoomTagStore.addListener(() => {
this.setState({tags: CustomRoomTagStore.getSortedTags()}); this.setState({ tags: CustomRoomTagStore.getSortedTags() });
}); });
} }
@ -64,7 +64,7 @@ class CustomRoomTagPanel extends React.Component {
class CustomRoomTagTile extends React.Component { class CustomRoomTagTile extends React.Component {
onClick = () => { onClick = () => {
dis.dispatch({action: 'select_custom_room_tag', tag: this.props.tag.name}); dis.dispatch({ action: 'select_custom_room_tag', tag: this.props.tag.name });
}; };
render() { render() {

View file

@ -22,7 +22,7 @@ import request from 'browser-request';
import { _t } from '../../languageHandler'; import { _t } from '../../languageHandler';
import sanitizeHtml from 'sanitize-html'; import sanitizeHtml from 'sanitize-html';
import dis from '../../dispatcher/dispatcher'; import dis from '../../dispatcher/dispatcher';
import {MatrixClientPeg} from '../../MatrixClientPeg'; import { MatrixClientPeg } from '../../MatrixClientPeg';
import classnames from 'classnames'; import classnames from 'classnames';
import MatrixClientContext from "../../contexts/MatrixClientContext"; import MatrixClientContext from "../../contexts/MatrixClientContext";
import AutoHideScrollbar from "./AutoHideScrollbar"; import AutoHideScrollbar from "./AutoHideScrollbar";

View file

@ -16,37 +16,49 @@ limitations under the License.
*/ */
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types';
import {Filter} from 'matrix-js-sdk/src/filter'; import { Filter } from 'matrix-js-sdk/src/filter';
import { EventTimelineSet } from "matrix-js-sdk/src/models/event-timeline-set";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { Room } from 'matrix-js-sdk/src/models/room';
import { TimelineWindow } from 'matrix-js-sdk/src/timeline-window';
import * as sdk from '../../index'; import * as sdk from '../../index';
import {MatrixClientPeg} from '../../MatrixClientPeg'; import { MatrixClientPeg } from '../../MatrixClientPeg';
import EventIndexPeg from "../../indexing/EventIndexPeg"; import EventIndexPeg from "../../indexing/EventIndexPeg";
import { _t } from '../../languageHandler'; import { _t } from '../../languageHandler';
import BaseCard from "../views/right_panel/BaseCard"; import BaseCard from "../views/right_panel/BaseCard";
import {RightPanelPhases} from "../../stores/RightPanelStorePhases"; import { RightPanelPhases } from "../../stores/RightPanelStorePhases";
import DesktopBuildsNotice, {WarningKind} from "../views/elements/DesktopBuildsNotice"; import DesktopBuildsNotice, { WarningKind } from "../views/elements/DesktopBuildsNotice";
import {replaceableComponent} from "../../utils/replaceableComponent"; import { replaceableComponent } from "../../utils/replaceableComponent";
import ResizeNotifier from '../../utils/ResizeNotifier';
interface IProps {
roomId: string;
onClose: () => void;
resizeNotifier: ResizeNotifier
}
interface IState {
timelineSet: EventTimelineSet;
}
/* /*
* Component which shows the filtered file using a TimelinePanel * Component which shows the filtered file using a TimelinePanel
*/ */
@replaceableComponent("structures.FilePanel") @replaceableComponent("structures.FilePanel")
class FilePanel extends React.Component { class FilePanel extends React.Component<IProps, IState> {
static propTypes = {
roomId: PropTypes.string.isRequired,
onClose: PropTypes.func.isRequired,
};
// This is used to track if a decrypted event was a live event and should be // This is used to track if a decrypted event was a live event and should be
// added to the timeline. // added to the timeline.
decryptingEvents = new Set(); private decryptingEvents = new Set<string>();
public noRoom: boolean;
state = { state = {
timelineSet: null, timelineSet: null,
}; };
onRoomTimeline = (ev, room, toStartOfTimeline, removed, data) => { private onRoomTimeline = (ev: MatrixEvent, room: Room, toStartOfTimeline: true, removed: true, data: any): void => {
if (room?.roomId !== this.props?.roomId) return; if (room?.roomId !== this.props?.roomId) return;
if (toStartOfTimeline || !data || !data.liveEvent || ev.isRedacted()) return; if (toStartOfTimeline || !data || !data.liveEvent || ev.isRedacted()) return;
@ -60,7 +72,7 @@ class FilePanel extends React.Component {
} }
}; };
onEventDecrypted = (ev, err) => { private onEventDecrypted = (ev: MatrixEvent, err?: any): void => {
if (ev.getRoomId() !== this.props.roomId) return; if (ev.getRoomId() !== this.props.roomId) return;
const eventId = ev.getId(); const eventId = ev.getId();
@ -70,7 +82,7 @@ class FilePanel extends React.Component {
this.addEncryptedLiveEvent(ev); this.addEncryptedLiveEvent(ev);
}; };
addEncryptedLiveEvent(ev, toStartOfTimeline) { public addEncryptedLiveEvent(ev: MatrixEvent): void {
if (!this.state.timelineSet) return; if (!this.state.timelineSet) return;
const timeline = this.state.timelineSet.getLiveTimeline(); const timeline = this.state.timelineSet.getLiveTimeline();
@ -84,7 +96,7 @@ class FilePanel extends React.Component {
} }
} }
async componentDidMount() { public async componentDidMount(): Promise<void> {
const client = MatrixClientPeg.get(); const client = MatrixClientPeg.get();
await this.updateTimelineSet(this.props.roomId); await this.updateTimelineSet(this.props.roomId);
@ -105,7 +117,7 @@ class FilePanel extends React.Component {
} }
} }
componentWillUnmount() { public componentWillUnmount(): void {
const client = MatrixClientPeg.get(); const client = MatrixClientPeg.get();
if (client === null) return; if (client === null) return;
@ -117,7 +129,7 @@ class FilePanel extends React.Component {
} }
} }
async fetchFileEventsServer(room) { public async fetchFileEventsServer(room: Room): Promise<void> {
const client = MatrixClientPeg.get(); const client = MatrixClientPeg.get();
const filter = new Filter(client.credentials.userId); const filter = new Filter(client.credentials.userId);
@ -141,7 +153,7 @@ class FilePanel extends React.Component {
return timelineSet; return timelineSet;
} }
onPaginationRequest = (timelineWindow, direction, limit) => { private onPaginationRequest = (timelineWindow: TimelineWindow, direction: string, limit: number): void => {
const client = MatrixClientPeg.get(); const client = MatrixClientPeg.get();
const eventIndex = EventIndexPeg.get(); const eventIndex = EventIndexPeg.get();
const roomId = this.props.roomId; const roomId = this.props.roomId;
@ -159,7 +171,7 @@ class FilePanel extends React.Component {
} }
}; };
async updateTimelineSet(roomId: string) { public async updateTimelineSet(roomId: string): Promise<void> {
const client = MatrixClientPeg.get(); const client = MatrixClientPeg.get();
const room = client.getRoom(roomId); const room = client.getRoom(roomId);
const eventIndex = EventIndexPeg.get(); const eventIndex = EventIndexPeg.get();
@ -195,7 +207,7 @@ class FilePanel extends React.Component {
} }
} }
render() { public render() {
if (MatrixClientPeg.get().isGuest()) { if (MatrixClientPeg.get().isGuest()) {
return <BaseCard return <BaseCard
className="mx_FilePanel mx_RoomView_messageListWrapper" className="mx_FilePanel mx_RoomView_messageListWrapper"

View file

@ -16,7 +16,7 @@ limitations under the License.
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import {replaceableComponent} from "../../utils/replaceableComponent"; import { replaceableComponent } from "../../utils/replaceableComponent";
@replaceableComponent("structures.GenericErrorPage") @replaceableComponent("structures.GenericErrorPage")
export default class GenericErrorPage extends React.PureComponent { export default class GenericErrorPage extends React.PureComponent {

View file

@ -29,7 +29,7 @@ import MatrixClientContext from "../../contexts/MatrixClientContext";
import AutoHideScrollbar from "./AutoHideScrollbar"; import AutoHideScrollbar from "./AutoHideScrollbar";
import SettingsStore from "../../settings/SettingsStore"; import SettingsStore from "../../settings/SettingsStore";
import UserTagTile from "../views/elements/UserTagTile"; import UserTagTile from "../views/elements/UserTagTile";
import {replaceableComponent} from "../../utils/replaceableComponent"; import { replaceableComponent } from "../../utils/replaceableComponent";
@replaceableComponent("structures.GroupFilterPanel") @replaceableComponent("structures.GroupFilterPanel")
class GroupFilterPanel extends React.Component { class GroupFilterPanel extends React.Component {
@ -85,12 +85,12 @@ class GroupFilterPanel extends React.Component {
onClick = e => { onClick = e => {
// only dispatch if its not a no-op // only dispatch if its not a no-op
if (this.state.selectedTags.length > 0) { if (this.state.selectedTags.length > 0) {
dis.dispatch({action: 'deselect_tags'}); dis.dispatch({ action: 'deselect_tags' });
} }
}; };
onClearFilterClick = ev => { onClearFilterClick = ev => {
dis.dispatch({action: 'deselect_tags'}); dis.dispatch({ action: 'deselect_tags' });
}; };
renderGlobalIcon() { renderGlobalIcon() {

View file

@ -18,7 +18,7 @@ limitations under the License.
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import {MatrixClientPeg} from '../../MatrixClientPeg'; import { MatrixClientPeg } from '../../MatrixClientPeg';
import * as sdk from '../../index'; import * as sdk from '../../index';
import dis from '../../dispatcher/dispatcher'; import dis from '../../dispatcher/dispatcher';
import { getHostingLink } from '../../utils/HostingLink'; import { getHostingLink } from '../../utils/HostingLink';
@ -34,13 +34,13 @@ import classnames from 'classnames';
import GroupStore from '../../stores/GroupStore'; import GroupStore from '../../stores/GroupStore';
import FlairStore from '../../stores/FlairStore'; import FlairStore from '../../stores/FlairStore';
import { showGroupAddRoomDialog } from '../../GroupAddressPicker'; import { showGroupAddRoomDialog } from '../../GroupAddressPicker';
import {makeGroupPermalink, makeUserPermalink} from "../../utils/permalinks/Permalinks"; import { makeGroupPermalink, makeUserPermalink } from "../../utils/permalinks/Permalinks";
import {Group} from "matrix-js-sdk/src/models/group"; import { Group } from "matrix-js-sdk/src/models/group";
import {sleep} from "../../utils/promise"; import { sleep } from "../../utils/promise";
import RightPanelStore from "../../stores/RightPanelStore"; import RightPanelStore from "../../stores/RightPanelStore";
import AutoHideScrollbar from "./AutoHideScrollbar"; import AutoHideScrollbar from "./AutoHideScrollbar";
import {mediaFromMxc} from "../../customisations/Media"; import { mediaFromMxc } from "../../customisations/Media";
import {replaceableComponent} from "../../utils/replaceableComponent"; import { replaceableComponent } from "../../utils/replaceableComponent";
const LONG_DESC_PLACEHOLDER = _td( const LONG_DESC_PLACEHOLDER = _td(
`<h1>HTML for your community's page</h1> `<h1>HTML for your community's page</h1>
@ -115,7 +115,7 @@ class CategoryRoomList extends React.Component {
{ {
title: _t( title: _t(
"Failed to add the following rooms to the summary of %(groupId)s:", "Failed to add the following rooms to the summary of %(groupId)s:",
{groupId: this.props.groupId}, { groupId: this.props.groupId },
), ),
description: errorList.join(", "), description: errorList.join(", "),
}, },
@ -126,12 +126,11 @@ class CategoryRoomList extends React.Component {
}; };
render() { render() {
const TintableSvg = sdk.getComponent("elements.TintableSvg");
const addButton = this.props.editing ? const addButton = this.props.editing ?
(<AccessibleButton className="mx_GroupView_featuredThings_addButton" (<AccessibleButton className="mx_GroupView_featuredThings_addButton"
onClick={this.onAddRoomsToSummaryClicked} onClick={this.onAddRoomsToSummaryClicked}
> >
<TintableSvg src={require("../../../res/img/icons-create-room.svg")} width="64" height="64" /> <img src={require("../../../res/img/icons-create-room.svg")} width="64" height="64" />
<div className="mx_GroupView_featuredThings_addButton_label"> <div className="mx_GroupView_featuredThings_addButton_label">
{ _t('Add a Room') } { _t('Add a Room') }
</div> </div>
@ -195,9 +194,9 @@ class FeaturedRoom extends React.Component {
{ {
title: _t( title: _t(
"Failed to remove the room from the summary of %(groupId)s", "Failed to remove the room from the summary of %(groupId)s",
{groupId: this.props.groupId}, { groupId: this.props.groupId },
), ),
description: _t("The room '%(roomName)s' could not be removed from the summary.", {roomName}), description: _t("The room '%(roomName)s' could not be removed from the summary.", { roomName }),
}, },
); );
}); });
@ -289,7 +288,7 @@ class RoleUserList extends React.Component {
{ {
title: _t( title: _t(
"Failed to add the following users to the summary of %(groupId)s:", "Failed to add the following users to the summary of %(groupId)s:",
{groupId: this.props.groupId}, { groupId: this.props.groupId },
), ),
description: errorList.join(", "), description: errorList.join(", "),
}, },
@ -300,10 +299,9 @@ class RoleUserList extends React.Component {
}; };
render() { render() {
const TintableSvg = sdk.getComponent("elements.TintableSvg");
const addButton = this.props.editing ? const addButton = this.props.editing ?
(<AccessibleButton className="mx_GroupView_featuredThings_addButton" onClick={this.onAddUsersClicked}> (<AccessibleButton className="mx_GroupView_featuredThings_addButton" onClick={this.onAddUsersClicked}>
<TintableSvg src={require("../../../res/img/icons-create-room.svg")} width="64" height="64" /> <img src={require("../../../res/img/icons-create-room.svg")} width="64" height="64" />
<div className="mx_GroupView_featuredThings_addButton_label"> <div className="mx_GroupView_featuredThings_addButton_label">
{ _t('Add a User') } { _t('Add a User') }
</div> </div>
@ -361,9 +359,12 @@ class FeaturedUser extends React.Component {
{ {
title: _t( title: _t(
"Failed to remove a user from the summary of %(groupId)s", "Failed to remove a user from the summary of %(groupId)s",
{groupId: this.props.groupId}, { groupId: this.props.groupId },
),
description: _t(
"The user '%(displayName)s' could not be removed from the summary.",
{ displayName },
), ),
description: _t("The user '%(displayName)s' could not be removed from the summary.", {displayName}),
}, },
); );
}); });
@ -470,7 +471,7 @@ export default class GroupView extends React.Component {
// Leave settings - the user might have clicked the "Leave" button // Leave settings - the user might have clicked the "Leave" button
this._closeSettings(); this._closeSettings();
} }
this.setState({membershipBusy: false}); this.setState({ membershipBusy: false });
}; };
_initGroupStore(groupId, firstInit) { _initGroupStore(groupId, firstInit) {
@ -491,7 +492,7 @@ export default class GroupView extends React.Component {
group_id: groupId, group_id: groupId,
}, },
}); });
dis.dispatch({action: 'require_registration', screen_after: {screen: `group/${groupId}`}}); dis.dispatch({ action: 'require_registration', screen_after: { screen: `group/${groupId}` } });
willDoOnboarding = true; willDoOnboarding = true;
} }
if (stateKey === GroupStore.STATE_KEY.Summary) { if (stateKey === GroupStore.STATE_KEY.Summary) {
@ -592,7 +593,7 @@ export default class GroupView extends React.Component {
}; };
_closeSettings = () => { _closeSettings = () => {
dis.dispatch({action: 'close_settings'}); dis.dispatch({ action: 'close_settings' });
}; };
_onNameChange = (value) => { _onNameChange = (value) => {
@ -620,7 +621,7 @@ export default class GroupView extends React.Component {
const file = ev.target.files[0]; const file = ev.target.files[0];
if (!file) return; if (!file) return;
this.setState({uploadingAvatar: true}); this.setState({ uploadingAvatar: true });
this._matrixClient.uploadContent(file).then((url) => { this._matrixClient.uploadContent(file).then((url) => {
const newProfileForm = Object.assign(this.state.profileForm, { avatar_url: url }); const newProfileForm = Object.assign(this.state.profileForm, { avatar_url: url });
this.setState({ this.setState({
@ -632,7 +633,7 @@ export default class GroupView extends React.Component {
avatarChanged: true, avatarChanged: true,
}); });
}).catch((e) => { }).catch((e) => {
this.setState({uploadingAvatar: false}); this.setState({ uploadingAvatar: false });
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
console.error("Failed to upload avatar image", e); console.error("Failed to upload avatar image", e);
Modal.createTrackedDialog('Failed to upload image', '', ErrorDialog, { Modal.createTrackedDialog('Failed to upload image', '', ErrorDialog, {
@ -649,7 +650,7 @@ export default class GroupView extends React.Component {
}; };
_onSaveClick = () => { _onSaveClick = () => {
this.setState({saving: true}); this.setState({ saving: true });
const savePromise = this.state.isUserPrivileged ? this._saveGroup() : Promise.resolve(); const savePromise = this.state.isUserPrivileged ? this._saveGroup() : Promise.resolve();
savePromise.then((result) => { savePromise.then((result) => {
this.setState({ this.setState({
@ -688,7 +689,7 @@ export default class GroupView extends React.Component {
} }
_onAcceptInviteClick = async () => { _onAcceptInviteClick = async () => {
this.setState({membershipBusy: true}); this.setState({ membershipBusy: true });
// Wait 500ms to prevent flashing. Do this before sending a request otherwise we risk the // Wait 500ms to prevent flashing. Do this before sending a request otherwise we risk the
// spinner disappearing after we have fetched new group data. // spinner disappearing after we have fetched new group data.
@ -697,7 +698,7 @@ export default class GroupView extends React.Component {
GroupStore.acceptGroupInvite(this.props.groupId).then(() => { GroupStore.acceptGroupInvite(this.props.groupId).then(() => {
// don't reset membershipBusy here: wait for the membership change to come down the sync // don't reset membershipBusy here: wait for the membership change to come down the sync
}).catch((e) => { }).catch((e) => {
this.setState({membershipBusy: false}); this.setState({ membershipBusy: false });
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog('Error accepting invite', '', ErrorDialog, { Modal.createTrackedDialog('Error accepting invite', '', ErrorDialog, {
title: _t("Error"), title: _t("Error"),
@ -707,7 +708,7 @@ export default class GroupView extends React.Component {
}; };
_onRejectInviteClick = async () => { _onRejectInviteClick = async () => {
this.setState({membershipBusy: true}); this.setState({ membershipBusy: true });
// Wait 500ms to prevent flashing. Do this before sending a request otherwise we risk the // Wait 500ms to prevent flashing. Do this before sending a request otherwise we risk the
// spinner disappearing after we have fetched new group data. // spinner disappearing after we have fetched new group data.
@ -716,7 +717,7 @@ export default class GroupView extends React.Component {
GroupStore.leaveGroup(this.props.groupId).then(() => { GroupStore.leaveGroup(this.props.groupId).then(() => {
// don't reset membershipBusy here: wait for the membership change to come down the sync // don't reset membershipBusy here: wait for the membership change to come down the sync
}).catch((e) => { }).catch((e) => {
this.setState({membershipBusy: false}); this.setState({ membershipBusy: false });
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog('Error rejecting invite', '', ErrorDialog, { Modal.createTrackedDialog('Error rejecting invite', '', ErrorDialog, {
title: _t("Error"), title: _t("Error"),
@ -727,11 +728,11 @@ export default class GroupView extends React.Component {
_onJoinClick = async () => { _onJoinClick = async () => {
if (this._matrixClient.isGuest()) { if (this._matrixClient.isGuest()) {
dis.dispatch({action: 'require_registration', screen_after: {screen: `group/${this.props.groupId}`}}); dis.dispatch({ action: 'require_registration', screen_after: { screen: `group/${this.props.groupId}` } });
return; return;
} }
this.setState({membershipBusy: true}); this.setState({ membershipBusy: true });
// Wait 500ms to prevent flashing. Do this before sending a request otherwise we risk the // Wait 500ms to prevent flashing. Do this before sending a request otherwise we risk the
// spinner disappearing after we have fetched new group data. // spinner disappearing after we have fetched new group data.
@ -740,7 +741,7 @@ export default class GroupView extends React.Component {
GroupStore.joinGroup(this.props.groupId).then(() => { GroupStore.joinGroup(this.props.groupId).then(() => {
// don't reset membershipBusy here: wait for the membership change to come down the sync // don't reset membershipBusy here: wait for the membership change to come down the sync
}).catch((e) => { }).catch((e) => {
this.setState({membershipBusy: false}); this.setState({ membershipBusy: false });
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog('Error joining room', '', ErrorDialog, { Modal.createTrackedDialog('Error joining room', '', ErrorDialog, {
title: _t("Error"), title: _t("Error"),
@ -773,7 +774,7 @@ export default class GroupView extends React.Component {
title: _t("Leave Community"), title: _t("Leave Community"),
description: ( description: (
<span> <span>
{ _t("Leave %(groupName)s?", {groupName: this.props.groupId}) } { _t("Leave %(groupName)s?", { groupName: this.props.groupId }) }
{ warnings } { warnings }
</span> </span>
), ),
@ -782,7 +783,7 @@ export default class GroupView extends React.Component {
onFinished: async (confirmed) => { onFinished: async (confirmed) => {
if (!confirmed) return; if (!confirmed) return;
this.setState({membershipBusy: true}); this.setState({ membershipBusy: true });
// Wait 500ms to prevent flashing. Do this before sending a request otherwise we risk the // Wait 500ms to prevent flashing. Do this before sending a request otherwise we risk the
// spinner disappearing after we have fetched new group data. // spinner disappearing after we have fetched new group data.
@ -791,7 +792,7 @@ export default class GroupView extends React.Component {
GroupStore.leaveGroup(this.props.groupId).then(() => { GroupStore.leaveGroup(this.props.groupId).then(() => {
// don't reset membershipBusy here: wait for the membership change to come down the sync // don't reset membershipBusy here: wait for the membership change to come down the sync
}).catch((e) => { }).catch((e) => {
this.setState({membershipBusy: false}); this.setState({ membershipBusy: false });
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog('Error leaving community', '', ErrorDialog, { Modal.createTrackedDialog('Error leaving community', '', ErrorDialog, {
title: _t("Error"), title: _t("Error"),
@ -855,7 +856,6 @@ export default class GroupView extends React.Component {
_getRoomsNode() { _getRoomsNode() {
const RoomDetailList = sdk.getComponent('rooms.RoomDetailList'); const RoomDetailList = sdk.getComponent('rooms.RoomDetailList');
const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
const TintableSvg = sdk.getComponent('elements.TintableSvg');
const Spinner = sdk.getComponent('elements.Spinner'); const Spinner = sdk.getComponent('elements.Spinner');
const TooltipButton = sdk.getComponent('elements.TooltipButton'); const TooltipButton = sdk.getComponent('elements.TooltipButton');
@ -871,7 +871,7 @@ export default class GroupView extends React.Component {
onClick={this._onAddRoomsClick} onClick={this._onAddRoomsClick}
> >
<div className="mx_GroupView_rooms_header_addRow_button"> <div className="mx_GroupView_rooms_header_addRow_button">
<TintableSvg src={require("../../../res/img/icons-room-add.svg")} width="24" height="24" /> <img src={require("../../../res/img/icons-room-add.svg")} width="24" height="24" />
</div> </div>
<div className="mx_GroupView_rooms_header_addRow_label"> <div className="mx_GroupView_rooms_header_addRow_label">
{ _t('Add rooms to this community') } { _t('Add rooms to this community') }
@ -1336,7 +1336,7 @@ export default class GroupView extends React.Component {
if (this.state.error.httpStatus === 404) { if (this.state.error.httpStatus === 404) {
return ( return (
<div className="mx_GroupView_error"> <div className="mx_GroupView_error">
{ _t('Community %(groupId)s not found', {groupId: this.props.groupId}) } { _t('Community %(groupId)s not found', { groupId: this.props.groupId }) }
</div> </div>
); );
} else { } else {
@ -1346,7 +1346,7 @@ export default class GroupView extends React.Component {
} }
return ( return (
<div className="mx_GroupView_error"> <div className="mx_GroupView_error">
{ _t('Failed to load %(groupId)s', {groupId: this.props.groupId }) } { _t('Failed to load %(groupId)s', { groupId: this.props.groupId }) }
{ extraText } { extraText }
</div> </div>
); );

View file

@ -15,29 +15,29 @@ limitations under the License.
*/ */
import * as React from "react"; import * as React from "react";
import {useContext, useState} from "react"; import { useContext, useState } from "react";
import AutoHideScrollbar from './AutoHideScrollbar'; import AutoHideScrollbar from './AutoHideScrollbar';
import {getHomePageUrl} from "../../utils/pages"; import { getHomePageUrl } from "../../utils/pages";
import {_t} from "../../languageHandler"; import { _t } from "../../languageHandler";
import SdkConfig from "../../SdkConfig"; import SdkConfig from "../../SdkConfig";
import * as sdk from "../../index"; import * as sdk from "../../index";
import dis from "../../dispatcher/dispatcher"; import dis from "../../dispatcher/dispatcher";
import {Action} from "../../dispatcher/actions"; import { Action } from "../../dispatcher/actions";
import BaseAvatar from "../views/avatars/BaseAvatar"; import BaseAvatar from "../views/avatars/BaseAvatar";
import {OwnProfileStore} from "../../stores/OwnProfileStore"; import { OwnProfileStore } from "../../stores/OwnProfileStore";
import AccessibleButton from "../views/elements/AccessibleButton"; import AccessibleButton from "../views/elements/AccessibleButton";
import {UPDATE_EVENT} from "../../stores/AsyncStore"; import { UPDATE_EVENT } from "../../stores/AsyncStore";
import {useEventEmitter} from "../../hooks/useEventEmitter"; import { useEventEmitter } from "../../hooks/useEventEmitter";
import MatrixClientContext from "../../contexts/MatrixClientContext"; import MatrixClientContext from "../../contexts/MatrixClientContext";
import MiniAvatarUploader, {AVATAR_SIZE} from "../views/elements/MiniAvatarUploader"; import MiniAvatarUploader, { AVATAR_SIZE } from "../views/elements/MiniAvatarUploader";
import Analytics from "../../Analytics"; import Analytics from "../../Analytics";
import CountlyAnalytics from "../../CountlyAnalytics"; import CountlyAnalytics from "../../CountlyAnalytics";
const onClickSendDm = () => { const onClickSendDm = () => {
Analytics.trackEvent('home_page', 'button', 'dm'); Analytics.trackEvent('home_page', 'button', 'dm');
CountlyAnalytics.instance.track("home_page_button", { button: "dm" }); CountlyAnalytics.instance.track("home_page_button", { button: "dm" });
dis.dispatch({action: 'view_create_chat'}); dis.dispatch({ action: 'view_create_chat' });
}; };
const onClickExplore = () => { const onClickExplore = () => {
@ -49,7 +49,7 @@ const onClickExplore = () => {
const onClickNewRoom = () => { const onClickNewRoom = () => {
Analytics.trackEvent('home_page', 'button', 'create_room'); Analytics.trackEvent('home_page', 'button', 'create_room');
CountlyAnalytics.instance.track("home_page_button", { button: "create_room" }); CountlyAnalytics.instance.track("home_page_button", { button: "create_room" });
dis.dispatch({action: 'view_create_room'}); dis.dispatch({ action: 'view_create_room' });
}; };
interface IProps { interface IProps {
@ -117,7 +117,6 @@ const HomePage: React.FC<IProps> = ({ justRegistered = false }) => {
</React.Fragment>; </React.Fragment>;
} }
return <AutoHideScrollbar className="mx_HomePage mx_HomePage_default"> return <AutoHideScrollbar className="mx_HomePage mx_HomePage_default">
<div className="mx_HomePage_default_wrapper"> <div className="mx_HomePage_default_wrapper">
{ introSection } { introSection }

View file

@ -22,7 +22,7 @@ import {
import { _t } from "../../languageHandler"; import { _t } from "../../languageHandler";
import { HostSignupStore } from "../../stores/HostSignupStore"; import { HostSignupStore } from "../../stores/HostSignupStore";
import SdkConfig from "../../SdkConfig"; import SdkConfig from "../../SdkConfig";
import {replaceableComponent} from "../../utils/replaceableComponent"; import { replaceableComponent } from "../../utils/replaceableComponent";
interface IProps { interface IProps {
onClick?(): void; onClick?(): void;
@ -35,7 +35,7 @@ export default class HostSignupAction extends React.PureComponent<IProps, IState
private openDialog = async () => { private openDialog = async () => {
this.props.onClick?.(); this.props.onClick?.();
await HostSignupStore.instance.setHostSignupActive(true); await HostSignupStore.instance.setHostSignupActive(true);
} };
public render(): React.ReactNode { public render(): React.ReactNode {
const hostSignupConfig = SdkConfig.get().hostSignup; const hostSignupConfig = SdkConfig.get().hostSignup;

View file

@ -17,7 +17,7 @@ limitations under the License.
import React from "react"; import React from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import AutoHideScrollbar from "./AutoHideScrollbar"; import AutoHideScrollbar from "./AutoHideScrollbar";
import {replaceableComponent} from "../../utils/replaceableComponent"; import { replaceableComponent } from "../../utils/replaceableComponent";
@replaceableComponent("structures.IndicatorScrollbar") @replaceableComponent("structures.IndicatorScrollbar")
export default class IndicatorScrollbar extends React.Component { export default class IndicatorScrollbar extends React.Component {
@ -70,7 +70,6 @@ export default class IndicatorScrollbar extends React.Component {
this._autoHideScrollbar = autoHideScrollbar; this._autoHideScrollbar = autoHideScrollbar;
} }
componentDidUpdate(prevProps) { componentDidUpdate(prevProps) {
const prevLen = prevProps && prevProps.children && prevProps.children.length || 0; const prevLen = prevProps && prevProps.children && prevProps.children.length || 0;
const curLen = this.props.children && this.props.children.length || 0; const curLen = this.props.children && this.props.children.length || 0;
@ -188,8 +187,8 @@ export default class IndicatorScrollbar extends React.Component {
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
const { children, trackHorizontalOverflow, verticalScrollsHorizontally, ...otherProps } = this.props; const { children, trackHorizontalOverflow, verticalScrollsHorizontally, ...otherProps } = this.props;
const leftIndicatorStyle = {left: this.state.leftIndicatorOffset}; const leftIndicatorStyle = { left: this.state.leftIndicatorOffset };
const rightIndicatorStyle = {right: this.state.rightIndicatorOffset}; const rightIndicatorStyle = { right: this.state.rightIndicatorOffset };
const leftOverflowIndicator = trackHorizontalOverflow const leftOverflowIndicator = trackHorizontalOverflow
? <div className="mx_IndicatorScrollbar_leftOverflowIndicator" style={leftIndicatorStyle} /> : null; ? <div className="mx_IndicatorScrollbar_leftOverflowIndicator" style={leftIndicatorStyle} /> : null;
const rightOverflowIndicator = trackHorizontalOverflow const rightOverflowIndicator = trackHorizontalOverflow

View file

@ -15,14 +15,14 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {InteractiveAuth} from "matrix-js-sdk/src/interactive-auth"; import { InteractiveAuth } from "matrix-js-sdk/src/interactive-auth";
import React, {createRef} from 'react'; import React, { createRef } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import getEntryComponentForLoginType from '../views/auth/InteractiveAuthEntryComponents'; import getEntryComponentForLoginType from '../views/auth/InteractiveAuthEntryComponents';
import * as sdk from '../../index'; import * as sdk from '../../index';
import {replaceableComponent} from "../../utils/replaceableComponent"; import { replaceableComponent } from "../../utils/replaceableComponent";
export const ERROR_USER_CANCELLED = new Error("User cancelled auth session"); export const ERROR_USER_CANCELLED = new Error("User cancelled auth session");

View file

@ -40,9 +40,9 @@ import AccessibleTooltipButton from "../views/elements/AccessibleTooltipButton";
import { OwnProfileStore } from "../../stores/OwnProfileStore"; import { OwnProfileStore } from "../../stores/OwnProfileStore";
import RoomListNumResults from "../views/rooms/RoomListNumResults"; import RoomListNumResults from "../views/rooms/RoomListNumResults";
import LeftPanelWidget from "./LeftPanelWidget"; import LeftPanelWidget from "./LeftPanelWidget";
import {replaceableComponent} from "../../utils/replaceableComponent"; import { replaceableComponent } from "../../utils/replaceableComponent";
import {mediaFromMxc} from "../../customisations/Media"; import { mediaFromMxc } from "../../customisations/Media";
import SpaceStore, {UPDATE_SELECTED_SPACE} from "../../stores/SpaceStore"; import SpaceStore, { UPDATE_SELECTED_SPACE } from "../../stores/SpaceStore";
import { getKeyBindingsManager, RoomListAction } from "../../KeyBindingsManager"; import { getKeyBindingsManager, RoomListAction } from "../../KeyBindingsManager";
import UIStore from "../../stores/UIStore"; import UIStore from "../../stores/UIStore";
@ -91,7 +91,7 @@ export default class LeftPanel extends React.Component<IProps, IState> {
this.bgImageWatcherRef = SettingsStore.watchSetting( this.bgImageWatcherRef = SettingsStore.watchSetting(
"RoomList.backgroundImage", null, this.onBackgroundImageUpdate); "RoomList.backgroundImage", null, this.onBackgroundImageUpdate);
this.groupFilterPanelWatcherRef = SettingsStore.watchSetting("TagPanel.enableTagPanel", null, () => { this.groupFilterPanelWatcherRef = SettingsStore.watchSetting("TagPanel.enableTagPanel", null, () => {
this.setState({showGroupFilterPanel: SettingsStore.getValue("TagPanel.enableTagPanel")}); this.setState({ showGroupFilterPanel: SettingsStore.getValue("TagPanel.enableTagPanel") });
}); });
} }
@ -127,7 +127,7 @@ export default class LeftPanel extends React.Component<IProps, IState> {
private onDialPad = () => { private onDialPad = () => {
dis.fire(Action.OpenDialPad); dis.fire(Action.OpenDialPad);
} };
private onExplore = () => { private onExplore = () => {
dis.fire(Action.ViewRoomDirectory); dis.fire(Action.ViewRoomDirectory);
@ -136,12 +136,12 @@ export default class LeftPanel extends React.Component<IProps, IState> {
private refreshStickyHeaders = () => { private refreshStickyHeaders = () => {
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 = () => {
const newVal = BreadcrumbsStore.instance.visible; const newVal = BreadcrumbsStore.instance.visible;
if (newVal !== this.state.showBreadcrumbs) { if (newVal !== this.state.showBreadcrumbs) {
this.setState({showBreadcrumbs: newVal}); this.setState({ showBreadcrumbs: newVal });
// Update the sticky headers too as the breadcrumbs will be popping in or out. // Update the sticky headers too as the breadcrumbs will be popping in or out.
if (!this.listContainerRef.current) return; // ignore: no headers to sticky if (!this.listContainerRef.current) return; // ignore: no headers to sticky

View file

@ -14,19 +14,19 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React, {useContext, useMemo} from "react"; import React, { useContext, useMemo } from "react";
import {Resizable} from "re-resizable"; import { Resizable } from "re-resizable";
import classNames from "classnames"; import classNames from "classnames";
import AccessibleButton from "../views/elements/AccessibleButton"; import AccessibleButton from "../views/elements/AccessibleButton";
import {useRovingTabIndex} from "../../accessibility/RovingTabIndex"; import { useRovingTabIndex } from "../../accessibility/RovingTabIndex";
import {Key} from "../../Keyboard"; import { Key } from "../../Keyboard";
import {useLocalStorageState} from "../../hooks/useLocalStorageState"; import { useLocalStorageState } from "../../hooks/useLocalStorageState";
import MatrixClientContext from "../../contexts/MatrixClientContext"; import MatrixClientContext from "../../contexts/MatrixClientContext";
import WidgetUtils, {IWidgetEvent} from "../../utils/WidgetUtils"; import WidgetUtils, { IWidgetEvent } from "../../utils/WidgetUtils";
import {useAccountData} from "../../hooks/useAccountData"; import { useAccountData } from "../../hooks/useAccountData";
import AppTile from "../views/elements/AppTile"; import AppTile from "../views/elements/AppTile";
import {useSettingValue} from "../../hooks/useSettings"; import { useSettingValue } from "../../hooks/useSettings";
import UIStore from "../../stores/UIStore"; import UIStore from "../../stores/UIStore";
const MIN_HEIGHT = 100; const MIN_HEIGHT = 100;
@ -62,14 +62,14 @@ const LeftPanelWidget: React.FC = () => {
let content; let content;
if (expanded) { if (expanded) {
content = <Resizable content = <Resizable
size={{height} as any} size={{ height } as any}
minHeight={MIN_HEIGHT} minHeight={MIN_HEIGHT}
maxHeight={Math.min(UIStore.instance.windowHeight / 2, MAX_HEIGHT)} maxHeight={Math.min(UIStore.instance.windowHeight / 2, MAX_HEIGHT)}
onResizeStop={(e, dir, ref, d) => { onResizeStop={(e, dir, ref, d) => {
setHeight(height + d.height); setHeight(height + d.height);
}} }}
handleWrapperClass="mx_LeftPanelWidget_resizerHandles" handleWrapperClass="mx_LeftPanelWidget_resizerHandles"
handleClasses={{top: "mx_LeftPanelWidget_resizerHandle"}} handleClasses={{ top: "mx_LeftPanelWidget_resizerHandle" }}
className="mx_LeftPanelWidget_resizeBox" className="mx_LeftPanelWidget_resizeBox"
enable={{ top: true }} enable={{ top: true }}
> >

View file

@ -20,7 +20,7 @@ import * as React from 'react';
import * as PropTypes from 'prop-types'; import * as PropTypes from 'prop-types';
import { MatrixClient } from 'matrix-js-sdk/src/client'; import { MatrixClient } from 'matrix-js-sdk/src/client';
import {Key} from '../../Keyboard'; import { Key } from '../../Keyboard';
import PageTypes from '../../PageTypes'; import PageTypes from '../../PageTypes';
import MediaDeviceHandler from '../../MediaDeviceHandler'; import MediaDeviceHandler from '../../MediaDeviceHandler';
import { fixupColorFonts } from '../../utils/FontManager'; import { fixupColorFonts } from '../../utils/FontManager';
@ -30,7 +30,7 @@ import { IMatrixClientCreds } from '../../MatrixClientPeg';
import SettingsStore from "../../settings/SettingsStore"; import SettingsStore from "../../settings/SettingsStore";
import ResizeHandle from '../views/elements/ResizeHandle'; import ResizeHandle from '../views/elements/ResizeHandle';
import {Resizer, CollapseDistributor} from '../../resizer'; import { Resizer, CollapseDistributor } from '../../resizer';
import MatrixClientContext from "../../contexts/MatrixClientContext"; import MatrixClientContext from "../../contexts/MatrixClientContext";
import * as KeyboardShortcuts from "../../accessibility/KeyboardShortcuts"; import * as KeyboardShortcuts from "../../accessibility/KeyboardShortcuts";
import HomePage from "./HomePage"; import HomePage from "./HomePage";
@ -55,7 +55,7 @@ import HostSignupContainer from '../views/host_signup/HostSignupContainer';
import { getKeyBindingsManager, NavigationAction, RoomAction } from '../../KeyBindingsManager'; import { getKeyBindingsManager, NavigationAction, RoomAction } from '../../KeyBindingsManager';
import { IOpts } from "../../createRoom"; import { IOpts } from "../../createRoom";
import SpacePanel from "../views/spaces/SpacePanel"; import SpacePanel from "../views/spaces/SpacePanel";
import {replaceableComponent} from "../../utils/replaceableComponent"; import { replaceableComponent } from "../../utils/replaceableComponent";
import CallHandler, { CallHandlerEvent } from '../../CallHandler'; import CallHandler, { CallHandlerEvent } from '../../CallHandler';
import { MatrixCall } from 'matrix-js-sdk/src/webrtc/call'; import { MatrixCall } from 'matrix-js-sdk/src/webrtc/call';
import AudioFeedArrayForCall from '../views/voip/AudioFeedArrayForCall'; import AudioFeedArrayForCall from '../views/voip/AudioFeedArrayForCall';
@ -232,10 +232,10 @@ class LoggedInView extends React.Component<IProps, IState> {
onCollapsed: (_collapsed) => { onCollapsed: (_collapsed) => {
collapsed = _collapsed; collapsed = _collapsed;
if (_collapsed) { if (_collapsed) {
dis.dispatch({action: "hide_left_panel"}); dis.dispatch({ action: "hide_left_panel" });
window.localStorage.setItem("mx_lhs_size", '0'); window.localStorage.setItem("mx_lhs_size", '0');
} else { } else {
dis.dispatch({action: "show_left_panel"}); dis.dispatch({ action: "show_left_panel" });
} }
}, },
onResized: (_size) => { onResized: (_size) => {
@ -272,7 +272,7 @@ class LoggedInView extends React.Component<IProps, IState> {
onAccountData = (event) => { onAccountData = (event) => {
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" });
} }
}; };
@ -319,7 +319,7 @@ class LoggedInView extends React.Component<IProps, IState> {
this.setState({ this.setState({
usageLimitDismissed: true, usageLimitDismissed: true,
}); });
} };
_calculateServerLimitToast(syncError: IState["syncErrorData"], usageLimitEventContent?: IUsageLimit) { _calculateServerLimitToast(syncError: IState["syncErrorData"], usageLimitEventContent?: IUsageLimit) {
const error = syncError && syncError.error && syncError.error.errcode === "M_RESOURCE_LIMIT_EXCEEDED"; const error = syncError && syncError.error && syncError.error.errcode === "M_RESOURCE_LIMIT_EXCEEDED";

View file

@ -17,7 +17,7 @@ limitations under the License.
import React from 'react'; import React from 'react';
import { Resizable } from 're-resizable'; import { Resizable } from 're-resizable';
import {replaceableComponent} from "../../utils/replaceableComponent"; import { replaceableComponent } from "../../utils/replaceableComponent";
@replaceableComponent("structures.MainSplit") @replaceableComponent("structures.MainSplit")
export default class MainSplit extends React.Component { export default class MainSplit extends React.Component {
@ -73,7 +73,7 @@ export default class MainSplit extends React.Component {
onResize={this._onResize} onResize={this._onResize}
onResizeStop={this._onResizeStop} onResizeStop={this._onResizeStop}
className="mx_RightPanel_ResizeWrapper" className="mx_RightPanel_ResizeWrapper"
handleClasses={{left: "mx_RightPanel_ResizeHandle"}} handleClasses={{ left: "mx_RightPanel_ResizeHandle" }}
> >
{ panelView } { panelView }
</Resizable>; </Resizable>;

View file

@ -34,7 +34,6 @@ import dis from "../../dispatcher/dispatcher";
import Notifier from '../../Notifier'; import Notifier from '../../Notifier';
import Modal from "../../Modal"; import Modal from "../../Modal";
import Tinter from "../../Tinter";
import * as sdk from '../../index'; import * as sdk from '../../index';
import { showRoomInviteDialog, showStartChatInviteDialog } from '../../RoomInvite'; import { showRoomInviteDialog, showStartChatInviteDialog } from '../../RoomInvite';
import * as Rooms from '../../Rooms'; import * as Rooms from '../../Rooms';
@ -44,8 +43,8 @@ import * as Lifecycle from '../../Lifecycle';
import '../../stores/LifecycleStore'; import '../../stores/LifecycleStore';
import PageTypes from '../../PageTypes'; import PageTypes from '../../PageTypes';
import createRoom, {IOpts} from "../../createRoom"; import createRoom, { IOpts } from "../../createRoom";
import {_t, _td, getCurrentLanguage} from '../../languageHandler'; import { _t, _td, getCurrentLanguage } from '../../languageHandler';
import SettingsStore from "../../settings/SettingsStore"; import SettingsStore from "../../settings/SettingsStore";
import ThemeController from "../../settings/controllers/ThemeController"; import ThemeController from "../../settings/controllers/ThemeController";
import { startAnyRegistrationFlow } from "../../Registration"; import { startAnyRegistrationFlow } from "../../Registration";
@ -66,7 +65,7 @@ import {
showToast as showAnalyticsToast, showToast as showAnalyticsToast,
hideToast as hideAnalyticsToast, hideToast as hideAnalyticsToast,
} from "../../toasts/AnalyticsToast"; } from "../../toasts/AnalyticsToast";
import {showToast as showNotificationsToast} from "../../toasts/DesktopNotificationsToast"; import { showToast as showNotificationsToast } from "../../toasts/DesktopNotificationsToast";
import { OpenToTabPayload } from "../../dispatcher/payloads/OpenToTabPayload"; import { OpenToTabPayload } from "../../dispatcher/payloads/OpenToTabPayload";
import ErrorDialog from "../views/dialogs/ErrorDialog"; import ErrorDialog from "../views/dialogs/ErrorDialog";
import { RoomNotificationStateStore } from "../../stores/notifications/RoomNotificationStateStore"; import { RoomNotificationStateStore } from "../../stores/notifications/RoomNotificationStateStore";
@ -74,15 +73,15 @@ import { SettingLevel } from "../../settings/SettingLevel";
import { leaveRoomBehaviour } from "../../utils/membership"; import { leaveRoomBehaviour } from "../../utils/membership";
import CreateCommunityPrototypeDialog from "../views/dialogs/CreateCommunityPrototypeDialog"; import CreateCommunityPrototypeDialog from "../views/dialogs/CreateCommunityPrototypeDialog";
import ThreepidInviteStore, { IThreepidInvite, IThreepidInviteWireFormat } from "../../stores/ThreepidInviteStore"; import ThreepidInviteStore, { IThreepidInvite, IThreepidInviteWireFormat } from "../../stores/ThreepidInviteStore";
import {UIFeature} from "../../settings/UIFeature"; import { UIFeature } from "../../settings/UIFeature";
import { CommunityPrototypeStore } from "../../stores/CommunityPrototypeStore"; import { CommunityPrototypeStore } from "../../stores/CommunityPrototypeStore";
import DialPadModal from "../views/voip/DialPadModal"; import DialPadModal from "../views/voip/DialPadModal";
import { showToast as showMobileGuideToast } from '../../toasts/MobileGuideToast'; import { showToast as showMobileGuideToast } from '../../toasts/MobileGuideToast';
import { shouldUseLoginForWelcome } from "../../utils/pages"; import { shouldUseLoginForWelcome } from "../../utils/pages";
import SpaceStore from "../../stores/SpaceStore"; import SpaceStore from "../../stores/SpaceStore";
import {replaceableComponent} from "../../utils/replaceableComponent"; import { replaceableComponent } from "../../utils/replaceableComponent";
import RoomListStore from "../../stores/room-list/RoomListStore"; import RoomListStore from "../../stores/room-list/RoomListStore";
import {RoomUpdateCause} from "../../stores/room-list/models"; import { RoomUpdateCause } from "../../stores/room-list/models";
import defaultDispatcher from "../../dispatcher/dispatcher"; import defaultDispatcher from "../../dispatcher/dispatcher";
import SecurityCustomisations from "../../customisations/Security"; import SecurityCustomisations from "../../customisations/Security";
@ -283,11 +282,6 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
this.pageChanging = false; this.pageChanging = false;
// check we have the right tint applied for this theme.
// N.B. we don't call the whole of setTheme() here as we may be
// racing with the theme CSS download finishing from index.js
Tinter.tint();
// For PersistentElement // For PersistentElement
this.state.resizeNotifier.on("middlePanelResized", this.dispatchTimelineResize); this.state.resizeNotifier.on("middlePanelResized", this.dispatchTimelineResize);
@ -401,7 +395,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
if (SecurityCustomisations.SHOW_ENCRYPTION_SETUP_UI === false) { if (SecurityCustomisations.SHOW_ENCRYPTION_SETUP_UI === false) {
this.onLoggedIn(); this.onLoggedIn();
} else { } else {
this.setStateForNewView({view: Views.COMPLETE_SECURITY}); this.setStateForNewView({ view: Views.COMPLETE_SECURITY });
} }
} else if (await cli.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing")) { } else if (await cli.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing")) {
this.setStateForNewView({ view: Views.E2E_SETUP }); this.setStateForNewView({ view: Views.E2E_SETUP });
@ -454,7 +448,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
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"];
return {serverConfig: props}; return { serverConfig: props };
} }
private loadSession() { private loadSession() {
@ -472,9 +466,9 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
if (!loadedSession) { if (!loadedSession) {
// fall back to showing the welcome screen... unless we have a 3pid invite pending // fall back to showing the welcome screen... unless we have a 3pid invite pending
if (ThreepidInviteStore.instance.pickBestInvite()) { if (ThreepidInviteStore.instance.pickBestInvite()) {
dis.dispatch({action: 'start_registration'}); dis.dispatch({ action: 'start_registration' });
} else { } else {
dis.dispatch({action: "view_welcome_page"}); dis.dispatch({ action: "view_welcome_page" });
} }
} else if (SettingsStore.getValue("analyticsOptIn")) { } else if (SettingsStore.getValue("analyticsOptIn")) {
CountlyAnalytics.instance.enable(/* anonymous = */ false); CountlyAnalytics.instance.enable(/* anonymous = */ false);
@ -538,7 +532,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
action: 'do_after_sync_prepared', action: 'do_after_sync_prepared',
deferred_action: payload, deferred_action: payload,
}); });
dis.dispatch({action: 'require_registration'}); dis.dispatch({ action: 'require_registration' });
return; return;
} }
@ -563,11 +557,11 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
} }
// redispatch the change with a more specific action // redispatch the change with a more specific action
dis.dispatch({action: 'id_server_changed'}); dis.dispatch({ action: 'id_server_changed' });
} }
break; break;
case 'logout': case 'logout':
dis.dispatch({action: "hangup_all"}); dis.dispatch({ action: "hangup_all" });
Lifecycle.logout(); Lifecycle.logout();
break; break;
case 'require_registration': case 'require_registration':
@ -624,7 +618,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
MatrixClientPeg.get().leave(payload.room_id).then(() => { MatrixClientPeg.get().leave(payload.room_id).then(() => {
modal.close(); modal.close();
if (this.state.currentRoomId === payload.room_id) { if (this.state.currentRoomId === payload.room_id) {
dis.dispatch({action: 'view_home_page'}); dis.dispatch({ action: 'view_home_page' });
} }
}, (err) => { }, (err) => {
modal.close(); modal.close();
@ -657,7 +651,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
const tabPayload = payload as OpenToTabPayload; const tabPayload = payload as OpenToTabPayload;
const UserSettingsDialog = sdk.getComponent("dialogs.UserSettingsDialog"); const UserSettingsDialog = sdk.getComponent("dialogs.UserSettingsDialog");
Modal.createTrackedDialog('User settings', '', UserSettingsDialog, Modal.createTrackedDialog('User settings', '', UserSettingsDialog,
{initialTabId: tabPayload.initialTabId}, { initialTabId: tabPayload.initialTabId },
/*className=*/null, /*isPriority=*/false, /*isStatic=*/true); /*className=*/null, /*isPriority=*/false, /*isStatic=*/true);
// View the welcome or home page if we need something to look at // View the welcome or home page if we need something to look at
@ -668,7 +662,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
this.createRoom(payload.public, payload.defaultName); this.createRoom(payload.public, payload.defaultName);
break; break;
case 'view_create_group': { case 'view_create_group': {
let CreateGroupDialog = sdk.getComponent("dialogs.CreateGroupDialog") let CreateGroupDialog = sdk.getComponent("dialogs.CreateGroupDialog");
if (SettingsStore.getValue("feature_communities_v2_prototypes")) { if (SettingsStore.getValue("feature_communities_v2_prototypes")) {
CreateGroupDialog = CreateCommunityPrototypeDialog; CreateGroupDialog = CreateCommunityPrototypeDialog;
} }
@ -727,9 +721,9 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
// We just dispatch the page change rather than have to worry about // We just dispatch the page change rather than have to worry about
// what the logic is for each of these branches. // what the logic is for each of these branches.
if (this.state.page_type === PageTypes.MyGroups) { if (this.state.page_type === PageTypes.MyGroups) {
dis.dispatch({action: 'view_last_screen'}); dis.dispatch({ action: 'view_last_screen' });
} else { } else {
dis.dispatch({action: 'view_my_groups'}); dis.dispatch({ action: 'view_my_groups' });
} }
break; break;
case 'hide_left_panel': case 'hide_left_panel':
@ -770,7 +764,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
this.onLoggedOut(); this.onLoggedOut();
break; break;
case 'will_start_client': case 'will_start_client':
this.setState({ready: false}, () => { this.setState({ ready: false }, () => {
// if the client is about to start, we are, by definition, not ready. // if the client is about to start, we are, by definition, not ready.
// Set ready to false now, then it'll be set to true when the sync // Set ready to false now, then it'll be set to true when the sync
// listener we set below fires. // listener we set below fires.
@ -1006,7 +1000,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
return; return;
} }
this.notifyNewScreen('user/' + userId); this.notifyNewScreen('user/' + userId);
this.setState({currentUserId: userId}); this.setState({ currentUserId: userId });
this.setPage(PageTypes.UserView); this.setPage(PageTypes.UserView);
}); });
} }
@ -1131,8 +1125,14 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
description: ( description: (
<span> <span>
{ isSpace { isSpace
? _t("Are you sure you want to leave the space '%(spaceName)s'?", {spaceName: roomToLeave.name}) ? _t(
: _t("Are you sure you want to leave the room '%(roomName)s'?", {roomName: roomToLeave.name}) } "Are you sure you want to leave the space '%(spaceName)s'?",
{ spaceName: roomToLeave.name },
)
: _t(
"Are you sure you want to leave the room '%(roomName)s'?",
{ roomName: roomToLeave.name },
)}
{ warnings } { warnings }
</span> </span>
), ),
@ -1170,7 +1170,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
}).catch((err) => { }).catch((err) => {
const errCode = err.errcode || _td("unknown error code"); const errCode = err.errcode || _td("unknown error code");
Modal.createTrackedDialog("Failed to forget room", '', ErrorDialog, { Modal.createTrackedDialog("Failed to forget room", '', ErrorDialog, {
title: _t("Failed to forget room %(errCode)s", {errCode}), title: _t("Failed to forget room %(errCode)s", { errCode }),
description: ((err && err.message) ? err.message : _t("Operation failed")), description: ((err && err.message) ? err.message : _t("Operation failed")),
}); });
}); });
@ -1254,7 +1254,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
if (welcomeUserRoom === null) { if (welcomeUserRoom === null) {
// We didn't redirect to the welcome user room, so show // We didn't redirect to the welcome user room, so show
// the homepage. // the homepage.
dis.dispatch({action: 'view_home_page', justRegistered: true}); dis.dispatch({ action: 'view_home_page', justRegistered: true });
} }
} else if (ThreepidInviteStore.instance.pickBestInvite()) { } else if (ThreepidInviteStore.instance.pickBestInvite()) {
// The user has a 3pid invite pending - show them that // The user has a 3pid invite pending - show them that
@ -1263,11 +1263,11 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
// HACK: This is a pretty brutal way of threading the invite back through // HACK: This is a pretty brutal way of threading the invite back through
// our systems, but it's the safest we have for now. // our systems, but it's the safest we have for now.
const params = ThreepidInviteStore.instance.translateToWireFormat(threepidInvite); const params = ThreepidInviteStore.instance.translateToWireFormat(threepidInvite);
this.showScreen(`room/${threepidInvite.roomId}`, params) this.showScreen(`room/${threepidInvite.roomId}`, params);
} else { } else {
// The user has just logged in after registering, // The user has just logged in after registering,
// so show the homepage. // so show the homepage.
dis.dispatch({action: 'view_home_page', justRegistered: true}); dis.dispatch({ action: 'view_home_page', justRegistered: true });
} }
} else { } else {
this.showScreenAfterLogin(); this.showScreenAfterLogin();
@ -1303,9 +1303,9 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
this.viewLastRoom(); this.viewLastRoom();
} else { } else {
if (MatrixClientPeg.get().isGuest()) { if (MatrixClientPeg.get().isGuest()) {
dis.dispatch({action: 'view_welcome_page'}); dis.dispatch({ action: 'view_welcome_page' });
} else { } else {
dis.dispatch({action: 'view_home_page'}); dis.dispatch({ action: 'view_home_page' });
} }
} }
} }
@ -1385,15 +1385,15 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
// So dispatch directly from here. Ideally we'd use a SyncStateStore that // So dispatch directly from here. Ideally we'd use a SyncStateStore that
// would do this dispatch and expose the sync state itself (by listening to // would do this dispatch and expose the sync state itself (by listening to
// its own dispatch). // its own dispatch).
dis.dispatch({action: 'sync_state', prevState, state}); dis.dispatch({ action: 'sync_state', prevState, state });
if (state === "ERROR" || state === "RECONNECTING") { if (state === "ERROR" || state === "RECONNECTING") {
if (data.error instanceof InvalidStoreError) { if (data.error instanceof InvalidStoreError) {
Lifecycle.handleInvalidStoreError(data.error); Lifecycle.handleInvalidStoreError(data.error);
} }
this.setState({syncError: data.error || true}); this.setState({ syncError: data.error || true });
} else if (this.state.syncError) { } else if (this.state.syncError) {
this.setState({syncError: null}); this.setState({ syncError: null });
} }
this.updateStatusIndicator(state, prevState); this.updateStatusIndicator(state, prevState);
@ -1567,16 +1567,12 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
key: 'verifreq_' + request.channel.transactionId, key: 'verifreq_' + request.channel.transactionId,
title: _t("Verification requested"), title: _t("Verification requested"),
icon: "verification", icon: "verification",
props: {request}, props: { request },
component: sdk.getComponent("toasts.VerificationRequestToast"), component: sdk.getComponent("toasts.VerificationRequestToast"),
priority: 90, priority: 90,
}); });
} }
}); });
// Fire the tinter right on startup to ensure the default theme is applied
// A later sync can/will correct the tint to be the right value for the user
const colorScheme = SettingsStore.getValue("roomColor");
Tinter.tint(colorScheme.primary_color, colorScheme.secondary_color);
} }
/** /**
@ -1668,7 +1664,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
// TODO if logged in, skip SSO // TODO if logged in, skip SSO
let cli = MatrixClientPeg.get(); let cli = MatrixClientPeg.get();
if (!cli) { if (!cli) {
const {hsUrl, isUrl} = this.props.serverConfig; const { hsUrl, isUrl } = this.props.serverConfig;
cli = createClient({ cli = createClient({
baseUrl: hsUrl, baseUrl: hsUrl,
idBaseUrl: isUrl, idBaseUrl: isUrl,
@ -1792,7 +1788,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
onAliasClick(event: MouseEvent, alias: string) { onAliasClick(event: MouseEvent, alias: string) {
event.preventDefault(); event.preventDefault();
dis.dispatch({action: 'view_room', room_alias: alias}); dis.dispatch({ action: 'view_room', room_alias: alias });
} }
onUserClick(event: MouseEvent, userId: string) { onUserClick(event: MouseEvent, userId: string) {
@ -1808,7 +1804,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
onGroupClick(event: MouseEvent, groupId: string) { onGroupClick(event: MouseEvent, groupId: string) {
event.preventDefault(); event.preventDefault();
dis.dispatch({action: 'view_group', group_id: groupId}); dis.dispatch({ action: 'view_group', group_id: groupId });
} }
onLogoutClick(event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) { onLogoutClick(event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) {
@ -1870,14 +1866,14 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
onSendEvent(roomId: string, event: MatrixEvent) { onSendEvent(roomId: string, event: MatrixEvent) {
const cli = MatrixClientPeg.get(); const cli = MatrixClientPeg.get();
if (!cli) { if (!cli) {
dis.dispatch({action: 'message_send_failed'}); dis.dispatch({ action: 'message_send_failed' });
return; return;
} }
cli.sendEvent(roomId, event.getType(), event.getContent()).then(() => { cli.sendEvent(roomId, event.getType(), event.getContent()).then(() => {
dis.dispatch({action: 'message_sent'}); dis.dispatch({ action: 'message_sent' });
}, (err) => { }, (err) => {
dis.dispatch({action: 'message_send_failed'}); dis.dispatch({ action: 'message_send_failed' });
}); });
} }
@ -1924,7 +1920,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
} }
onServerConfigChange = (serverConfig: ValidatedServerConfig) => { onServerConfigChange = (serverConfig: ValidatedServerConfig) => {
this.setState({serverConfig}); this.setState({ serverConfig });
}; };
private makeRegistrationUrl = (params: {[key: string]: string}) => { private makeRegistrationUrl = (params: {[key: string]: string}) => {

View file

@ -24,7 +24,7 @@ import dis from '../../dispatcher/dispatcher';
import AccessibleButton from '../views/elements/AccessibleButton'; import AccessibleButton from '../views/elements/AccessibleButton';
import MatrixClientContext from "../../contexts/MatrixClientContext"; import MatrixClientContext from "../../contexts/MatrixClientContext";
import AutoHideScrollbar from "./AutoHideScrollbar"; import AutoHideScrollbar from "./AutoHideScrollbar";
import {replaceableComponent} from "../../utils/replaceableComponent"; import { replaceableComponent } from "../../utils/replaceableComponent";
import BetaCard from "../views/beta/BetaCard"; import BetaCard from "../views/beta/BetaCard";
@replaceableComponent("structures.MyGroups") @replaceableComponent("structures.MyGroups")
@ -41,19 +41,19 @@ export default class MyGroups extends React.Component {
} }
_onCreateGroupClick = () => { _onCreateGroupClick = () => {
dis.dispatch({action: 'view_create_group'}); dis.dispatch({ action: 'view_create_group' });
}; };
_fetch() { _fetch() {
this.context.getJoinedGroups().then((result) => { this.context.getJoinedGroups().then((result) => {
this.setState({groups: result.groups, error: null}); this.setState({ groups: result.groups, error: null });
}, (err) => { }, (err) => {
if (err.errcode === 'M_GUEST_ACCESS_FORBIDDEN') { if (err.errcode === 'M_GUEST_ACCESS_FORBIDDEN') {
// Indicate that the guest isn't in any groups (which should be true) // Indicate that the guest isn't in any groups (which should be true)
this.setState({groups: [], error: null}); this.setState({ groups: [], error: null });
return; return;
} }
this.setState({groups: null, error: err}); this.setState({ groups: null, error: err });
}); });
} }
@ -123,7 +123,7 @@ export default class MyGroups extends React.Component {
</div> </div>
{/*<div className="mx_MyGroups_joinBox mx_MyGroups_headerCard"> {/*<div className="mx_MyGroups_joinBox mx_MyGroups_headerCard">
<AccessibleButton className='mx_MyGroups_headerCard_button' onClick={this._onJoinGroupClick}> <AccessibleButton className='mx_MyGroups_headerCard_button' onClick={this._onJoinGroupClick}>
<TintableSvg src={require("../../../res/img/icons-create-room.svg")} width="50" height="50" /> <img src={require("../../../res/img/icons-create-room.svg")} width="50" height="50" />
</AccessibleButton> </AccessibleButton>
<div className="mx_MyGroups_headerCard_content"> <div className="mx_MyGroups_headerCard_content">
<div className="mx_MyGroups_headerCard_header"> <div className="mx_MyGroups_headerCard_header">

View file

@ -18,7 +18,7 @@ import * as React from "react";
import { ComponentClass } from "../../@types/common"; import { ComponentClass } from "../../@types/common";
import NonUrgentToastStore from "../../stores/NonUrgentToastStore"; import NonUrgentToastStore from "../../stores/NonUrgentToastStore";
import { UPDATE_EVENT } from "../../stores/AsyncStore"; import { UPDATE_EVENT } from "../../stores/AsyncStore";
import {replaceableComponent} from "../../utils/replaceableComponent"; import { replaceableComponent } from "../../utils/replaceableComponent";
interface IProps { interface IProps {
} }
@ -44,7 +44,7 @@ export default class NonUrgentToastContainer extends React.PureComponent<IProps,
} }
private onUpdateToasts = () => { private onUpdateToasts = () => {
this.setState({toasts: NonUrgentToastStore.instance.components}); this.setState({ toasts: NonUrgentToastStore.instance.components });
}; };
public render() { public render() {

View file

@ -22,6 +22,7 @@ import BaseCard from "../views/right_panel/BaseCard";
import { replaceableComponent } from "../../utils/replaceableComponent"; import { replaceableComponent } from "../../utils/replaceableComponent";
import TimelinePanel from "./TimelinePanel"; import TimelinePanel from "./TimelinePanel";
import Spinner from "../views/elements/Spinner"; import Spinner from "../views/elements/Spinner";
import { TileShape } from "../views/rooms/EventTile";
interface IProps { interface IProps {
onClose(): void; onClose(): void;
@ -48,7 +49,7 @@ export default class NotificationPanel extends React.PureComponent<IProps> {
manageReadMarkers={false} manageReadMarkers={false}
timelineSet={timelineSet} timelineSet={timelineSet}
showUrlPreview={false} showUrlPreview={false}
tileShape="notif" tileShape={TileShape.Notif}
empty={emptyState} empty={emptyState}
alwaysShowTimestamps={true} alwaysShowTimestamps={true}
/> />

Some files were not shown because too many files have changed in this diff Show more