diff --git a/package.json b/package.json index 610404ba0d..bb92ad11d8 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,6 @@ }, "dependencies": { "@babel/runtime": "^7.12.5", - "@types/commonmark": "^0.27.4", "await-lock": "^2.1.0", "blurhash": "^1.1.3", "browser-encrypt-attachment": "^0.3.0", @@ -92,6 +91,7 @@ "re-resizable": "^6.9.0", "react": "^17.0.2", "react-beautiful-dnd": "^13.1.0", + "react-blurhash": "^0.1.3", "react-dom": "^17.0.2", "react-focus-lock": "^2.5.0", "react-transition-group": "^4.4.1", @@ -124,6 +124,7 @@ "@peculiar/webcrypto": "^1.1.4", "@sinonjs/fake-timers": "^7.0.2", "@types/classnames": "^2.2.11", + "@types/commonmark": "^0.27.4", "@types/counterpart": "^0.18.1", "@types/diff-match-patch": "^1.0.32", "@types/flux": "^3.1.9", diff --git a/res/css/_components.scss b/res/css/_components.scss index 389be11c60..7360c61c25 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -201,6 +201,7 @@ @import "./views/rooms/_GroupLayout.scss"; @import "./views/rooms/_IRCLayout.scss"; @import "./views/rooms/_JumpToBottomButton.scss"; +@import "./views/rooms/_LinkPreviewGroup.scss"; @import "./views/rooms/_LinkPreviewWidget.scss"; @import "./views/rooms/_MemberInfo.scss"; @import "./views/rooms/_MemberList.scss"; diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index 27a83e58f8..55f73c0315 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -477,8 +477,7 @@ $hover-select-border: 4px; pre, code { font-family: $monospace-font-family !important; - // deliberate constants as we're behind an invert filter - color: #333; + background-color: $header-panel-bg-color; } pre { @@ -488,11 +487,6 @@ $hover-select-border: 4px; overflow-x: overlay; overflow-y: visible; } - - code { - // deliberate constants as we're behind an invert filter - background-color: #f8f8f8; - } } .mx_EventTile_lineNumbers { diff --git a/res/css/views/rooms/_LinkPreviewGroup.scss b/res/css/views/rooms/_LinkPreviewGroup.scss new file mode 100644 index 0000000000..ed341904fd --- /dev/null +++ b/res/css/views/rooms/_LinkPreviewGroup.scss @@ -0,0 +1,38 @@ +/* +Copyright 2021 The Matrix.org Foundation C.I.C. + +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. +*/ + +.mx_LinkPreviewGroup { + .mx_LinkPreviewGroup_hide { + cursor: pointer; + width: 18px; + height: 18px; + + img { + flex: 0 0 40px; + visibility: hidden; + } + } + + &:hover .mx_LinkPreviewGroup_hide img, + .mx_LinkPreviewGroup_hide.focus-visible:focus img { + visibility: visible; + } + + > .mx_AccessibleButton { + color: $accent-color; + text-align: center; + } +} diff --git a/res/css/views/rooms/_LinkPreviewWidget.scss b/res/css/views/rooms/_LinkPreviewWidget.scss index 022cf3ed28..e1628e19a6 100644 --- a/res/css/views/rooms/_LinkPreviewWidget.scss +++ b/res/css/views/rooms/_LinkPreviewWidget.scss @@ -33,12 +33,16 @@ limitations under the License. .mx_LinkPreviewWidget_caption { margin-left: 15px; flex: 1 1 auto; + overflow-x: hidden; // cause it to wrap rather than clip } .mx_LinkPreviewWidget_title { - display: inline; font-weight: bold; white-space: normal; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; } .mx_LinkPreviewWidget_siteName { @@ -49,22 +53,9 @@ limitations under the License. margin-top: 8px; white-space: normal; word-wrap: break-word; -} - -.mx_LinkPreviewWidget_cancel { - cursor: pointer; - width: 18px; - height: 18px; - - img { - flex: 0 0 40px; - visibility: hidden; - } -} - -.mx_LinkPreviewWidget:hover .mx_LinkPreviewWidget_cancel img, -.mx_LinkPreviewWidget_cancel.focus-visible:focus img { - visibility: visible; + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; } .mx_MatrixChat_useCompactLayout { diff --git a/res/themes/dark/css/_dark.scss b/res/themes/dark/css/_dark.scss index 81fd3c892a..57cbc7efa9 100644 --- a/res/themes/dark/css/_dark.scss +++ b/res/themes/dark/css/_dark.scss @@ -119,8 +119,6 @@ $voipcall-plinth-color: #394049; $theme-button-bg-color: #e3e8f0; $dialpad-button-bg-color: #6F7882; -; - $roomlist-button-bg-color: rgba(141, 151, 165, 0.2); // Buttons include the filter box, explore button, and sublist buttons $roomlist-filter-active-bg-color: $bg-color; @@ -274,24 +272,7 @@ $composer-shadow-color: rgba(0, 0, 0, 0.28); } // markdown overrides: -.mx_EventTile_content .markdown-body pre:hover { - border-color: #808080 !important; // inverted due to rules below - scrollbar-color: rgba(0, 0, 0, 0.2) transparent; // copied from light theme due to inversion below - // the code above works only in Firefox, this is for other browsers - // see https://developer.mozilla.org/en-US/docs/Web/CSS/scrollbar-color - &::-webkit-scrollbar-thumb { - background-color: rgba(0, 0, 0, 0.2); // copied from light theme due to inversion below - } -} .mx_EventTile_content .markdown-body { - pre, code { - filter: invert(1); - } - - pre code { - filter: none; - } - table { tr { background-color: #000000; @@ -301,18 +282,9 @@ $composer-shadow-color: rgba(0, 0, 0, 0.28); background-color: #080808; } } - - blockquote { - color: #919191; - } } -// diff highlight colors -// intentionally swapped to avoid inversion -.hljs-addition { - background: #fdd; -} - -.hljs-deletion { - background: #dfd; +// highlight.js overrides +.hljs-tag { + color: inherit; // Without this they'd be weirdly blue which doesn't match the theme } diff --git a/res/themes/dark/css/dark.scss b/res/themes/dark/css/dark.scss index f9695018e4..600cfd528a 100644 --- a/res/themes/dark/css/dark.scss +++ b/res/themes/dark/css/dark.scss @@ -9,3 +9,4 @@ @import "_dark.scss"; @import "../../light/css/_mods.scss"; @import "../../../../res/css/_components.scss"; +@import url("highlight.js/styles/atom-one-dark.css"); diff --git a/res/themes/legacy-dark/css/_legacy-dark.scss b/res/themes/legacy-dark/css/_legacy-dark.scss index df01efbe1e..555ef4f66c 100644 --- a/res/themes/legacy-dark/css/_legacy-dark.scss +++ b/res/themes/legacy-dark/css/_legacy-dark.scss @@ -118,7 +118,7 @@ $voipcall-plinth-color: #394049; $theme-button-bg-color: #e3e8f0; $dialpad-button-bg-color: #6F7882; -; + $roomlist-button-bg-color: #1A1D23; // Buttons include the filter box, explore button, and sublist buttons $roomlist-filter-active-bg-color: $roomlist-button-bg-color; @@ -249,7 +249,7 @@ $composer-shadow-color: tranparent; @define-mixin mx_DialogButton_secondary { // flip colours for the secondary ones font-weight: 600; - border: 1px solid $accent-color ! important; + border: 1px solid $accent-color !important; color: $accent-color; background-color: $button-secondary-bg-color; } @@ -267,18 +267,7 @@ $composer-shadow-color: tranparent; } // markdown overrides: -.mx_EventTile_content .markdown-body pre:hover { - border-color: #808080 !important; // inverted due to rules below -} .mx_EventTile_content .markdown-body { - pre, code { - filter: invert(1); - } - - pre code { - filter: none; - } - table { tr { background-color: #000000; @@ -290,12 +279,7 @@ $composer-shadow-color: tranparent; } } -// diff highlight colors -// intentionally swapped to avoid inversion -.hljs-addition { - background: #fdd; -} - -.hljs-deletion { - background: #dfd; +// highlight.js overrides: +.hljs-tag { + color: inherit; // Without this they'd be weirdly blue which doesn't match the theme } diff --git a/res/themes/legacy-dark/css/legacy-dark.scss b/res/themes/legacy-dark/css/legacy-dark.scss index 2a4d432d26..840794f7c0 100644 --- a/res/themes/legacy-dark/css/legacy-dark.scss +++ b/res/themes/legacy-dark/css/legacy-dark.scss @@ -4,3 +4,4 @@ @import "../../legacy-light/css/_legacy-light.scss"; @import "_legacy-dark.scss"; @import "../../../../res/css/_components.scss"; +@import url("highlight.js/styles/atom-one-dark.css"); diff --git a/res/themes/legacy-light/css/legacy-light.scss b/res/themes/legacy-light/css/legacy-light.scss index e39a1765f3..347d240fc6 100644 --- a/res/themes/legacy-light/css/legacy-light.scss +++ b/res/themes/legacy-light/css/legacy-light.scss @@ -3,3 +3,4 @@ @import "_fonts.scss"; @import "_legacy-light.scss"; @import "../../../../res/css/_components.scss"; +@import url("highlight.js/styles/atom-one-light.css"); diff --git a/res/themes/light/css/light.scss b/res/themes/light/css/light.scss index f31ce5c139..4e912bc756 100644 --- a/res/themes/light/css/light.scss +++ b/res/themes/light/css/light.scss @@ -4,3 +4,4 @@ @import "_light.scss"; @import "_mods.scss"; @import "../../../../res/css/_components.scss"; +@import url("highlight.js/styles/atom-one-light.css"); diff --git a/scripts/ci/install-deps.sh b/scripts/ci/install-deps.sh index bbda74ef9d..fcbf6b1198 100755 --- a/scripts/ci/install-deps.sh +++ b/scripts/ci/install-deps.sh @@ -6,8 +6,8 @@ scripts/fetchdep.sh matrix-org matrix-js-sdk pushd matrix-js-sdk yarn link -yarn install $@ +yarn install --pure-lockfile $@ popd yarn link matrix-js-sdk -yarn install $@ +yarn install --pure-lockfile $@ diff --git a/scripts/ci/layered.sh b/scripts/ci/layered.sh index 039f90c7df..2e163456fe 100755 --- a/scripts/ci/layered.sh +++ b/scripts/ci/layered.sh @@ -13,13 +13,13 @@ scripts/fetchdep.sh matrix-org matrix-js-sdk pushd matrix-js-sdk yarn link -yarn install +yarn install --pure-lockfile popd # Now set up the react-sdk yarn link matrix-js-sdk yarn link -yarn install +yarn install --pure-lockfile yarn reskindex # Finally, set up element-web @@ -27,6 +27,6 @@ scripts/fetchdep.sh vector-im element-web pushd element-web yarn link matrix-js-sdk yarn link matrix-react-sdk -yarn install +yarn install --pure-lockfile yarn build:res popd diff --git a/src/Analytics.tsx b/src/Analytics.tsx index 8c82639b5f..ce8287de56 100644 --- a/src/Analytics.tsx +++ b/src/Analytics.tsx @@ -390,6 +390,7 @@ export class Analytics { { expl: _td('Your device resolution'), value: resolution }, ]; + // FIXME: Using an import will result in test failures const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog'); Modal.createTrackedDialog('Analytics Details', '', ErrorDialog, { title: _t('Analytics'), diff --git a/src/AsyncWrapper.tsx b/src/AsyncWrapper.tsx index 3bbef71093..ef8924add8 100644 --- a/src/AsyncWrapper.tsx +++ b/src/AsyncWrapper.tsx @@ -77,6 +77,7 @@ export default class AsyncWrapper extends React.Component { const Component = this.state.component; return ; } else if (this.state.error) { + // FIXME: Using an import will result in test failures const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); return diff --git a/src/ContentMessages.tsx b/src/ContentMessages.tsx index 1b8a3be37e..670c175a48 100644 --- a/src/ContentMessages.tsx +++ b/src/ContentMessages.tsx @@ -49,8 +49,6 @@ const MAX_HEIGHT = 600; const PHYS_HIDPI = [0x00, 0x00, 0x16, 0x25, 0x00, 0x00, 0x16, 0x25, 0x01]; export const BLURHASH_FIELD = "xyz.amorgan.blurhash"; // MSC2448 -const BLURHASH_X_COMPONENTS = 6; -const BLURHASH_Y_COMPONENTS = 6; export class UploadCanceledError extends Error {} @@ -137,8 +135,9 @@ function createThumbnail( imageData.data, imageData.width, imageData.height, - BLURHASH_X_COMPONENTS, - BLURHASH_Y_COMPONENTS, + // use 4 components on the longer dimension, if square then both + imageData.width >= imageData.height ? 4 : 3, + imageData.height >= imageData.width ? 4 : 3, ); canvas.toBlob(function(thumbnail) { resolve({ @@ -419,6 +418,7 @@ export default class ContentMessages { const isQuoting = Boolean(RoomViewStore.getQuotingEvent()); if (isQuoting) { + // FIXME: Using an import will result in Element crashing const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); const { finished } = Modal.createTrackedDialog<[boolean]>('Upload Reply Warning', '', QuestionDialog, { title: _t('Replying With Files'), @@ -453,6 +453,7 @@ export default class ContentMessages { } if (tooBigFiles.length > 0) { + // FIXME: Using an import will result in Element crashing const UploadFailureDialog = sdk.getComponent("dialogs.UploadFailureDialog"); const { finished } = Modal.createTrackedDialog<[boolean]>('Upload Failure', '', UploadFailureDialog, { badFiles: tooBigFiles, @@ -463,7 +464,6 @@ export default class ContentMessages { if (!shouldContinue) return; } - const UploadConfirmDialog = sdk.getComponent("dialogs.UploadConfirmDialog"); let uploadAll = false; // Promise to complete before sending next file into room, used for synchronisation of file-sending // to match the order the files were specified in @@ -471,6 +471,8 @@ export default class ContentMessages { for (let i = 0; i < okFiles.length; ++i) { const file = okFiles[i]; if (!uploadAll) { + // FIXME: Using an import will result in Element crashing + const UploadConfirmDialog = sdk.getComponent("dialogs.UploadConfirmDialog"); const { finished } = Modal.createTrackedDialog<[boolean, boolean]>('Upload Files confirmation', '', UploadConfirmDialog, { file, @@ -606,6 +608,7 @@ export default class ContentMessages { { fileName: upload.fileName }, ); } + // FIXME: Using an import will result in Element crashing const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); Modal.createTrackedDialog('Upload failed', '', ErrorDialog, { title: _t('Upload Failed'), diff --git a/src/DeviceListener.ts b/src/DeviceListener.ts index d70585e5ec..d033063677 100644 --- a/src/DeviceListener.ts +++ b/src/DeviceListener.ts @@ -160,7 +160,8 @@ export default class DeviceListener { // which result in account data changes affecting checks below. if ( ev.getType().startsWith('m.secret_storage.') || - ev.getType().startsWith('m.cross_signing.') + ev.getType().startsWith('m.cross_signing.') || + ev.getType() === 'm.megolm_backup.v1' ) { this._recheck(); } diff --git a/src/Lifecycle.ts b/src/Lifecycle.ts index 76dee5ab55..61ded93833 100644 --- a/src/Lifecycle.ts +++ b/src/Lifecycle.ts @@ -33,7 +33,6 @@ import Presence from './Presence'; import dis from './dispatcher/dispatcher'; import DMRoomMap from './utils/DMRoomMap'; import Modal from './Modal'; -import * as sdk from './index'; import ActiveWidgetStore from './stores/ActiveWidgetStore'; import PlatformPeg from "./PlatformPeg"; import { sendLoginRequest } from "./Login"; @@ -52,6 +51,10 @@ import CallHandler from './CallHandler'; import LifecycleCustomisations from "./customisations/Lifecycle"; import ErrorDialog from "./components/views/dialogs/ErrorDialog"; import { _t } from "./languageHandler"; +import LazyLoadingResyncDialog from "./components/views/dialogs/LazyLoadingResyncDialog"; +import LazyLoadingDisabledDialog from "./components/views/dialogs/LazyLoadingDisabledDialog"; +import SessionRestoreErrorDialog from "./components/views/dialogs/SessionRestoreErrorDialog"; +import StorageEvictedDialog from "./components/views/dialogs/StorageEvictedDialog"; const HOMESERVER_URL_KEY = "mx_hs_url"; const ID_SERVER_URL_KEY = "mx_is_url"; @@ -238,8 +241,6 @@ export function handleInvalidStoreError(e: InvalidStoreError): Promise { return Promise.resolve().then(() => { const lazyLoadEnabled = e.value; if (lazyLoadEnabled) { - const LazyLoadingResyncDialog = - sdk.getComponent("views.dialogs.LazyLoadingResyncDialog"); return new Promise((resolve) => { Modal.createDialog(LazyLoadingResyncDialog, { onFinished: resolve, @@ -250,8 +251,6 @@ export function handleInvalidStoreError(e: InvalidStoreError): Promise { // between LL/non-LL version on same host. // as disabling LL when previously enabled // is a strong indicator of this (/develop & /app) - const LazyLoadingDisabledDialog = - sdk.getComponent("views.dialogs.LazyLoadingDisabledDialog"); return new Promise((resolve) => { Modal.createDialog(LazyLoadingDisabledDialog, { onFinished: resolve, @@ -451,9 +450,6 @@ export async function restoreFromLocalStorage(opts?: { ignoreGuest?: boolean }): async function handleLoadSessionFailure(e: Error): Promise { console.error("Unable to load session", e); - const SessionRestoreErrorDialog = - sdk.getComponent('views.dialogs.SessionRestoreErrorDialog'); - const modal = Modal.createTrackedDialog('Session Restore Error', '', SessionRestoreErrorDialog, { error: e.message, }); @@ -612,7 +608,6 @@ async function doSetLoggedIn( } function showStorageEvictedDialog(): Promise { - const StorageEvictedDialog = sdk.getComponent('views.dialogs.StorageEvictedDialog'); return new Promise(resolve => { Modal.createTrackedDialog('Storage evicted', '', StorageEvictedDialog, { onFinished: resolve, diff --git a/src/MatrixClientPeg.ts b/src/MatrixClientPeg.ts index cb6cb5c65c..063c5f4cad 100644 --- a/src/MatrixClientPeg.ts +++ b/src/MatrixClientPeg.ts @@ -219,6 +219,7 @@ class _MatrixClientPeg implements IMatrixClientPeg { } catch (e) { if (e && e.name === 'InvalidCryptoStoreError') { // The js-sdk found a crypto DB too new for it to use + // FIXME: Using an import will result in test failures const CryptoStoreTooNewDialog = sdk.getComponent("views.dialogs.CryptoStoreTooNewDialog"); Modal.createDialog(CryptoStoreTooNewDialog); diff --git a/src/Notifier.ts b/src/Notifier.ts index 2335dc59ac..415adcafc8 100644 --- a/src/Notifier.ts +++ b/src/Notifier.ts @@ -27,7 +27,6 @@ import * as TextForEvent from './TextForEvent'; import Analytics from './Analytics'; import * as Avatar from './Avatar'; import dis from './dispatcher/dispatcher'; -import * as sdk from './index'; import { _t } from './languageHandler'; import Modal from './Modal'; import SettingsStore from "./settings/SettingsStore"; @@ -37,6 +36,7 @@ import { isPushNotifyDisabled } from "./settings/controllers/NotificationControl import RoomViewStore from "./stores/RoomViewStore"; import UserActivity from "./UserActivity"; import { mediaFromMxc } from "./customisations/Media"; +import ErrorDialog from "./components/views/dialogs/ErrorDialog"; /* * Dispatches: @@ -240,7 +240,6 @@ export const Notifier = { ? _t('%(brand)s does not have permission to send you notifications - ' + 'please check your browser settings', { brand }) : _t('%(brand)s was not given permission to send notifications - please try again', { brand }); - const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog'); Modal.createTrackedDialog('Unable to enable Notifications', result, ErrorDialog, { title: _t('Unable to enable Notifications'), description, diff --git a/src/RoomInvite.tsx b/src/RoomInvite.tsx index c7f377b6e8..7d093f4092 100644 --- a/src/RoomInvite.tsx +++ b/src/RoomInvite.tsx @@ -22,13 +22,13 @@ import { User } from "matrix-js-sdk/src/models/user"; import { MatrixClientPeg } from './MatrixClientPeg'; import MultiInviter, { CompletionStates } from './utils/MultiInviter'; import Modal from './Modal'; -import * as sdk from './'; import { _t } from './languageHandler'; import InviteDialog, { KIND_DM, KIND_INVITE, Member } from "./components/views/dialogs/InviteDialog"; import CommunityPrototypeInviteDialog from "./components/views/dialogs/CommunityPrototypeInviteDialog"; import { CommunityPrototypeStore } from "./stores/CommunityPrototypeStore"; import BaseAvatar from "./components/views/avatars/BaseAvatar"; import { mediaFromMxc } from "./customisations/Media"; +import ErrorDialog from "./components/views/dialogs/ErrorDialog"; export interface IInviteResult { states: CompletionStates; @@ -51,7 +51,6 @@ export function inviteMultipleToRoom(roomId: string, addresses: string[]): Promi export function showStartChatInviteDialog(initialText = ""): void { // This dialog handles the room creation internally - we don't need to worry about it. - const InviteDialog = sdk.getComponent("dialogs.InviteDialog"); Modal.createTrackedDialog( 'Start DM', '', InviteDialog, { kind: KIND_DM, initialText }, /*className=*/null, /*isPriority=*/false, /*isStatic=*/true, @@ -111,7 +110,6 @@ export function inviteUsersToRoom(roomId: string, userIds: string[]): Promise { console.error(err.stack); - const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); Modal.createTrackedDialog('Failed to invite', '', ErrorDialog, { title: _t("Failed to invite"), description: ((err && err.message) ? err.message : _t("Operation failed")), @@ -131,7 +129,6 @@ export function showAnyInviteErrors( // Just get the first message because there was a fatal problem on the first // user. This usually means that no other users were attempted, making it // pointless for us to list who failed exactly. - const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); Modal.createTrackedDialog('Failed to invite users to the room', '', ErrorDialog, { title: _t("Failed to invite users to the room:", { roomName: room.name }), description: inviter.getErrorText(failedUsers[0]), @@ -178,7 +175,6 @@ export function showAnyInviteErrors( ; - const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); Modal.createTrackedDialog("Some invites could not be sent", "", ErrorDialog, { title: _t("Some invites couldn't be sent"), description, diff --git a/src/SecurityManager.ts b/src/SecurityManager.ts index 214047c4fa..370b21b396 100644 --- a/src/SecurityManager.ts +++ b/src/SecurityManager.ts @@ -14,7 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { ICryptoCallbacks, ISecretStorageKeyInfo } from 'matrix-js-sdk/src/matrix'; +import { ICryptoCallbacks } from 'matrix-js-sdk/src/matrix'; +import { ISecretStorageKeyInfo } from 'matrix-js-sdk/src/crypto/api'; import { MatrixClient } from 'matrix-js-sdk/src/client'; import Modal from './Modal'; import * as sdk from './index'; @@ -354,6 +355,7 @@ export async function accessSecretStorage(func = async () => { }, forceReset = f throw new Error("Secret storage creation canceled"); } } else { + // FIXME: Using an import will result in test failures const InteractiveAuthDialog = sdk.getComponent("dialogs.InteractiveAuthDialog"); await cli.bootstrapCrossSigning({ authUploadDeviceSigningKeys: async (makeRequest) => { diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx index 128ca9e5e2..7753ff6f75 100644 --- a/src/SlashCommands.tsx +++ b/src/SlashCommands.tsx @@ -23,7 +23,6 @@ import { User } from "matrix-js-sdk/src/models/user"; import * as ContentHelpers from 'matrix-js-sdk/src/content-helpers'; import { MatrixClientPeg } from './MatrixClientPeg'; import dis from './dispatcher/dispatcher'; -import * as sdk from './index'; import { _t, _td } from './languageHandler'; import Modal from './Modal'; import MultiInviter from './utils/MultiInviter'; @@ -50,6 +49,12 @@ import { UIFeature } from "./settings/UIFeature"; import { CHAT_EFFECTS } from "./effects"; import CallHandler from "./CallHandler"; import { guessAndSetDMRoom } from "./Rooms"; +import UploadConfirmDialog from './components/views/dialogs/UploadConfirmDialog'; +import ErrorDialog from './components/views/dialogs/ErrorDialog'; +import DevtoolsDialog from './components/views/dialogs/DevtoolsDialog'; +import RoomUpgradeWarningDialog from "./components/views/dialogs/RoomUpgradeWarningDialog"; +import InfoDialog from "./components/views/dialogs/InfoDialog"; +import SlashCommandHelpDialog from "./components/views/dialogs/SlashCommandHelpDialog"; // XXX: workaround for https://github.com/microsoft/TypeScript/issues/31816 interface HTMLInputEvent extends Event { @@ -63,7 +68,6 @@ const singleMxcUpload = async (): Promise => { fileSelector.onchange = (ev: HTMLInputEvent) => { const file = ev.target.files[0]; - const UploadConfirmDialog = sdk.getComponent("dialogs.UploadConfirmDialog"); Modal.createTrackedDialog('Upload Files confirmation', '', UploadConfirmDialog, { file, onFinished: (shouldContinue) => { @@ -246,7 +250,6 @@ export const Commands = [ args: '', description: _td('Searches DuckDuckGo for results'), runFn: function() { - const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog'); // TODO Don't explain this away, actually show a search UI here. Modal.createTrackedDialog('Slash Commands', '/ddg is not a command', ErrorDialog, { title: _t('/ddg is not a command'), @@ -269,8 +272,6 @@ export const Commands = [ return reject(_t("You do not have the required permissions to use this command.")); } - const RoomUpgradeWarningDialog = sdk.getComponent("dialogs.RoomUpgradeWarningDialog"); - const { finished } = Modal.createTrackedDialog('Slash Commands', 'upgrade room confirmation', RoomUpgradeWarningDialog, { roomId: roomId, targetVersion: args }, /*className=*/null, /*isPriority=*/false, /*isStatic=*/true); @@ -314,7 +315,6 @@ export const Commands = [ if (checkForUpgradeFn) cli.removeListener('Room', checkForUpgradeFn); - const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog'); Modal.createTrackedDialog('Slash Commands', 'room upgrade error', ErrorDialog, { title: _t('Error upgrading room'), description: _t( @@ -434,7 +434,6 @@ export const Commands = [ const topic = topicEvents && topicEvents.getContent().topic; const topicHtml = topic ? linkifyAndSanitizeHtml(topic) : _t('This room has no topic.'); - const InfoDialog = sdk.getComponent('dialogs.InfoDialog'); Modal.createTrackedDialog('Slash Commands', 'Topic', InfoDialog, { title: room.name, description:
, @@ -737,7 +736,6 @@ export const Commands = [ ignoredUsers.push(userId); // de-duped internally in the js-sdk return success( cli.setIgnoredUsers(ignoredUsers).then(() => { - const InfoDialog = sdk.getComponent('dialogs.InfoDialog'); Modal.createTrackedDialog('Slash Commands', 'User ignored', InfoDialog, { title: _t('Ignored user'), description:
@@ -768,7 +766,6 @@ export const Commands = [ if (index !== -1) ignoredUsers.splice(index, 1); return success( cli.setIgnoredUsers(ignoredUsers).then(() => { - const InfoDialog = sdk.getComponent('dialogs.InfoDialog'); Modal.createTrackedDialog('Slash Commands', 'User unignored', InfoDialog, { title: _t('Unignored user'), description:
@@ -838,7 +835,6 @@ export const Commands = [ command: 'devtools', description: _td('Opens the Developer Tools dialog'), runFn: function(roomId) { - const DevtoolsDialog = sdk.getComponent('dialogs.DevtoolsDialog'); Modal.createDialog(DevtoolsDialog, { roomId }); return success(); }, @@ -943,7 +939,6 @@ export const Commands = [ await cli.setDeviceVerified(userId, deviceId, true); // Tell the user we verified everything - const InfoDialog = sdk.getComponent('dialogs.InfoDialog'); Modal.createTrackedDialog('Slash Commands', 'Verified key', InfoDialog, { title: _t('Verified key'), description:
@@ -1000,8 +995,6 @@ export const Commands = [ command: "help", description: _td("Displays list of commands with usages and descriptions"), runFn: function() { - const SlashCommandHelpDialog = sdk.getComponent('dialogs.SlashCommandHelpDialog'); - Modal.createTrackedDialog('Slash Commands', 'Help', SlashCommandHelpDialog); return success(); }, diff --git a/src/Terms.ts b/src/Terms.ts index e9ba5ff8be..351d1c0951 100644 --- a/src/Terms.ts +++ b/src/Terms.ts @@ -189,6 +189,7 @@ export function dialogTermsInteractionCallback( ): Promise { return new Promise((resolve, reject) => { console.log("Terms that need agreement", policiesAndServicePairs); + // FIXME: Using an import will result in test failures const TermsDialog = sdk.getComponent("views.dialogs.TermsDialog"); Modal.createTrackedDialog('Terms of Service', '', TermsDialog, { diff --git a/src/accessibility/KeyboardShortcuts.tsx b/src/accessibility/KeyboardShortcuts.tsx index 25c41f9db5..c5cf85facd 100644 --- a/src/accessibility/KeyboardShortcuts.tsx +++ b/src/accessibility/KeyboardShortcuts.tsx @@ -17,10 +17,10 @@ limitations under the License. import * as React from "react"; import classNames from "classnames"; -import * as sdk from "../index"; import Modal from "../Modal"; import { _t, _td } from "../languageHandler"; import { isMac, Key } from "../Keyboard"; +import InfoDialog from "../components/views/dialogs/InfoDialog"; // TS: once languageHandler is TS we can probably inline this into the enum _td("Navigation"); @@ -375,7 +375,6 @@ export const toggleDialog = () => {
; }); - const InfoDialog = sdk.getComponent('dialogs.InfoDialog'); activeModal = Modal.createTrackedDialog("Keyboard Shortcuts", "", InfoDialog, { className: "mx_KeyboardShortcutsDialog", title: _t("Keyboard Shortcuts"), diff --git a/src/actions/RoomListActions.ts b/src/actions/RoomListActions.ts index 81e05f8678..a7f629c40d 100644 --- a/src/actions/RoomListActions.ts +++ b/src/actions/RoomListActions.ts @@ -19,13 +19,13 @@ import { asyncAction } from './actionCreators'; import Modal from '../Modal'; import * as Rooms from '../Rooms'; import { _t } from '../languageHandler'; -import * as sdk from '../index'; import { MatrixClient } from "matrix-js-sdk/src/client"; import { Room } from "matrix-js-sdk/src/models/room"; import { AsyncActionPayload } from "../dispatcher/payloads"; import RoomListStore from "../stores/room-list/RoomListStore"; import { SortAlgorithm } from "../stores/room-list/algorithms/models"; import { DefaultTagID } from "../stores/room-list/models"; +import ErrorDialog from '../components/views/dialogs/ErrorDialog'; export default class RoomListActions { /** @@ -88,7 +88,6 @@ export default class RoomListActions { return Rooms.guessAndSetDMRoom( room, newTag === DefaultTagID.DM, ).catch((err) => { - const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); console.error("Failed to set direct chat tag " + err); Modal.createTrackedDialog('Failed to set direct chat tag', '', ErrorDialog, { title: _t('Failed to set direct chat tag'), @@ -109,7 +108,6 @@ export default class RoomListActions { const promiseToDelete = matrixClient.deleteRoomTag( roomId, oldTag, ).catch(function(err) { - const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); console.error("Failed to remove tag " + oldTag + " from room: " + err); Modal.createTrackedDialog('Failed to remove tag from room', '', ErrorDialog, { title: _t('Failed to remove tag %(tagName)s from room', { tagName: oldTag }), @@ -129,7 +127,6 @@ export default class RoomListActions { metaData = metaData || {}; const promiseToAdd = matrixClient.setRoomTag(roomId, newTag, metaData).catch(function(err) { - const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); console.error("Failed to add tag " + newTag + " to room: " + err); Modal.createTrackedDialog('Failed to add tag to room', '', ErrorDialog, { title: _t('Failed to add tag %(tagName)s to room', { tagName: newTag }), diff --git a/src/async-components/views/dialogs/eventindex/ManageEventIndexDialog.tsx b/src/async-components/views/dialogs/eventindex/ManageEventIndexDialog.tsx index 76c3373ba4..c5c8022346 100644 --- a/src/async-components/views/dialogs/eventindex/ManageEventIndexDialog.tsx +++ b/src/async-components/views/dialogs/eventindex/ManageEventIndexDialog.tsx @@ -15,7 +15,6 @@ limitations under the License. */ import React from 'react'; -import * as sdk from '../../../../index'; import { _t } from '../../../../languageHandler'; import SdkConfig from '../../../../SdkConfig'; import SettingsStore from "../../../../settings/SettingsStore"; @@ -24,6 +23,9 @@ import Modal from '../../../../Modal'; import { formatBytes, formatCountLong } from "../../../../utils/FormattingUtils"; import EventIndexPeg from "../../../../indexing/EventIndexPeg"; import { SettingLevel } from "../../../../settings/SettingLevel"; +import Field from '../../../../components/views/elements/Field'; +import BaseDialog from "../../../../components/views/dialogs/BaseDialog"; +import DialogButtons from "../../../../components/views/elements/DialogButtons"; interface IProps { onFinished: (confirmed: boolean) => void; @@ -145,7 +147,6 @@ export default class ManageEventIndexDialog extends React.Component
); - const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); - const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); - return ( { - const BaseAvatar = sdk.getComponent('views.avatars.BaseAvatar'); - // Disable autocompletions when composing commands because of various issues // (see https://github.com/vector-im/element-web/issues/4762) if (/^(\/join|\/leave)/.test(query)) { diff --git a/src/autocomplete/NotifProvider.tsx b/src/autocomplete/NotifProvider.tsx index 1d42915ec9..31b834ccfe 100644 --- a/src/autocomplete/NotifProvider.tsx +++ b/src/autocomplete/NotifProvider.tsx @@ -21,8 +21,8 @@ import AutocompleteProvider from './AutocompleteProvider'; import { _t } from '../languageHandler'; import { MatrixClientPeg } from '../MatrixClientPeg'; import { PillCompletion } from './Components'; -import * as sdk from '../index'; import { ICompletion, ISelectionRange } from "./Autocompleter"; +import RoomAvatar from '../components/views/avatars/RoomAvatar'; const AT_ROOM_REGEX = /@\S*/g; @@ -40,8 +40,6 @@ export default class NotifProvider extends AutocompleteProvider { force = false, limit = -1, ): Promise { - const RoomAvatar = sdk.getComponent('views.avatars.RoomAvatar'); - const client = MatrixClientPeg.get(); if (!this.room.currentState.mayTriggerNotifOfType('room', client.credentials.userId)) return []; diff --git a/src/autocomplete/UserProvider.tsx b/src/autocomplete/UserProvider.tsx index 470e018e22..d8f17c54d0 100644 --- a/src/autocomplete/UserProvider.tsx +++ b/src/autocomplete/UserProvider.tsx @@ -21,7 +21,6 @@ import React from 'react'; import { _t } from '../languageHandler'; import AutocompleteProvider from './AutocompleteProvider'; import { PillCompletion } from './Components'; -import * as sdk from '../index'; import QueryMatcher from './QueryMatcher'; import { sortBy } from 'lodash'; import { MatrixClientPeg } from '../MatrixClientPeg'; @@ -33,6 +32,7 @@ import { RoomState } from "matrix-js-sdk/src/models/room-state"; import { EventTimeline } from "matrix-js-sdk/src/models/event-timeline"; import { makeUserPermalink } from "../utils/permalinks/Permalinks"; import { ICompletion, ISelectionRange } from "./Autocompleter"; +import MemberAvatar from '../components/views/avatars/MemberAvatar'; const USER_REGEX = /\B@\S*/g; @@ -108,8 +108,6 @@ export default class UserProvider extends AutocompleteProvider { force = false, limit = -1, ): Promise { - const MemberAvatar = sdk.getComponent('views.avatars.MemberAvatar'); - // lazy-load user list into matcher if (!this.users) this._makeUsers(); diff --git a/src/components/structures/FilePanel.tsx b/src/components/structures/FilePanel.tsx index 5865201069..36f774a130 100644 --- a/src/components/structures/FilePanel.tsx +++ b/src/components/structures/FilePanel.tsx @@ -24,7 +24,6 @@ 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 { MatrixClientPeg } from '../../MatrixClientPeg'; import EventIndexPeg from "../../indexing/EventIndexPeg"; import { _t } from '../../languageHandler'; @@ -34,6 +33,9 @@ import DesktopBuildsNotice, { WarningKind } from "../views/elements/DesktopBuild import { replaceableComponent } from "../../utils/replaceableComponent"; import ResizeNotifier from '../../utils/ResizeNotifier'; +import TimelinePanel from "./TimelinePanel"; +import Spinner from "../views/elements/Spinner"; +import { TileShape } from '../views/rooms/EventTile'; interface IProps { roomId: string; @@ -237,8 +239,6 @@ class FilePanel extends React.Component { } // wrap a TimelinePanel with the jump-to-event bits turned off. - const TimelinePanel = sdk.getComponent("structures.TimelinePanel"); - const Loader = sdk.getComponent("elements.Spinner"); const emptyState = (

{_t('No files visible in this room')}

@@ -264,7 +264,7 @@ class FilePanel extends React.Component { timelineSet={this.state.timelineSet} showUrlPreview = {false} onPaginationRequest={this.onPaginationRequest} - tileShape="file_grid" + tileShape={TileShape.FileGrid} resizeNotifier={this.props.resizeNotifier} empty={emptyState} /> @@ -277,7 +277,7 @@ class FilePanel extends React.Component { onClose={this.props.onClose} previousPhase={RightPanelPhases.RoomSummary} > - + ); } diff --git a/src/components/structures/HomePage.tsx b/src/components/structures/HomePage.tsx index 046e07f455..4ed160d493 100644 --- a/src/components/structures/HomePage.tsx +++ b/src/components/structures/HomePage.tsx @@ -96,6 +96,7 @@ const HomePage: React.FC = ({ justRegistered = false }) => { const pageUrl = getHomePageUrl(config); if (pageUrl) { + // FIXME: Using an import will result in wrench-element-tests failures const EmbeddedPage = sdk.getComponent('structures.EmbeddedPage'); return ; } diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index 1f870da900..5a26967cb0 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -24,7 +24,6 @@ import { Key } from '../../Keyboard'; import PageTypes from '../../PageTypes'; import MediaDeviceHandler from '../../MediaDeviceHandler'; import { fixupColorFonts } from '../../utils/FontManager'; -import * as sdk from '../../index'; import dis from '../../dispatcher/dispatcher'; import { IMatrixClientCreds } from '../../MatrixClientPeg'; import SettingsStore from "../../settings/SettingsStore"; @@ -59,6 +58,11 @@ import { replaceableComponent } from "../../utils/replaceableComponent"; import CallHandler, { CallHandlerEvent } from '../../CallHandler'; import { MatrixCall } from 'matrix-js-sdk/src/webrtc/call'; import AudioFeedArrayForCall from '../views/voip/AudioFeedArrayForCall'; +import RoomView from './RoomView'; +import ToastContainer from './ToastContainer'; +import MyGroups from "./MyGroups"; +import UserView from "./UserView"; +import GroupView from "./GroupView"; // We need to fetch each pinned message individually (if we don't already have it) // so each pinned message may trigger a request. Limit the number per room for sanity. @@ -78,8 +82,8 @@ interface IProps { hideToSRUsers: boolean; resizeNotifier: ResizeNotifier; // eslint-disable-next-line camelcase - page_type: string; - autoJoin: boolean; + page_type?: string; + autoJoin?: boolean; threepidInvite?: IThreepidInvite; roomOobData?: IOOBData; currentRoomId: string; @@ -567,12 +571,6 @@ class LoggedInView extends React.Component { }; render() { - const RoomView = sdk.getComponent('structures.RoomView'); - const UserView = sdk.getComponent('structures.UserView'); - const GroupView = sdk.getComponent('structures.GroupView'); - const MyGroups = sdk.getComponent('structures.MyGroups'); - const ToastContainer = sdk.getComponent('structures.ToastContainer'); - let pageElement; switch (this.props.page_type) { diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index 06c7bfac8b..c7a200239c 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -36,7 +36,6 @@ import dis from "../../dispatcher/dispatcher"; import Notifier from '../../Notifier'; import Modal from "../../Modal"; -import * as sdk from '../../index'; import { showRoomInviteDialog, showStartChatInviteDialog } from '../../RoomInvite'; import * as Rooms from '../../Rooms'; import linkifyMatrix from "../../linkify-matrix"; @@ -85,9 +84,27 @@ import RoomListStore from "../../stores/room-list/RoomListStore"; import { RoomUpdateCause } from "../../stores/room-list/models"; import defaultDispatcher from "../../dispatcher/dispatcher"; import SecurityCustomisations from "../../customisations/Security"; +import Spinner from "../views/elements/Spinner"; +import QuestionDialog from "../views/dialogs/QuestionDialog"; +import UserSettingsDialog from '../views/dialogs/UserSettingsDialog'; +import CreateGroupDialog from '../views/dialogs/CreateGroupDialog'; +import CreateRoomDialog from '../views/dialogs/CreateRoomDialog'; +import RoomDirectory from './RoomDirectory'; +import KeySignatureUploadFailedDialog from "../views/dialogs/KeySignatureUploadFailedDialog"; +import IncomingSasDialog from "../views/dialogs/IncomingSasDialog"; +import CompleteSecurity from "./auth/CompleteSecurity"; +import LoggedInView from './LoggedInView'; +import Welcome from "../views/auth/Welcome"; +import ForgotPassword from "./auth/ForgotPassword"; +import E2eSetup from "./auth/E2eSetup"; +import Registration from './auth/Registration'; +import Login from "./auth/Login"; +import ErrorBoundary from '../views/elements/ErrorBoundary'; +import VerificationRequestToast from '../views/toasts/VerificationRequestToast'; import PerformanceMonitor, { PerformanceEntryNames } from "../../performance"; import UIStore, { UI_EVENTS } from "../../stores/UIStore"; +import SoftLogout from './auth/SoftLogout'; /** constants for MatrixChat.state.view */ export enum Views { @@ -156,7 +173,12 @@ interface IRoomInfo { /* eslint-enable camelcase */ interface IProps { // TODO type things better - config: Record; + config: { + piwik: { + policyUrl: string; + }; + [key: string]: any; + }; serverConfig?: ValidatedServerConfig; onNewScreen: (screen: string, replaceLast: boolean) => void; enableGuest?: boolean; @@ -519,7 +541,6 @@ export default class MatrixChat extends React.PureComponent { onAction = (payload) => { // console.log(`MatrixClientPeg.onAction: ${payload.action}`); - const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); // Start the onboarding process for certain actions if (MatrixClientPeg.get() && MatrixClientPeg.get().isGuest() && @@ -613,8 +634,7 @@ export default class MatrixChat extends React.PureComponent { onFinished: (confirm) => { if (confirm) { // FIXME: controller shouldn't be loading a view :( - const Loader = sdk.getComponent("elements.Spinner"); - const modal = Modal.createDialog(Loader, null, 'mx_Dialog_spinner'); + const modal = Modal.createDialog(Spinner, null, 'mx_Dialog_spinner'); MatrixClientPeg.get().leave(payload.room_id).then(() => { modal.close(); @@ -650,7 +670,6 @@ export default class MatrixChat extends React.PureComponent { } case Action.ViewUserSettings: { const tabPayload = payload as OpenToTabPayload; - const UserSettingsDialog = sdk.getComponent("dialogs.UserSettingsDialog"); Modal.createTrackedDialog('User settings', '', UserSettingsDialog, { initialTabId: tabPayload.initialTabId }, /*className=*/null, /*isPriority=*/false, /*isStatic=*/true); @@ -663,11 +682,12 @@ export default class MatrixChat extends React.PureComponent { this.createRoom(payload.public, payload.defaultName); break; case 'view_create_group': { - let CreateGroupDialog = sdk.getComponent("dialogs.CreateGroupDialog"); - if (SettingsStore.getValue("feature_communities_v2_prototypes")) { - CreateGroupDialog = CreateCommunityPrototypeDialog; - } - Modal.createTrackedDialog('Create Community', '', CreateGroupDialog); + const prototype = SettingsStore.getValue("feature_communities_v2_prototypes"); + Modal.createTrackedDialog( + 'Create Community', + '', + prototype ? CreateCommunityPrototypeDialog : CreateGroupDialog, + ); break; } case Action.ViewRoomDirectory: { @@ -677,7 +697,6 @@ export default class MatrixChat extends React.PureComponent { room_id: SpaceStore.instance.activeSpace.roomId, }); } else { - const RoomDirectory = sdk.getComponent("structures.RoomDirectory"); Modal.createTrackedDialog('Room directory', '', RoomDirectory, { initialText: payload.initialText, }, 'mx_RoomDirectory_dialogWrapper', false, true); @@ -1019,7 +1038,6 @@ export default class MatrixChat extends React.PureComponent { } } - const CreateRoomDialog = sdk.getComponent('dialogs.CreateRoomDialog'); const modal = Modal.createTrackedDialog('Create Room', '', CreateRoomDialog, { defaultPublic, defaultName, @@ -1116,7 +1134,6 @@ export default class MatrixChat extends React.PureComponent { } private leaveRoom(roomId: string) { - const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); const roomToLeave = MatrixClientPeg.get().getRoom(roomId); const warnings = this.leaveRoomWarnings(roomId); @@ -1143,8 +1160,7 @@ export default class MatrixChat extends React.PureComponent { const d = leaveRoomBehaviour(roomId); // FIXME: controller shouldn't be loading a view :( - const Loader = sdk.getComponent("elements.Spinner"); - const modal = Modal.createDialog(Loader, null, 'mx_Dialog_spinner'); + const modal = Modal.createDialog(Spinner, null, 'mx_Dialog_spinner'); d.finally(() => modal.close()); dis.dispatch({ @@ -1439,7 +1455,6 @@ export default class MatrixChat extends React.PureComponent { }); }); cli.on('no_consent', function(message, consentUri) { - const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); Modal.createTrackedDialog('No Consent Dialog', '', QuestionDialog, { title: _t('Terms and Conditions'), description:
@@ -1548,8 +1563,6 @@ export default class MatrixChat extends React.PureComponent { }); cli.on("crypto.keySignatureUploadFailure", (failures, source, continuation) => { - const KeySignatureUploadFailedDialog = - sdk.getComponent('views.dialogs.KeySignatureUploadFailedDialog'); Modal.createTrackedDialog( 'Failed to upload key signatures', 'Failed to upload key signatures', @@ -1559,7 +1572,6 @@ export default class MatrixChat extends React.PureComponent { cli.on("crypto.verification.request", request => { if (request.verifier) { - const IncomingSasDialog = sdk.getComponent("views.dialogs.IncomingSasDialog"); Modal.createTrackedDialog('Incoming Verification', '', IncomingSasDialog, { verifier: request.verifier, }, null, /* priority = */ false, /* static = */ true); @@ -1569,7 +1581,7 @@ export default class MatrixChat extends React.PureComponent { title: _t("Verification requested"), icon: "verification", props: { request }, - component: sdk.getComponent("toasts.VerificationRequestToast"), + component: VerificationRequestToast, priority: 90, }); } @@ -1977,21 +1989,18 @@ export default class MatrixChat extends React.PureComponent { let view = null; if (this.state.view === Views.LOADING) { - const Spinner = sdk.getComponent('elements.Spinner'); view = (
); } else if (this.state.view === Views.COMPLETE_SECURITY) { - const CompleteSecurity = sdk.getComponent('structures.auth.CompleteSecurity'); view = ( ); } else if (this.state.view === Views.E2E_SETUP) { - const E2eSetup = sdk.getComponent('structures.auth.E2eSetup'); view = ( { * we should go through and figure out what we actually need to pass down, as well * as using something like redux to avoid having a billion bits of state kicking around. */ - const LoggedInView = sdk.getComponent('structures.LoggedInView'); view = ( { ref={this.loggedInView} matrixClient={MatrixClientPeg.get()} onRoomCreated={this.onRoomCreated} - onCloseAllSettings={this.onCloseAllSettings} onRegistered={this.onRegistered} currentRoomId={this.state.currentRoomId} /> ); } else { // we think we are logged in, but are still waiting for the /sync to complete - const Spinner = sdk.getComponent('elements.Spinner'); let errorBox; if (this.state.syncError && !isStoreError) { errorBox =
@@ -2045,10 +2051,8 @@ export default class MatrixChat extends React.PureComponent { ); } } else if (this.state.view === Views.WELCOME) { - const Welcome = sdk.getComponent('auth.Welcome'); view = ; } else if (this.state.view === Views.REGISTER && SettingsStore.getValue(UIFeature.Registration)) { - const Registration = sdk.getComponent('structures.auth.Registration'); const email = ThreepidInviteStore.instance.pickBestInvite()?.toEmail; view = ( { /> ); } else if (this.state.view === Views.FORGOT_PASSWORD && SettingsStore.getValue(UIFeature.PasswordReset)) { - const ForgotPassword = sdk.getComponent('structures.auth.ForgotPassword'); view = ( { ); } else if (this.state.view === Views.LOGIN) { const showPasswordReset = SettingsStore.getValue(UIFeature.PasswordReset); - const Login = sdk.getComponent('structures.auth.Login'); view = ( { /> ); } else if (this.state.view === Views.SOFT_LOGOUT) { - const SoftLogout = sdk.getComponent('structures.auth.SoftLogout'); view = ( { console.error(`Unknown view ${this.state.view}`); } - const ErrorBoundary = sdk.getComponent('elements.ErrorBoundary'); return {view} ; diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 0985719302..0e77c301fd 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -34,7 +34,6 @@ import { RoomPermalinkCreator } from '../../utils/permalinks/Permalinks'; import ResizeNotifier from '../../utils/ResizeNotifier'; import ContentMessages from '../../ContentMessages'; import Modal from '../../Modal'; -import * as sdk from '../../index'; import CallHandler, { PlaceCallType } from '../../CallHandler'; import dis from '../../dispatcher/dispatcher'; import * as Rooms from '../../Rooms'; @@ -82,6 +81,14 @@ import { replaceableComponent } from "../../utils/replaceableComponent"; import UIStore from "../../stores/UIStore"; import EditorStateTransfer from "../../utils/EditorStateTransfer"; import { throttle } from "lodash"; +import ErrorDialog from '../views/dialogs/ErrorDialog'; +import SearchResultTile from '../views/rooms/SearchResultTile'; +import Spinner from "../views/elements/Spinner"; +import UploadBar from './UploadBar'; +import RoomStatusBar from "./RoomStatusBar"; +import MessageComposer from '../views/rooms/MessageComposer'; +import JumpToBottomButton from "../views/rooms/JumpToBottomButton"; +import TopUnreadMessagesBar from "../views/rooms/TopUnreadMessagesBar"; const DEBUG = false; let debuglog = function(msg: string) {}; @@ -1328,7 +1335,6 @@ export default class RoomView extends React.Component { searchResults: results, }); }, (error) => { - const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); console.error("Search failed", error); Modal.createTrackedDialog('Search failed', '', ErrorDialog, { title: _t("Search failed"), @@ -1344,9 +1350,6 @@ export default class RoomView extends React.Component { } private getSearchResultTiles() { - const SearchResultTile = sdk.getComponent('rooms.SearchResultTile'); - const Spinner = sdk.getComponent("elements.Spinner"); - // XXX: todo: merge overlapping results somehow? // XXX: why doesn't searching on name work? @@ -1466,7 +1469,6 @@ export default class RoomView extends React.Component { console.error("Failed to reject invite: %s", error); const msg = error.message ? error.message : JSON.stringify(error); - const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); Modal.createTrackedDialog('Failed to reject invite', '', ErrorDialog, { title: _t("Failed to reject invite"), description: msg, @@ -1500,7 +1502,6 @@ export default class RoomView extends React.Component { console.error("Failed to reject invite: %s", error); const msg = error.message ? error.message : JSON.stringify(error); - const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); Modal.createTrackedDialog('Failed to reject invite', '', ErrorDialog, { title: _t("Failed to reject invite"), description: msg, @@ -1834,10 +1835,8 @@ export default class RoomView extends React.Component { let isStatusAreaExpanded = true; if (ContentMessages.sharedInstance().getCurrentUploads().length > 0) { - const UploadBar = sdk.getComponent('structures.UploadBar'); statusBar = ; } else if (!this.state.searchResults) { - const RoomStatusBar = sdk.getComponent('structures.RoomStatusBar'); isStatusAreaExpanded = this.state.statusBarVisible; statusBar = { myMembership === 'join' && !this.state.searchResults ); if (canSpeak) { - const MessageComposer = sdk.getComponent('rooms.MessageComposer'); messageComposer = { let topUnreadMessagesBar = null; // Do not show TopUnreadMessagesBar if we have search results showing, it makes no sense if (this.state.showTopUnreadMessagesBar && !this.state.searchResults) { - const TopUnreadMessagesBar = sdk.getComponent('rooms.TopUnreadMessagesBar'); topUnreadMessagesBar = ( ); @@ -2042,7 +2037,6 @@ export default class RoomView extends React.Component { let jumpToBottom; // Do not show JumpToBottomButton if we have search results showing, it makes no sense if (!this.state.atEndOfLiveTimeline && !this.state.searchResults) { - const JumpToBottomButton = sdk.getComponent('rooms.JumpToBottomButton'); jumpToBottom = ( 0} numUnreadMessages={this.state.numUnreadMessages} diff --git a/src/components/structures/TabbedView.tsx b/src/components/structures/TabbedView.tsx index 3d77eaeac1..dcfde94811 100644 --- a/src/components/structures/TabbedView.tsx +++ b/src/components/structures/TabbedView.tsx @@ -18,9 +18,9 @@ limitations under the License. import * as React from "react"; import { _t } from '../../languageHandler'; -import * as sdk from "../../index"; import AutoHideScrollbar from './AutoHideScrollbar'; import { replaceableComponent } from "../../utils/replaceableComponent"; +import AccessibleButton from "../views/elements/AccessibleButton"; /** * Represents a tab for the TabbedView. @@ -82,8 +82,6 @@ export default class TabbedView extends React.Component { } private _renderTabLabel(tab: Tab) { - const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); - let classes = "mx_TabbedView_tabLabel "; const idx = this.props.tabs.indexOf(tab); diff --git a/src/components/structures/TimelinePanel.tsx b/src/components/structures/TimelinePanel.tsx index 0b6d33b409..85a048e9b8 100644 --- a/src/components/structures/TimelinePanel.tsx +++ b/src/components/structures/TimelinePanel.tsx @@ -32,7 +32,6 @@ import RoomContext from "../../contexts/RoomContext"; import UserActivity from "../../UserActivity"; import Modal from "../../Modal"; import dis from "../../dispatcher/dispatcher"; -import * as sdk from "../../index"; import { Key } from '../../Keyboard'; import Timer from '../../utils/Timer'; import shouldHideEvent from '../../shouldHideEvent'; @@ -47,6 +46,7 @@ import ResizeNotifier from "../../utils/ResizeNotifier"; import { RoomPermalinkCreator } from "../../utils/permalinks/Permalinks"; import Spinner from "../views/elements/Spinner"; import EditorStateTransfer from '../../utils/EditorStateTransfer'; +import ErrorDialog from '../views/dialogs/ErrorDialog'; const PAGINATE_SIZE = 20; const INITIAL_SIZE = 20; @@ -1096,7 +1096,6 @@ class TimelinePanel extends React.Component { console.error( `Error loading timeline panel at ${eventId}: ${error}`, ); - const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); let onFinished; diff --git a/src/components/structures/auth/ForgotPassword.tsx b/src/components/structures/auth/ForgotPassword.tsx index 432f69fd1b..6382e143f9 100644 --- a/src/components/structures/auth/ForgotPassword.tsx +++ b/src/components/structures/auth/ForgotPassword.tsx @@ -45,7 +45,7 @@ enum Phase { interface IProps { serverConfig: ValidatedServerConfig; - onServerConfigChange: () => void; + onServerConfigChange: (serverConfig: ValidatedServerConfig) => void; onLoginClick?: () => void; onComplete: () => void; } diff --git a/src/components/structures/auth/Login.tsx b/src/components/structures/auth/Login.tsx index 61d3759dee..9f12521a34 100644 --- a/src/components/structures/auth/Login.tsx +++ b/src/components/structures/auth/Login.tsx @@ -18,7 +18,6 @@ import React, { ReactNode } from 'react'; import { MatrixError } from "matrix-js-sdk/src/http-api"; import { _t, _td } from '../../../languageHandler'; -import * as sdk from '../../../index'; import Login, { ISSOFlow, LoginFlow } from '../../../Login'; import SdkConfig from '../../../SdkConfig'; import { messageForResourceLimitError } from '../../../utils/ErrorUtils'; @@ -36,6 +35,8 @@ import Spinner from "../../views/elements/Spinner"; import SSOButtons from "../../views/elements/SSOButtons"; import ServerPicker from "../../views/elements/ServerPicker"; import { replaceableComponent } from "../../../utils/replaceableComponent"; +import AuthBody from "../../views/auth/AuthBody"; +import AuthHeader from "../../views/auth/AuthHeader"; // These are used in several places, and come from the js-sdk's autodiscovery // stuff. We define them here so that they'll be picked up by i18n. @@ -541,8 +542,6 @@ export default class LoginComponent extends React.PureComponent }; render() { - const AuthHeader = sdk.getComponent("auth.AuthHeader"); - const AuthBody = sdk.getComponent("auth.AuthBody"); const loader = this.isBusy() && !this.state.busyLoggingIn ?
: null; diff --git a/src/components/structures/auth/Registration.tsx b/src/components/structures/auth/Registration.tsx index 57b758091a..8d32981e57 100644 --- a/src/components/structures/auth/Registration.tsx +++ b/src/components/structures/auth/Registration.tsx @@ -18,19 +18,24 @@ import { createClient } from 'matrix-js-sdk/src/matrix'; import React, { ReactNode } from 'react'; import { MatrixClient } from "matrix-js-sdk/src/client"; -import * as sdk from '../../../index'; import { _t, _td } from '../../../languageHandler'; import { messageForResourceLimitError } from '../../../utils/ErrorUtils'; import AutoDiscoveryUtils, { ValidatedServerConfig } from "../../../utils/AutoDiscoveryUtils"; import classNames from "classnames"; import * as Lifecycle from '../../../Lifecycle'; -import { MatrixClientPeg } from "../../../MatrixClientPeg"; +import { IMatrixClientCreds, MatrixClientPeg } from "../../../MatrixClientPeg"; import AuthPage from "../../views/auth/AuthPage"; import Login, { ISSOFlow } from "../../../Login"; import dis from "../../../dispatcher/dispatcher"; import SSOButtons from "../../views/elements/SSOButtons"; import ServerPicker from '../../views/elements/ServerPicker'; import { replaceableComponent } from "../../../utils/replaceableComponent"; +import RegistrationForm from '../../views/auth/RegistrationForm'; +import AccessibleButton from '../../views/elements/AccessibleButton'; +import AuthBody from "../../views/auth/AuthBody"; +import AuthHeader from "../../views/auth/AuthHeader"; +import InteractiveAuth from "../InteractiveAuth"; +import Spinner from "../../views/elements/Spinner"; interface IProps { serverConfig: ValidatedServerConfig; @@ -47,13 +52,7 @@ interface IProps { // - The user's password, if available and applicable (may be cached in memory // for a short time so the user is not required to re-enter their password // for operations like uploading cross-signing keys). - onLoggedIn(params: { - userId: string; - deviceId: string; - homeserverUrl: string; - identityServerUrl?: string; - accessToken: string; - }, password: string): void; + onLoggedIn(params: IMatrixClientCreds, password: string): void; makeRegistrationUrl(params: { /* eslint-disable camelcase */ client_secret: string; @@ -246,7 +245,7 @@ export default class Registration extends React.Component { } } - private onFormSubmit = formVals => { + private onFormSubmit = async (formVals): Promise => { this.setState({ errorText: "", busy: true, @@ -442,10 +441,6 @@ export default class Registration extends React.Component { }; private renderRegisterComponent() { - const InteractiveAuth = sdk.getComponent('structures.InteractiveAuth'); - const Spinner = sdk.getComponent('elements.Spinner'); - const RegistrationForm = sdk.getComponent('auth.RegistrationForm'); - if (this.state.matrixClient && this.state.doingUIAuth) { return { } render() { - const AuthHeader = sdk.getComponent('auth.AuthHeader'); - const AuthBody = sdk.getComponent("auth.AuthBody"); - const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); - let errorText; const err = this.state.errorText; if (err) { diff --git a/src/components/structures/auth/SetupEncryptionBody.tsx b/src/components/structures/auth/SetupEncryptionBody.tsx index 13790c2e47..c7ce74077b 100644 --- a/src/components/structures/auth/SetupEncryptionBody.tsx +++ b/src/components/structures/auth/SetupEncryptionBody.tsx @@ -21,7 +21,7 @@ import Modal from '../../../Modal'; import VerificationRequestDialog from '../../views/dialogs/VerificationRequestDialog'; import { SetupEncryptionStore, Phase } from '../../../stores/SetupEncryptionStore'; import { replaceableComponent } from "../../../utils/replaceableComponent"; -import { ISecretStorageKeyInfo } from 'matrix-js-sdk'; +import { ISecretStorageKeyInfo } from 'matrix-js-sdk/src/crypto/api'; import EncryptionPanel from "../../views/right_panel/EncryptionPanel"; import AccessibleButton from '../../views/elements/AccessibleButton'; import Spinner from '../../views/elements/Spinner'; diff --git a/src/components/structures/auth/SoftLogout.tsx b/src/components/structures/auth/SoftLogout.tsx index 3790028fea..d232f55dd1 100644 --- a/src/components/structures/auth/SoftLogout.tsx +++ b/src/components/structures/auth/SoftLogout.tsx @@ -16,7 +16,6 @@ limitations under the License. import React from 'react'; import { _t } from '../../../languageHandler'; -import * as sdk from '../../../index'; import dis from '../../../dispatcher/dispatcher'; import * as Lifecycle from '../../../Lifecycle'; import Modal from '../../../Modal'; @@ -26,6 +25,12 @@ import AuthPage from "../../views/auth/AuthPage"; import { SSO_HOMESERVER_URL_KEY, SSO_ID_SERVER_URL_KEY } from "../../../BasePlatform"; import SSOButtons from "../../views/elements/SSOButtons"; import { replaceableComponent } from "../../../utils/replaceableComponent"; +import ConfirmWipeDeviceDialog from '../../views/dialogs/ConfirmWipeDeviceDialog'; +import Field from '../../views/elements/Field'; +import AccessibleButton from '../../views/elements/AccessibleButton'; +import Spinner from "../../views/elements/Spinner"; +import AuthHeader from "../../views/auth/AuthHeader"; +import AuthBody from "../../views/auth/AuthBody"; const LOGIN_VIEW = { LOADING: 1, @@ -94,7 +99,6 @@ export default class SoftLogout extends React.Component { } onClearAll = () => { - const ConfirmWipeDeviceDialog = sdk.getComponent('dialogs.ConfirmWipeDeviceDialog'); Modal.createTrackedDialog('Clear Data', 'Soft Logout', ConfirmWipeDeviceDialog, { onFinished: (wipeData) => { if (!wipeData) return; @@ -202,7 +206,6 @@ export default class SoftLogout extends React.Component { private renderSignInSection() { if (this.state.loginView === LOGIN_VIEW.LOADING) { - const Spinner = sdk.getComponent("elements.Spinner"); return ; } @@ -214,9 +217,6 @@ export default class SoftLogout extends React.Component { } if (this.state.loginView === LOGIN_VIEW.PASSWORD) { - const Field = sdk.getComponent("elements.Field"); - const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); - let error = null; if (this.state.errorText) { error = {this.state.errorText}; @@ -286,10 +286,6 @@ export default class SoftLogout extends React.Component { } render() { - const AuthHeader = sdk.getComponent("auth.AuthHeader"); - const AuthBody = sdk.getComponent("auth.AuthBody"); - const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); - return ( diff --git a/src/components/views/auth/InteractiveAuthEntryComponents.tsx b/src/components/views/auth/InteractiveAuthEntryComponents.tsx index e002eb5717..4b1ecec740 100644 --- a/src/components/views/auth/InteractiveAuthEntryComponents.tsx +++ b/src/components/views/auth/InteractiveAuthEntryComponents.tsx @@ -18,7 +18,6 @@ import React, { ChangeEvent, createRef, FormEvent, MouseEvent } from 'react'; import classNames from 'classnames'; import { MatrixClient } from "matrix-js-sdk/src/client"; -import * as sdk from '../../../index'; import { _t } from '../../../languageHandler'; import SettingsStore from "../../../settings/SettingsStore"; import AccessibleButton from "../elements/AccessibleButton"; @@ -26,6 +25,8 @@ import Spinner from "../elements/Spinner"; import CountlyAnalytics from "../../../CountlyAnalytics"; import { replaceableComponent } from "../../../utils/replaceableComponent"; import { LocalisedPolicy, Policies } from '../../../Terms'; +import Field from '../elements/Field'; +import CaptchaForm from "./CaptchaForm"; /* This file contains a collection of components which are used by the * InteractiveAuth to prompt the user to enter the information needed @@ -164,8 +165,7 @@ export class PasswordAuthEntry extends React.Component; + submitButtonOrSpinner = ; } else { submitButtonOrSpinner = (

{ _t("Confirm your identity by entering your account password below.") }

@@ -236,13 +234,11 @@ export class RecaptchaAuthEntry extends React.Component; + return ; } let errorText = this.props.errorText; - const CaptchaForm = sdk.getComponent("views.auth.CaptchaForm"); let sitePublicKey; if (!this.props.stageParams || !this.props.stageParams.public_key) { errorText = _t( @@ -390,8 +386,7 @@ export class TermsAuthEntry extends React.Component; + return ; } const checkboxes = []; @@ -590,8 +585,7 @@ export class MsisdnAuthEntry extends React.Component; + return ; } else { const enableSubmit = Boolean(this.state.token); const submitClasses = classNames({ diff --git a/src/components/views/auth/RegistrationForm.tsx b/src/components/views/auth/RegistrationForm.tsx index be6e29a493..25ea347d24 100644 --- a/src/components/views/auth/RegistrationForm.tsx +++ b/src/components/views/auth/RegistrationForm.tsx @@ -17,7 +17,6 @@ limitations under the License. import React from 'react'; -import * as sdk from '../../../index'; import * as Email from '../../../email'; import { looksValid as phoneNumberLooksValid } from '../../../phonenumber'; import Modal from '../../../Modal'; @@ -31,6 +30,7 @@ import CountlyAnalytics from "../../../CountlyAnalytics"; import Field from '../elements/Field'; import RegistrationEmailPromptDialog from '../dialogs/RegistrationEmailPromptDialog'; import { replaceableComponent } from "../../../utils/replaceableComponent"; +import CountryDropdown from "./CountryDropdown"; enum RegistrationField { Email = "field_email", @@ -471,7 +471,6 @@ export default class RegistrationForm extends React.PureComponent -Copyright 2015, 2016, 2018, 2019, 2021 The Matrix.org Foundation C.I.C. +Copyright 2015 - 2021 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,12 +16,11 @@ limitations under the License. */ import React from 'react'; -import PropTypes from 'prop-types'; -import { EventStatus } from 'matrix-js-sdk/src/models/event'; +import { EventStatus, MatrixEvent } from 'matrix-js-sdk/src/models/event'; +import { EventType, RelationType } from "matrix-js-sdk/src/@types/event"; import { MatrixClientPeg } from '../../../MatrixClientPeg'; import dis from '../../../dispatcher/dispatcher'; -import * as sdk from '../../../index'; import { _t } from '../../../languageHandler'; import Modal from '../../../Modal'; import Resend from '../../../Resend'; @@ -29,53 +28,65 @@ import SettingsStore from '../../../settings/SettingsStore'; import { isUrlPermitted } from '../../../HtmlUtils'; import { isContentActionable } from '../../../utils/EventUtils'; import IconizedContextMenu, { IconizedContextMenuOption, IconizedContextMenuOptionList } from './IconizedContextMenu'; -import { EventType } from "matrix-js-sdk/src/@types/event"; import { replaceableComponent } from "../../../utils/replaceableComponent"; import { ReadPinsEventId } from "../right_panel/PinnedMessagesCard"; import ForwardDialog from "../dialogs/ForwardDialog"; import { Action } from "../../../dispatcher/actions"; +import ReportEventDialog from '../dialogs/ReportEventDialog'; +import ViewSource from '../../structures/ViewSource'; +import ConfirmRedactDialog from '../dialogs/ConfirmRedactDialog'; +import ErrorDialog from '../dialogs/ErrorDialog'; +import ShareDialog from '../dialogs/ShareDialog'; +import { RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks"; -export function canCancel(eventStatus) { +export function canCancel(eventStatus: EventStatus): boolean { return eventStatus === EventStatus.QUEUED || eventStatus === EventStatus.NOT_SENT; } +interface IEventTileOps { + isWidgetHidden(): boolean; + unhideWidget(): void; +} + +interface IProps { + /* the MatrixEvent associated with the context menu */ + mxEvent: MatrixEvent; + /* an optional EventTileOps implementation that can be used to unhide preview widgets */ + eventTileOps?: IEventTileOps; + permalinkCreator?: RoomPermalinkCreator; + /* an optional function to be called when the user clicks collapse thread, if not provided hide button */ + collapseReplyThread?(): void; + /* callback called when the menu is dismissed */ + onFinished(): void; + /* if the menu is inside a dialog, we sometimes need to close that dialog after click (forwarding) */ + onCloseDialog?(): void; +} + +interface IState { + canRedact: boolean; + canPin: boolean; +} + @replaceableComponent("views.context_menus.MessageContextMenu") -export default class MessageContextMenu extends React.Component { - static propTypes = { - /* the MatrixEvent associated with the context menu */ - mxEvent: PropTypes.object.isRequired, - - /* an optional EventTileOps implementation that can be used to unhide preview widgets */ - eventTileOps: PropTypes.object, - - /* an optional function to be called when the user clicks collapse thread, if not provided hide button */ - collapseReplyThread: PropTypes.func, - - /* callback called when the menu is dismissed */ - onFinished: PropTypes.func, - - /* if the menu is inside a dialog, we sometimes need to close that dialog after click (forwarding) */ - onCloseDialog: PropTypes.func, - }; - +export default class MessageContextMenu extends React.Component { state = { canRedact: false, canPin: false, }; componentDidMount() { - MatrixClientPeg.get().on('RoomMember.powerLevel', this._checkPermissions); - this._checkPermissions(); + MatrixClientPeg.get().on('RoomMember.powerLevel', this.checkPermissions); + this.checkPermissions(); } componentWillUnmount() { const cli = MatrixClientPeg.get(); if (cli) { - cli.removeListener('RoomMember.powerLevel', this._checkPermissions); + cli.removeListener('RoomMember.powerLevel', this.checkPermissions); } } - _checkPermissions = () => { + private checkPermissions = (): void => { const cli = MatrixClientPeg.get(); const room = cli.getRoom(this.props.mxEvent.getRoomId()); @@ -93,7 +104,7 @@ export default class MessageContextMenu extends React.Component { this.setState({ canRedact, canPin }); }; - _isPinned() { + private isPinned(): boolean { const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId()); const pinnedEvent = room.currentState.getStateEvents(EventType.RoomPinnedEvents, ''); if (!pinnedEvent) return false; @@ -101,38 +112,35 @@ export default class MessageContextMenu extends React.Component { return content.pinned && Array.isArray(content.pinned) && content.pinned.includes(this.props.mxEvent.getId()); } - onResendReactionsClick = () => { - for (const reaction of this._getUnsentReactions()) { + private onResendReactionsClick = (): void => { + for (const reaction of this.getUnsentReactions()) { Resend.resend(reaction); } this.closeMenu(); }; - onReportEventClick = () => { - const ReportEventDialog = sdk.getComponent("dialogs.ReportEventDialog"); + private onReportEventClick = (): void => { Modal.createTrackedDialog('Report Event', '', ReportEventDialog, { mxEvent: this.props.mxEvent, }, 'mx_Dialog_reportEvent'); this.closeMenu(); }; - onViewSourceClick = () => { - const ViewSource = sdk.getComponent('structures.ViewSource'); + private onViewSourceClick = (): void => { Modal.createTrackedDialog('View Event Source', '', ViewSource, { mxEvent: this.props.mxEvent, }, 'mx_Dialog_viewsource'); this.closeMenu(); }; - onRedactClick = () => { - const ConfirmRedactDialog = sdk.getComponent("dialogs.ConfirmRedactDialog"); + private onRedactClick = (): void => { Modal.createTrackedDialog('Confirm Redact Dialog', '', ConfirmRedactDialog, { - onFinished: async (proceed, reason) => { + onFinished: async (proceed: boolean, reason?: string) => { if (!proceed) return; const cli = MatrixClientPeg.get(); try { - if (this.props.onCloseDialog) this.props.onCloseDialog(); + this.props.onCloseDialog?.(); await cli.redactEvent( this.props.mxEvent.getRoomId(), this.props.mxEvent.getId(), @@ -145,7 +153,6 @@ export default class MessageContextMenu extends React.Component { // (e.g. no errcode or statusCode) as in that case the redactions end up in the // detached queue and we show the room status bar to allow retry if (typeof code !== "undefined") { - const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); // display error message stating you couldn't delete this. Modal.createTrackedDialog('You cannot delete this message', '', ErrorDialog, { title: _t('Error'), @@ -158,7 +165,7 @@ export default class MessageContextMenu extends React.Component { this.closeMenu(); }; - onForwardClick = () => { + private onForwardClick = (): void => { Modal.createTrackedDialog('Forward Message', '', ForwardDialog, { matrixClient: MatrixClientPeg.get(), event: this.props.mxEvent, @@ -167,12 +174,12 @@ export default class MessageContextMenu extends React.Component { this.closeMenu(); }; - onPinClick = () => { + private onPinClick = (): void => { const cli = MatrixClientPeg.get(); const room = cli.getRoom(this.props.mxEvent.getRoomId()); const eventId = this.props.mxEvent.getId(); - const pinnedIds = room?.currentState?.getStateEvents(EventType.RoomPinnedEvents, "")?.pinned || []; + const pinnedIds = room?.currentState?.getStateEvents(EventType.RoomPinnedEvents, "")?.getContent().pinned || []; if (pinnedIds.includes(eventId)) { pinnedIds.splice(pinnedIds.indexOf(eventId), 1); } else { @@ -188,18 +195,16 @@ export default class MessageContextMenu extends React.Component { this.closeMenu(); }; - closeMenu = () => { - if (this.props.onFinished) this.props.onFinished(); + private closeMenu = (): void => { + this.props.onFinished(); }; - onUnhidePreviewClick = () => { - if (this.props.eventTileOps) { - this.props.eventTileOps.unhideWidget(); - } + private onUnhidePreviewClick = (): void => { + this.props.eventTileOps?.unhideWidget(); this.closeMenu(); }; - onQuoteClick = () => { + private onQuoteClick = (): void => { dis.dispatch({ action: Action.ComposerInsert, event: this.props.mxEvent, @@ -207,9 +212,8 @@ export default class MessageContextMenu extends React.Component { this.closeMenu(); }; - onPermalinkClick = (e) => { + private onPermalinkClick = (e: React.MouseEvent): void => { e.preventDefault(); - const ShareDialog = sdk.getComponent("dialogs.ShareDialog"); Modal.createTrackedDialog('share room message dialog', '', ShareDialog, { target: this.props.mxEvent, permalinkCreator: this.props.permalinkCreator, @@ -217,30 +221,27 @@ export default class MessageContextMenu extends React.Component { this.closeMenu(); }; - onCollapseReplyThreadClick = () => { + private onCollapseReplyThreadClick = (): void => { this.props.collapseReplyThread(); this.closeMenu(); }; - _getReactions(filter) { + private getReactions(filter: (e: MatrixEvent) => boolean): MatrixEvent[] { const cli = MatrixClientPeg.get(); const room = cli.getRoom(this.props.mxEvent.getRoomId()); const eventId = this.props.mxEvent.getId(); return room.getPendingEvents().filter(e => { const relation = e.getRelation(); - return relation && - relation.rel_type === "m.annotation" && - relation.event_id === eventId && - filter(e); + return relation?.rel_type === RelationType.Annotation && relation.event_id === eventId && filter(e); }); } - _getPendingReactions() { - return this._getReactions(e => canCancel(e.status)); + private getPendingReactions(): MatrixEvent[] { + return this.getReactions(e => canCancel(e.status)); } - _getUnsentReactions() { - return this._getReactions(e => e.status === EventStatus.NOT_SENT); + private getUnsentReactions(): MatrixEvent[] { + return this.getReactions(e => e.status === EventStatus.NOT_SENT); } render() { @@ -248,16 +249,17 @@ export default class MessageContextMenu extends React.Component { const me = cli.getUserId(); const mxEvent = this.props.mxEvent; const eventStatus = mxEvent.status; - const unsentReactionsCount = this._getUnsentReactions().length; - let resendReactionsButton; - let redactButton; - let forwardButton; - let pinButton; - let unhidePreviewButton; - let externalURLButton; - let quoteButton; - let collapseReplyThread; - let redactItemList; + const unsentReactionsCount = this.getUnsentReactions().length; + + let resendReactionsButton: JSX.Element; + let redactButton: JSX.Element; + let forwardButton: JSX.Element; + let pinButton: JSX.Element; + let unhidePreviewButton: JSX.Element; + let externalURLButton: JSX.Element; + let quoteButton: JSX.Element; + let collapseReplyThread: JSX.Element; + let redactItemList: JSX.Element; // status is SENT before remote-echo, null after const isSent = !eventStatus || eventStatus === EventStatus.SENT; @@ -296,7 +298,7 @@ export default class MessageContextMenu extends React.Component { pinButton = ( ); @@ -327,16 +329,20 @@ export default class MessageContextMenu extends React.Component { if (this.props.permalinkCreator) { permalink = this.props.permalinkCreator.forEvent(this.props.mxEvent.getId()); } - // XXX: if we use room ID, we should also include a server where the event can be found (other than in the domain of the event ID) const permalinkButton = ( ); @@ -351,8 +357,8 @@ export default class MessageContextMenu extends React.Component { } // Bridges can provide a 'external_url' to link back to the source. - if (typeof (mxEvent.event.content.external_url) === "string" && - isUrlPermitted(mxEvent.event.content.external_url) + if (typeof (mxEvent.getContent().external_url) === "string" && + isUrlPermitted(mxEvent.getContent().external_url) ) { externalURLButton = ( ); } @@ -377,7 +388,7 @@ export default class MessageContextMenu extends React.Component { ); } - let reportEventButton; + let reportEventButton: JSX.Element; if (mxEvent.getSender() !== me) { reportEventButton = ( { }; public render() { - const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); - const errorList = this.props.unknownProfileUsers .map(address =>
  • {address.userId}: {address.errorText}
  • ); diff --git a/src/components/views/dialogs/BugReportDialog.tsx b/src/components/views/dialogs/BugReportDialog.tsx index eeb3769bf9..6baf24f797 100644 --- a/src/components/views/dialogs/BugReportDialog.tsx +++ b/src/components/views/dialogs/BugReportDialog.tsx @@ -18,13 +18,17 @@ limitations under the License. */ import React from 'react'; -import * as sdk from '../../../index'; import SdkConfig from '../../../SdkConfig'; import Modal from '../../../Modal'; import { _t } from '../../../languageHandler'; import sendBugReport, { downloadBugReport } from '../../../rageshake/submit-rageshake'; import AccessibleButton from "../elements/AccessibleButton"; import { replaceableComponent } from "../../../utils/replaceableComponent"; +import QuestionDialog from "./QuestionDialog"; +import BaseDialog from "./BaseDialog"; +import Field from '../elements/Field'; +import Spinner from "../elements/Spinner"; +import DialogButtons from "../elements/DialogButtons"; interface IProps { onFinished: (success: boolean) => void; @@ -93,7 +97,6 @@ export default class BugReportDialog extends React.Component { }).then(() => { if (!this.unmounted) { this.props.onFinished(false); - const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); // N.B. first param is passed to piwik and so doesn't want i18n Modal.createTrackedDialog('Bug report sent', '', QuestionDialog, { title: _t('Logs sent'), @@ -160,11 +163,6 @@ export default class BugReportDialog extends React.Component { }; public render() { - const Loader = sdk.getComponent("elements.Spinner"); - const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); - const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); - const Field = sdk.getComponent('elements.Field'); - let error = null; if (this.state.err) { error =
    @@ -176,7 +174,7 @@ export default class BugReportDialog extends React.Component { if (this.state.busy) { progress = (
    - + {this.state.progress} ...
    ); diff --git a/src/components/views/dialogs/ChangelogDialog.tsx b/src/components/views/dialogs/ChangelogDialog.tsx index 8acacd8e73..d484d94249 100644 --- a/src/components/views/dialogs/ChangelogDialog.tsx +++ b/src/components/views/dialogs/ChangelogDialog.tsx @@ -16,9 +16,10 @@ Copyright 2019 Michael Telatynski <7t3chguy@gmail.com> */ import React from 'react'; -import * as sdk from '../../../index'; import request from 'browser-request'; import { _t } from '../../../languageHandler'; +import QuestionDialog from "./QuestionDialog"; +import Spinner from "../elements/Spinner"; interface IProps { newVersion: string; @@ -65,9 +66,6 @@ export default class ChangelogDialog extends React.Component { } public render() { - const Spinner = sdk.getComponent('views.elements.Spinner'); - const QuestionDialog = sdk.getComponent('dialogs.QuestionDialog'); - const logs = REPOS.map(repo => { let content; if (this.state[repo] == null) { diff --git a/src/components/views/dialogs/ConfirmAndWaitRedactDialog.tsx b/src/components/views/dialogs/ConfirmAndWaitRedactDialog.tsx index 90b749b959..d21fde329c 100644 --- a/src/components/views/dialogs/ConfirmAndWaitRedactDialog.tsx +++ b/src/components/views/dialogs/ConfirmAndWaitRedactDialog.tsx @@ -15,9 +15,12 @@ limitations under the License. */ import React from 'react'; -import * as sdk from '../../../index'; import { _t } from '../../../languageHandler'; import { replaceableComponent } from "../../../utils/replaceableComponent"; +import ConfirmRedactDialog from './ConfirmRedactDialog'; +import ErrorDialog from './ErrorDialog'; +import BaseDialog from "./BaseDialog"; +import Spinner from "../elements/Spinner"; interface IProps { redact: () => Promise; @@ -73,7 +76,6 @@ export default class ConfirmAndWaitRedactDialog extends React.PureComponent ); } else { - const BaseDialog = sdk.getComponent("dialogs.BaseDialog"); - const Spinner = sdk.getComponent('elements.Spinner'); return ( ; } } diff --git a/src/components/views/dialogs/ConfirmRedactDialog.tsx b/src/components/views/dialogs/ConfirmRedactDialog.tsx index 94f29a71fc..a2f2b10144 100644 --- a/src/components/views/dialogs/ConfirmRedactDialog.tsx +++ b/src/components/views/dialogs/ConfirmRedactDialog.tsx @@ -15,9 +15,9 @@ limitations under the License. */ import React from 'react'; -import * as sdk from '../../../index'; import { _t } from '../../../languageHandler'; import { replaceableComponent } from "../../../utils/replaceableComponent"; +import TextInputDialog from "./TextInputDialog"; interface IProps { onFinished: (success: boolean) => void; @@ -29,7 +29,6 @@ interface IProps { @replaceableComponent("views.dialogs.ConfirmRedactDialog") export default class ConfirmRedactDialog extends React.Component { render() { - const TextInputDialog = sdk.getComponent('views.dialogs.TextInputDialog'); return ( { }; public render() { - const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); - const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); - const MemberAvatar = sdk.getComponent("views.avatars.MemberAvatar"); - const BaseAvatar = sdk.getComponent("views.avatars.BaseAvatar"); - const confirmButtonClass = this.props.danger ? 'danger' : ''; let reasonBox; diff --git a/src/components/views/dialogs/ConfirmWipeDeviceDialog.tsx b/src/components/views/dialogs/ConfirmWipeDeviceDialog.tsx index 2978179817..544d0df1c9 100644 --- a/src/components/views/dialogs/ConfirmWipeDeviceDialog.tsx +++ b/src/components/views/dialogs/ConfirmWipeDeviceDialog.tsx @@ -16,8 +16,9 @@ limitations under the License. import React from 'react'; import { _t } from "../../../languageHandler"; -import * as sdk from "../../../index"; import { replaceableComponent } from "../../../utils/replaceableComponent"; +import BaseDialog from "./BaseDialog"; +import DialogButtons from "../elements/DialogButtons"; interface IProps { onFinished: (success: boolean) => void; @@ -34,9 +35,6 @@ export default class ConfirmWipeDeviceDialog extends React.Component { }; render() { - const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); - const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); - return ( void; @@ -106,9 +107,6 @@ export default class CreateGroupDialog extends React.Component { }; render() { - const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); - const Spinner = sdk.getComponent('elements.Spinner'); - if (this.state.creating) { return ; } diff --git a/src/components/views/dialogs/CryptoStoreTooNewDialog.tsx b/src/components/views/dialogs/CryptoStoreTooNewDialog.tsx index 2cdaf9cf4f..134c4ab79e 100644 --- a/src/components/views/dialogs/CryptoStoreTooNewDialog.tsx +++ b/src/components/views/dialogs/CryptoStoreTooNewDialog.tsx @@ -16,21 +16,22 @@ limitations under the License. */ import React from 'react'; -import * as sdk from '../../../index'; import dis from '../../../dispatcher/dispatcher'; import { _t } from '../../../languageHandler'; import SdkConfig from '../../../SdkConfig'; import Modal from '../../../Modal'; +import BaseDialog from "./BaseDialog"; +import DialogButtons from "../elements/DialogButtons"; +import QuestionDialog from "./QuestionDialog"; interface IProps { onFinished: (success: boolean) => void; } -export default (props: IProps) => { +const CryptoStoreTooNewDialog: React.FC = (props: IProps) => { const brand = SdkConfig.get().brand; const _onLogoutClicked = () => { - const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); Modal.createTrackedDialog('Logout e2e db too new', '', QuestionDialog, { title: _t("Sign out"), description: _t( @@ -58,8 +59,6 @@ export default (props: IProps) => { { brand }, ); - const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); - const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); return ( { ); }; + +export default CryptoStoreTooNewDialog; diff --git a/src/components/views/dialogs/DeactivateAccountDialog.tsx b/src/components/views/dialogs/DeactivateAccountDialog.tsx index 6df6056670..b2ac849314 100644 --- a/src/components/views/dialogs/DeactivateAccountDialog.tsx +++ b/src/components/views/dialogs/DeactivateAccountDialog.tsx @@ -17,7 +17,6 @@ limitations under the License. import React from 'react'; -import * as sdk from '../../../index'; import Analytics from '../../../Analytics'; import { MatrixClientPeg } from '../../../MatrixClientPeg'; import * as Lifecycle from '../../../Lifecycle'; @@ -26,6 +25,7 @@ import InteractiveAuth, { ERROR_USER_CANCELLED } from "../../structures/Interact import { DEFAULT_PHASE, PasswordAuthEntry, SSOAuthEntry } from "../auth/InteractiveAuthEntryComponents"; import StyledCheckbox from "../elements/StyledCheckbox"; import { replaceableComponent } from "../../../utils/replaceableComponent"; +import BaseDialog from "./BaseDialog"; interface IProps { onFinished: (success: boolean) => void; @@ -165,8 +165,6 @@ export default class DeactivateAccountDialog extends React.Component diff --git a/src/components/views/dialogs/DevtoolsDialog.tsx b/src/components/views/dialogs/DevtoolsDialog.tsx index de958f8e9a..86b8f93d7b 100644 --- a/src/components/views/dialogs/DevtoolsDialog.tsx +++ b/src/components/views/dialogs/DevtoolsDialog.tsx @@ -16,7 +16,6 @@ limitations under the License. */ import React, { useState, useEffect, ChangeEvent, MouseEvent } from 'react'; -import * as sdk from '../../../index'; import SyntaxHighlight from '../elements/SyntaxHighlight'; import { _t } from '../../../languageHandler'; import Field from "../elements/Field"; @@ -42,6 +41,8 @@ import { replaceableComponent } from "../../../utils/replaceableComponent"; import { Room } from "matrix-js-sdk/src/models/room"; import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import { SettingLevel } from '../../../settings/SettingLevel'; +import BaseDialog from "./BaseDialog"; +import TruncatedList from "../elements/TruncatedList"; interface IGenericEditorProps { onBack: () => void; @@ -369,7 +370,6 @@ class FilteredList extends React.PureComponent ; } - const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); return ( { body } diff --git a/src/components/views/dialogs/ErrorDialog.tsx b/src/components/views/dialogs/ErrorDialog.tsx index 0f675f0df7..56cd76237f 100644 --- a/src/components/views/dialogs/ErrorDialog.tsx +++ b/src/components/views/dialogs/ErrorDialog.tsx @@ -26,9 +26,9 @@ limitations under the License. */ import React from 'react'; -import * as sdk from '../../../index'; import { _t } from '../../../languageHandler'; import { replaceableComponent } from "../../../utils/replaceableComponent"; +import BaseDialog from "./BaseDialog"; interface IProps { onFinished: (success: boolean) => void; @@ -57,7 +57,6 @@ export default class ErrorDialog extends React.Component { }; public render() { - const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); return ( 0) { - const QuestionDialog = sdk.getComponent('dialogs.QuestionDialog'); Modal.createTrackedDialog('Invite Paste Fail', '', QuestionDialog, { title: _t('Failed to find the following users'), description: _t( @@ -1158,7 +1159,6 @@ export default class InviteDialog extends React.PureComponent; diff --git a/src/components/views/dialogs/ReportEventDialog.tsx b/src/components/views/dialogs/ReportEventDialog.tsx index 81a7d569fe..494fd59082 100644 --- a/src/components/views/dialogs/ReportEventDialog.tsx +++ b/src/components/views/dialogs/ReportEventDialog.tsx @@ -15,7 +15,6 @@ limitations under the License. */ import React from 'react'; -import * as sdk from '../../../index'; import { _t } from '../../../languageHandler'; import { ensureDMExists } from "../../../createRoom"; import { IDialogProps } from "./IDialogProps"; @@ -26,6 +25,10 @@ import Markdown from '../../../Markdown'; import { replaceableComponent } from "../../../utils/replaceableComponent"; import SettingsStore from "../../../settings/SettingsStore"; import StyledRadioButton from "../elements/StyledRadioButton"; +import BaseDialog from "./BaseDialog"; +import DialogButtons from "../elements/DialogButtons"; +import Field from "../elements/Field"; +import Spinner from "../elements/Spinner"; interface IProps extends IDialogProps { mxEvent: MatrixEvent; @@ -239,11 +242,6 @@ export default class ReportEventDialog extends React.Component { }; render() { - const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); - const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); - const Loader = sdk.getComponent('elements.Spinner'); - const Field = sdk.getComponent('elements.Field'); - let error = null; if (this.state.err) { error =
    @@ -255,7 +253,7 @@ export default class ReportEventDialog extends React.Component { if (this.state.busy) { progress = (
    - +
    ); } diff --git a/src/components/views/dialogs/RoomSettingsDialog.tsx b/src/components/views/dialogs/RoomSettingsDialog.tsx index 005222a94e..a426dce5c7 100644 --- a/src/components/views/dialogs/RoomSettingsDialog.tsx +++ b/src/components/views/dialogs/RoomSettingsDialog.tsx @@ -24,12 +24,12 @@ import GeneralRoomSettingsTab from "../settings/tabs/room/GeneralRoomSettingsTab import SecurityRoomSettingsTab from "../settings/tabs/room/SecurityRoomSettingsTab"; import NotificationSettingsTab from "../settings/tabs/room/NotificationSettingsTab"; import BridgeSettingsTab from "../settings/tabs/room/BridgeSettingsTab"; -import * as sdk from "../../../index"; import { MatrixClientPeg } from "../../../MatrixClientPeg"; import dis from "../../../dispatcher/dispatcher"; import SettingsStore from "../../../settings/SettingsStore"; import { UIFeature } from "../../../settings/UIFeature"; import { replaceableComponent } from "../../../utils/replaceableComponent"; +import BaseDialog from "./BaseDialog"; export const ROOM_GENERAL_TAB = "ROOM_GENERAL_TAB"; export const ROOM_SECURITY_TAB = "ROOM_SECURITY_TAB"; @@ -119,8 +119,6 @@ export default class RoomSettingsDialog extends React.Component { } render() { - const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); - const roomName = MatrixClientPeg.get().getRoom(this.props.roomId).name; return ( { const successful = await copyPlaintext(this.getUrl()); const buttonRect = target.getBoundingClientRect(); - const GenericTextContextMenu = sdk.getComponent('context_menus.GenericTextContextMenu'); const { close } = ContextMenu.createMenu(GenericTextContextMenu, { ...toRightOf(buttonRect, 2), message: successful ? _t('Copied!') : _t('Failed to copy'), @@ -230,7 +230,6 @@ export default class ShareDialog extends React.PureComponent { ; } - const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); return void; @@ -117,9 +118,6 @@ export default class TermsDialog extends React.PureComponent { }; render() { - const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); - const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); - let title; if (this.props.totalFiles > 1 && this.props.currentIndex !== undefined) { title = _t( diff --git a/src/components/views/dialogs/UserSettingsDialog.tsx b/src/components/views/dialogs/UserSettingsDialog.tsx index 2878a07d35..e85938afe0 100644 --- a/src/components/views/dialogs/UserSettingsDialog.tsx +++ b/src/components/views/dialogs/UserSettingsDialog.tsx @@ -28,11 +28,11 @@ import PreferencesUserSettingsTab from "../settings/tabs/user/PreferencesUserSet import VoiceUserSettingsTab from "../settings/tabs/user/VoiceUserSettingsTab"; import HelpUserSettingsTab from "../settings/tabs/user/HelpUserSettingsTab"; import FlairUserSettingsTab from "../settings/tabs/user/FlairUserSettingsTab"; -import * as sdk from "../../../index"; import SdkConfig from "../../../SdkConfig"; import MjolnirUserSettingsTab from "../settings/tabs/user/MjolnirUserSettingsTab"; import { UIFeature } from "../../../settings/UIFeature"; import { replaceableComponent } from "../../../utils/replaceableComponent"; +import BaseDialog from "./BaseDialog"; export enum UserTab { General = "USER_GENERAL_TAB", @@ -162,8 +162,6 @@ export default class UserSettingsDialog extends React.Component } render() { - const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); - return ( void; @@ -34,9 +35,6 @@ export default class ConfirmDestroyCrossSigningDialog extends React.Component | React.KeyboardEvent { inputRef?: React.Ref; - element?: string; + element?: keyof ReactHTML; // The kind of button, similar to how Bootstrap works. // See available classes for AccessibleButton for options. kind?: string; @@ -122,7 +122,7 @@ export default function AccessibleButton({ } AccessibleButton.defaultProps = { - element: 'div', + element: 'div' as keyof ReactHTML, role: 'button', tabIndex: 0, }; diff --git a/src/components/views/elements/BlurhashPlaceholder.tsx b/src/components/views/elements/BlurhashPlaceholder.tsx deleted file mode 100644 index 0e59253fe8..0000000000 --- a/src/components/views/elements/BlurhashPlaceholder.tsx +++ /dev/null @@ -1,56 +0,0 @@ -/* - Copyright 2020 The Matrix.org Foundation C.I.C. - - 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. - */ - -import React from 'react'; -import { decode } from "blurhash"; - -interface IProps { - blurhash: string; - width: number; - height: number; -} - -export default class BlurhashPlaceholder extends React.PureComponent { - private canvas: React.RefObject = React.createRef(); - - public componentDidMount() { - this.draw(); - } - - public componentDidUpdate() { - this.draw(); - } - - private draw() { - if (!this.canvas.current) return; - - try { - const { width, height } = this.props; - - const pixels = decode(this.props.blurhash, Math.ceil(width), Math.ceil(height)); - const ctx = this.canvas.current.getContext("2d"); - const imgData = ctx.createImageData(width, height); - imgData.data.set(pixels); - ctx.putImageData(imgData, 0, 0); - } catch (e) { - console.error("Error rendering blurhash: ", e); - } - } - - public render() { - return ; - } -} diff --git a/src/components/views/elements/Field.tsx b/src/components/views/elements/Field.tsx index 297044e422..60f029c32e 100644 --- a/src/components/views/elements/Field.tsx +++ b/src/components/views/elements/Field.tsx @@ -260,6 +260,7 @@ export default class Field extends React.PureComponent { }); // Handle displaying feedback on validity + // FIXME: Using an import will result in test failures const Tooltip = sdk.getComponent("elements.Tooltip"); let fieldTooltip; if (tooltipContent || this.state.feedback) { diff --git a/src/components/views/elements/ImageView.tsx b/src/components/views/elements/ImageView.tsx index 0de34251d8..90f5d18be7 100644 --- a/src/components/views/elements/ImageView.tsx +++ b/src/components/views/elements/ImageView.tsx @@ -24,7 +24,7 @@ import FocusLock from "react-focus-lock"; import MemberAvatar from "../avatars/MemberAvatar"; import { ContextMenuTooltipButton } from "../../../accessibility/context_menu/ContextMenuTooltipButton"; import MessageContextMenu from "../context_menus/MessageContextMenu"; -import { aboveLeftOf, ContextMenu } from '../../structures/ContextMenu'; +import { aboveLeftOf } from '../../structures/ContextMenu'; import MessageTimestamp from "../messages/MessageTimestamp"; import SettingsStore from "../../../settings/SettingsStore"; import { formatFullDate } from "../../../DateUtils"; @@ -304,17 +304,13 @@ export default class ImageView extends React.Component { let contextMenu = null; if (this.state.contextMenuDisplayed) { contextMenu = ( - - - + onCloseDialog={this.props.onFinished} + /> ); } diff --git a/src/components/views/elements/RoomAliasField.tsx b/src/components/views/elements/RoomAliasField.tsx index d9e081341b..62de4dd2bb 100644 --- a/src/components/views/elements/RoomAliasField.tsx +++ b/src/components/views/elements/RoomAliasField.tsx @@ -27,6 +27,7 @@ interface IProps { value: string; label?: string; placeholder?: string; + disabled?: boolean; onChange?(value: string): void; } @@ -68,6 +69,7 @@ export default class RoomAliasField extends React.PureComponent onChange={this.onChange} value={this.props.value.substring(1, this.props.value.length - this.props.domain.length - 1)} maxLength={maxlength} + disabled={this.props.disabled} /> ); } diff --git a/src/components/views/elements/SpellCheckLanguagesDropdown.tsx b/src/components/views/elements/SpellCheckLanguagesDropdown.tsx index 1678bdb33a..5230042c38 100644 --- a/src/components/views/elements/SpellCheckLanguagesDropdown.tsx +++ b/src/components/views/elements/SpellCheckLanguagesDropdown.tsx @@ -17,11 +17,11 @@ limitations under the License. import React from 'react'; import Dropdown from "../../views/elements/Dropdown"; -import * as sdk from '../../../index'; import PlatformPeg from "../../../PlatformPeg"; import SettingsStore from "../../../settings/SettingsStore"; import { _t } from "../../../languageHandler"; import { replaceableComponent } from "../../../utils/replaceableComponent"; +import Spinner from "./Spinner"; function languageMatchesSearchQuery(query, language) { if (language.label.toUpperCase().includes(query.toUpperCase())) return true; @@ -84,7 +84,6 @@ export default class SpellCheckLanguagesDropdown extends React.Component; } diff --git a/src/components/views/elements/ToggleSwitch.tsx b/src/components/views/elements/ToggleSwitch.tsx index 7315cc6383..c439ef4f85 100644 --- a/src/components/views/elements/ToggleSwitch.tsx +++ b/src/components/views/elements/ToggleSwitch.tsx @@ -17,7 +17,7 @@ limitations under the License. import React from "react"; import classNames from "classnames"; -import * as sdk from "../../../index"; +import AccessibleButton from "./AccessibleButton"; interface IProps { // Whether or not this toggle is in the 'on' position. @@ -43,7 +43,6 @@ export default ({ checked, disabled = false, onChange, ...props }: IProps) => { "mx_ToggleSwitch_enabled": !disabled, }); - const AccessibleButton = sdk.getComponent("elements.AccessibleButton"); return ( { }; render() { - const Tooltip = sdk.getComponent("elements.Tooltip"); const tip = this.state.hover ? ; + selectedEmojis?: Set; showQuickReactions?: boolean; onChoose(unicode: string): boolean; } diff --git a/src/components/views/messages/MImageBody.js b/src/components/views/messages/MImageBody.js index 5566f5aec0..6da4aa1494 100644 --- a/src/components/views/messages/MImageBody.js +++ b/src/components/views/messages/MImageBody.js @@ -18,6 +18,7 @@ limitations under the License. import React, { createRef } from 'react'; import PropTypes from 'prop-types'; +import { Blurhash } from "react-blurhash"; import MFileBody from './MFileBody'; import Modal from '../../../Modal'; @@ -29,7 +30,6 @@ import MatrixClientContext from "../../../contexts/MatrixClientContext"; import InlineSpinner from '../elements/InlineSpinner'; import { replaceableComponent } from "../../../utils/replaceableComponent"; import { mediaFromContent } from "../../../customisations/Media"; -import BlurhashPlaceholder from "../elements/BlurhashPlaceholder"; import { BLURHASH_FIELD } from "../../../ContentMessages"; @replaceableComponent("views.messages.MImageBody") @@ -436,7 +436,7 @@ export default class MImageBody extends React.Component { // Overidden by MStickerBody getPlaceholder(width, height) { const blurhash = this.props.mxEvent.getContent().info[BLURHASH_FIELD]; - if (blurhash) return ; + if (blurhash) return ; return
    ; diff --git a/src/components/views/messages/MKeyVerificationRequest.tsx b/src/components/views/messages/MKeyVerificationRequest.tsx index fc550845e2..c57cb5932d 100644 --- a/src/components/views/messages/MKeyVerificationRequest.tsx +++ b/src/components/views/messages/MKeyVerificationRequest.tsx @@ -17,7 +17,6 @@ limitations under the License. import React from 'react'; import { MatrixEvent } from 'matrix-js-sdk/src'; import { MatrixClientPeg } from '../../../MatrixClientPeg'; -import * as sdk from '../../../index'; import { _t } from '../../../languageHandler'; import { getNameForEventRoom, userLabelForEventRoom } from '../../../utils/KeyVerificationStateObserver'; @@ -26,6 +25,7 @@ import { RightPanelPhases } from "../../../stores/RightPanelStorePhases"; import { Action } from "../../../dispatcher/actions"; import EventTileBubble from "./EventTileBubble"; import { replaceableComponent } from "../../../utils/replaceableComponent"; +import AccessibleButton from '../elements/AccessibleButton'; interface IProps { mxEvent: MatrixEvent; @@ -115,8 +115,6 @@ export default class MKeyVerificationRequest extends React.Component { } public render() { - const AccessibleButton = sdk.getComponent("elements.AccessibleButton"); - const { mxEvent } = this.props; const request = mxEvent.verificationRequest; diff --git a/src/components/views/messages/SenderProfile.tsx b/src/components/views/messages/SenderProfile.tsx index 11c3ca4e3c..bdae9cec4a 100644 --- a/src/components/views/messages/SenderProfile.tsx +++ b/src/components/views/messages/SenderProfile.tsx @@ -24,7 +24,7 @@ import { MatrixEvent } from "matrix-js-sdk/src/models/event"; interface IProps { mxEvent: MatrixEvent; - onClick(): void; + onClick?(): void; enableFlair: boolean; } diff --git a/src/components/views/messages/TextualBody.tsx b/src/components/views/messages/TextualBody.tsx index 6ba018c512..9c2786c642 100644 --- a/src/components/views/messages/TextualBody.tsx +++ b/src/components/views/messages/TextualBody.tsx @@ -45,7 +45,7 @@ import Spoiler from "../elements/Spoiler"; import QuestionDialog from "../dialogs/QuestionDialog"; import MessageEditHistoryDialog from "../dialogs/MessageEditHistoryDialog"; import EditMessageComposer from '../rooms/EditMessageComposer'; -import LinkPreviewWidget from '../rooms/LinkPreviewWidget'; +import LinkPreviewGroup from '../rooms/LinkPreviewGroup'; interface IProps { /* the MatrixEvent to show */ @@ -294,14 +294,8 @@ export default class TextualBody extends React.Component { // pass only the first child which is the event tile otherwise this recurses on edited events let links = this.findLinks([this.contentRef.current]); if (links.length) { - // de-duplicate the links after stripping hashes as they don't affect the preview - // using a set here maintains the order - links = Array.from(new Set(links.map(link => { - const url = new URL(link); - url.hash = ""; - return url.toString(); - }))); - + // de-duplicate the links using a set here maintains the order + links = Array.from(new Set(links)); this.setState({ links }); // lazy-load the hidden state of the preview widget from localstorage @@ -530,15 +524,12 @@ export default class TextualBody extends React.Component { let widgets; if (this.state.links.length && !this.state.widgetHidden && this.props.showUrlPreview) { - widgets = this.state.links.map((link)=>{ - return ; - }); + widgets = ; } switch (content.msgtype) { diff --git a/src/components/views/right_panel/EncryptionInfo.tsx b/src/components/views/right_panel/EncryptionInfo.tsx index c34cf18710..e74caf8457 100644 --- a/src/components/views/right_panel/EncryptionInfo.tsx +++ b/src/components/views/right_panel/EncryptionInfo.tsx @@ -16,13 +16,13 @@ limitations under the License. import React from "react"; -import * as sdk from "../../../index"; import { _t } from "../../../languageHandler"; import { RoomMember } from "matrix-js-sdk/src/models/room-member"; import { User } from "matrix-js-sdk/src/models/user"; +import AccessibleButton from "../elements/AccessibleButton"; +import Spinner from "../elements/Spinner"; export const PendingActionSpinner = ({ text }) => { - const Spinner = sdk.getComponent('elements.Spinner'); return
    { text } @@ -64,7 +64,6 @@ const EncryptionInfo: React.FC = ({ } content = ; } else { - const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); content = ( {_t("Start Verification")} diff --git a/src/components/views/right_panel/EncryptionPanel.tsx b/src/components/views/right_panel/EncryptionPanel.tsx index 251c04d3cc..9ed791c229 100644 --- a/src/components/views/right_panel/EncryptionPanel.tsx +++ b/src/components/views/right_panel/EncryptionPanel.tsx @@ -81,6 +81,7 @@ const EncryptionPanel: React.FC = (props: IProps) => { const changeHandler = useCallback(() => { // handle transitions -> cancelled for mismatches which fire a modal instead of showing a card if (request && request.cancelled && MISMATCHES.includes(request.cancellationCode)) { + // FIXME: Using an import will result in test failures const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); Modal.createTrackedDialog("Verification failed", "insecure", ErrorDialog, { headerImage: require("../../../../res/img/e2e/warning.svg"), diff --git a/src/components/views/right_panel/VerificationPanel.tsx b/src/components/views/right_panel/VerificationPanel.tsx index 6087923057..a4d4d2fa30 100644 --- a/src/components/views/right_panel/VerificationPanel.tsx +++ b/src/components/views/right_panel/VerificationPanel.tsx @@ -17,7 +17,6 @@ limitations under the License. import React from "react"; import { MatrixClientPeg } from "../../../MatrixClientPeg"; -import * as sdk from '../../../index'; import { verificationMethods } from 'matrix-js-sdk/src/crypto'; import { SCAN_QR_CODE_METHOD } from "matrix-js-sdk/src/crypto/verification/QRCode"; import { VerificationRequest } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; @@ -38,6 +37,8 @@ import { } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; import Spinner from "../elements/Spinner"; import { replaceableComponent } from "../../../utils/replaceableComponent"; +import AccessibleButton from "../elements/AccessibleButton"; +import VerificationShowSas from "../verification/VerificationShowSas"; // XXX: Should be defined in matrix-js-sdk enum VerificationPhase { @@ -81,7 +82,6 @@ export default class VerificationPanel extends React.PureComponent

    {_t("Verified")}

    @@ -282,8 +280,6 @@ export default class VerificationPanel extends React.PureComponent } public insertMention(userId: string): void { + this.modifiedFlag = true; const { model } = this.props; const { partCreator } = model; const member = this.props.room.getMember(userId); @@ -729,6 +730,7 @@ export default class BasicMessageEditor extends React.Component } public insertQuotedMessage(event: MatrixEvent): void { + this.modifiedFlag = true; const { model } = this.props; const { partCreator } = model; const quoteParts = parseEvent(event, partCreator, { isQuotedMessage: true }); @@ -744,6 +746,7 @@ export default class BasicMessageEditor extends React.Component } public insertPlaintext(text: string): void { + this.modifiedFlag = true; const { model } = this.props; const { partCreator } = model; const caret = this.getCaret(); diff --git a/src/components/views/rooms/EditMessageComposer.tsx b/src/components/views/rooms/EditMessageComposer.tsx index 4e51a0105b..fea6499dd8 100644 --- a/src/components/views/rooms/EditMessageComposer.tsx +++ b/src/components/views/rooms/EditMessageComposer.tsx @@ -131,11 +131,12 @@ export default class EditMessageComposer extends React.Component super(props); this.context = context; // otherwise React will only set it prior to render due to type def above + const isRestored = this.createEditorModel(); + const ev = this.props.editState.getEvent(); this.state = { - saveDisabled: true, + saveDisabled: !isRestored || !this.isContentModified(createEditContent(this.model, ev)["m.new_content"]), }; - this.createEditorModel(); window.addEventListener("beforeunload", this.saveStoredEditorState); this.dispatcherRef = dis.register(this.onAction); } @@ -230,12 +231,12 @@ export default class EditMessageComposer extends React.Component } } - private saveStoredEditorState(): void { + private saveStoredEditorState = (): void => { const item = SendHistoryManager.createItem(this.model); this.clearPreviousEdit(); localStorage.setItem(this.editorRoomKey, this.props.editState.getEvent().getId()); localStorage.setItem(this.editorStateKey, JSON.stringify(item)); - } + }; private isSlashCommand(): boolean { const parts = this.model.parts; @@ -256,10 +257,9 @@ export default class EditMessageComposer extends React.Component private isContentModified(newContent: IContent): boolean { // if nothing has changed then bail const oldContent = this.props.editState.getEvent().getContent(); - if (!this.editorRef.current?.isModified() || - (oldContent["msgtype"] === newContent["msgtype"] && oldContent["body"] === newContent["body"] && + if (oldContent["msgtype"] === newContent["msgtype"] && oldContent["body"] === newContent["body"] && oldContent["format"] === newContent["format"] && - oldContent["formatted_body"] === newContent["formatted_body"])) { + oldContent["formatted_body"] === newContent["formatted_body"]) { return false; } return true; @@ -410,36 +410,27 @@ export default class EditMessageComposer extends React.Component dis.unregister(this.dispatcherRef); } - private createEditorModel(): void { + private createEditorModel(): boolean { const { editState } = this.props; const room = this.getRoom(); const partCreator = new CommandPartCreator(room, this.context); + let parts; + let isRestored = false; if (editState.hasEditorState()) { // if restoring state from a previous editor, // restore serialized parts from the state parts = editState.getSerializedParts().map(p => partCreator.deserializePart(p)); } else { - //otherwise, either restore serialized parts from localStorage or parse the body of the event - parts = this.restoreStoredEditorState(partCreator) || parseEvent(editState.getEvent(), partCreator); + // otherwise, either restore serialized parts from localStorage or parse the body of the event + const restoredParts = this.restoreStoredEditorState(partCreator); + parts = restoredParts || parseEvent(editState.getEvent(), partCreator); + isRestored = !!restoredParts; } this.model = new EditorModel(parts, partCreator); this.saveStoredEditorState(); - } - private getInitialCaretPosition(): CaretPosition { - const { editState } = this.props; - let caretPosition; - if (editState.hasEditorState() && editState.getCaret()) { - // if restoring state from a previous editor, - // restore caret position from the state - const caret = editState.getCaret(); - caretPosition = this.model.positionForOffset(caret.offset, caret.atNodeEnd); - } else { - // otherwise, set it at the end - caretPosition = this.model.getPositionAtEnd(); - } - return caretPosition; + return isRestored; } private onChange = (): void => { diff --git a/src/components/views/rooms/EventTile.tsx b/src/components/views/rooms/EventTile.tsx index c9d1040433..7cceef4a86 100644 --- a/src/components/views/rooms/EventTile.tsx +++ b/src/components/views/rooms/EventTile.tsx @@ -47,6 +47,13 @@ import { StaticNotificationState } from "../../../stores/notifications/StaticNot import NotificationBadge from "./NotificationBadge"; import { ComposerInsertPayload } from "../../../dispatcher/payloads/ComposerInsertPayload"; import { Action } from '../../../dispatcher/actions'; +import MemberAvatar from '../avatars/MemberAvatar'; +import SenderProfile from '../messages/SenderProfile'; +import MessageTimestamp from '../messages/MessageTimestamp'; +import TooltipButton from '../elements/TooltipButton'; +import ReadReceiptMarker from "./ReadReceiptMarker"; +import MessageActionBar from "../messages/MessageActionBar"; +import ReactionsRow from '../messages/ReactionsRow'; const eventTileTypes = { [EventType.RoomMessage]: 'messages.MessageEvent', @@ -666,7 +673,6 @@ export default class EventTile extends React.Component { ); } - const ReadReceiptMarker = sdk.getComponent('rooms.ReadReceiptMarker'); const avatars = []; const receiptOffset = 15; let left = 0; @@ -733,7 +739,7 @@ export default class EventTile extends React.Component { ); } - onSenderProfileClick = event => { + onSenderProfileClick = () => { const mxEvent = this.props.mxEvent; dis.dispatch({ action: Action.ComposerInsert, @@ -841,10 +847,6 @@ export default class EventTile extends React.Component { }; render() { - const MessageTimestamp = sdk.getComponent('messages.MessageTimestamp'); - const SenderProfile = sdk.getComponent('messages.SenderProfile'); - const MemberAvatar = sdk.getComponent('avatars.MemberAvatar'); - //console.info("EventTile showUrlPreview for %s is %s", this.props.mxEvent.getId(), this.props.showUrlPreview); const content = this.props.mxEvent.getContent(); @@ -987,7 +989,6 @@ export default class EventTile extends React.Component { } } - const MessageActionBar = sdk.getComponent('messages.MessageActionBar'); const actionBar = !isEditing ? { { 'requestLink': (sub) => { sub } }, ); - const TooltipButton = sdk.getComponent('elements.TooltipButton'); const keyRequestInfo = isEncryptionFailure && !isRedacted ?
    @@ -1038,7 +1038,6 @@ export default class EventTile extends React.Component { let reactionsRow; if (!isRedacted) { - const ReactionsRow = sdk.getComponent('messages.ReactionsRow'); reactionsRow = = ({ links, mxEvent, onCancelClick, onHeightChanged }) => { + const [expanded, toggleExpanded] = useStateToggle(); + useEffect(() => { + onHeightChanged(); + }, [onHeightChanged, expanded]); + + const shownLinks = expanded ? links : links.slice(0, INITIAL_NUM_PREVIEWS); + + let toggleButton; + if (links.length > INITIAL_NUM_PREVIEWS) { + toggleButton = + { expanded + ? _t("Collapse") + : _t("Show %(count)s other previews", { count: links.length - shownLinks.length }) } + ; + } + + return
    + { shownLinks.map((link, i) => ( + + { i === 0 ? ( + + + + ): undefined } + + )) } + { toggleButton } +
    ; +}; + +export default LinkPreviewGroup; diff --git a/src/components/views/rooms/LinkPreviewWidget.js b/src/components/views/rooms/LinkPreviewWidget.tsx similarity index 73% rename from src/components/views/rooms/LinkPreviewWidget.js rename to src/components/views/rooms/LinkPreviewWidget.tsx index 360ca41d55..db13021b32 100644 --- a/src/components/views/rooms/LinkPreviewWidget.js +++ b/src/components/views/rooms/LinkPreviewWidget.tsx @@ -1,6 +1,5 @@ /* -Copyright 2016 OpenMarket Ltd -Copyright 2019 The Matrix.org Foundation C.I.C. +Copyright 2016 - 2021 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,26 +15,33 @@ limitations under the License. */ import React, { createRef } from 'react'; -import PropTypes from 'prop-types'; import { AllHtmlEntities } from 'html-entities'; +import { MatrixEvent } from 'matrix-js-sdk/src/models/event'; +import { IPreviewUrlResponse } from 'matrix-js-sdk/src/client'; + import { linkifyElement } from '../../../HtmlUtils'; import SettingsStore from "../../../settings/SettingsStore"; import { MatrixClientPeg } from "../../../MatrixClientPeg"; -import * as sdk from "../../../index"; import Modal from "../../../Modal"; import * as ImageUtils from "../../../ImageUtils"; -import { _t } from "../../../languageHandler"; import { replaceableComponent } from "../../../utils/replaceableComponent"; import { mediaFromMxc } from "../../../customisations/Media"; +import ImageView from '../elements/ImageView'; + +interface IProps { + link: string; // the URL being previewed + mxEvent: MatrixEvent; // the Event associated with the preview + onHeightChanged(): void; // called when the preview's contents has loaded +} + +interface IState { + preview?: IPreviewUrlResponse; +} @replaceableComponent("views.rooms.LinkPreviewWidget") -export default class LinkPreviewWidget extends React.Component { - static propTypes = { - link: PropTypes.string.isRequired, // the URL being previewed - mxEvent: PropTypes.object.isRequired, // the Event associated with the preview - onCancelClick: PropTypes.func, // called when the preview's cancel ('hide') button is clicked - onHeightChanged: PropTypes.func, // called when the preview's contents has loaded - }; +export default class LinkPreviewWidget extends React.Component { + private unmounted = false; + private readonly description = createRef(); constructor(props) { super(props); @@ -44,31 +50,25 @@ export default class LinkPreviewWidget extends React.Component { preview: null, }; - this.unmounted = false; - MatrixClientPeg.get().getUrlPreview(this.props.link, this.props.mxEvent.getTs()).then((res)=>{ + MatrixClientPeg.get().getUrlPreview(this.props.link, this.props.mxEvent.getTs()).then((preview) => { if (this.unmounted) { return; } - this.setState( - { preview: res }, - this.props.onHeightChanged, - ); - }, (error)=>{ + this.setState({ preview }, this.props.onHeightChanged); + }, (error) => { console.error("Failed to get URL preview: " + error); }); - - this._description = createRef(); } componentDidMount() { - if (this._description.current) { - linkifyElement(this._description.current); + if (this.description.current) { + linkifyElement(this.description.current); } } componentDidUpdate() { - if (this._description.current) { - linkifyElement(this._description.current); + if (this.description.current) { + linkifyElement(this.description.current); } } @@ -76,11 +76,10 @@ export default class LinkPreviewWidget extends React.Component { this.unmounted = true; } - onImageClick = ev => { + private onImageClick = ev => { const p = this.state.preview; if (ev.button != 0 || ev.metaKey) return; ev.preventDefault(); - const ImageView = sdk.getComponent("elements.ImageView"); let src = p["og:image"]; if (src && src.startsWith("mxc://")) { @@ -136,21 +135,17 @@ export default class LinkPreviewWidget extends React.Component { // opaque string. This does not allow any HTML to be injected into the DOM. const description = AllHtmlEntities.decode(p["og:description"] || ""); - const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); return (
    { img }
    { p["og:site_name"] ? (" - " + p["og:site_name"]) : null }
    -
    +
    { description }
    - - - + { this.props.children }
    ); } diff --git a/src/components/views/rooms/MessageComposer.tsx b/src/components/views/rooms/MessageComposer.tsx index 7d61ba5ec6..b7015d2275 100644 --- a/src/components/views/rooms/MessageComposer.tsx +++ b/src/components/views/rooms/MessageComposer.tsx @@ -17,7 +17,6 @@ import React from 'react'; import classNames from 'classnames'; import { _t } from '../../../languageHandler'; import { MatrixClientPeg } from '../../../MatrixClientPeg'; -import * as sdk from '../../../index'; import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import { Room } from "matrix-js-sdk/src/models/room"; import { RoomMember } from "matrix-js-sdk/src/models/room-member"; @@ -44,13 +43,14 @@ import SendMessageComposer from "./SendMessageComposer"; import { ComposerInsertPayload } from "../../../dispatcher/payloads/ComposerInsertPayload"; import { Action } from "../../../dispatcher/actions"; import EditorModel from "../../../editor/model"; +import EmojiPicker from '../emojipicker/EmojiPicker'; +import MemberStatusMessageAvatar from "../avatars/MemberStatusMessageAvatar"; interface IComposerAvatarProps { me: object; } function ComposerAvatar(props: IComposerAvatarProps) { - const MemberStatusMessageAvatar = sdk.getComponent('avatars.MemberStatusMessageAvatar'); return
    ; @@ -76,7 +76,6 @@ const EmojiButton = ({ addEmoji }) => { let contextMenu; if (menuDisplayed) { const buttonRect = button.current.getBoundingClientRect(); - const EmojiPicker = sdk.getComponent('emojipicker.EmojiPicker'); contextMenu = ; @@ -366,15 +365,12 @@ export default class MessageComposer extends React.Component { ]; if (!this.state.tombstone && this.state.canSendMessages) { - const SendMessageComposer = sdk.getComponent("rooms.SendMessageComposer"); - controls.push( this.messageComposerInput = c} key="controls_input" room={this.props.room} placeholder={this.renderPlaceholderText()} - resizeNotifier={this.props.resizeNotifier} permalinkCreator={this.props.permalinkCreator} replyToEvent={this.props.replyToEvent} onChange={this.onChange} diff --git a/src/components/views/rooms/ThirdPartyMemberInfo.tsx b/src/components/views/rooms/ThirdPartyMemberInfo.tsx index 4cdabede2f..2bcc3ead57 100644 --- a/src/components/views/rooms/ThirdPartyMemberInfo.tsx +++ b/src/components/views/rooms/ThirdPartyMemberInfo.tsx @@ -20,13 +20,14 @@ import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import { Room } from "matrix-js-sdk/src/models/room"; import { _t } from "../../../languageHandler"; import dis from "../../../dispatcher/dispatcher"; -import * as sdk from "../../../index"; import Modal from "../../../Modal"; import { isValid3pidInvite } from "../../../RoomInvite"; import RoomAvatar from "../avatars/RoomAvatar"; import RoomName from "../elements/RoomName"; import { replaceableComponent } from "../../../utils/replaceableComponent"; import SettingsStore from "../../../settings/SettingsStore"; +import ErrorDialog from '../dialogs/ErrorDialog'; +import AccessibleButton from '../elements/AccessibleButton'; interface IProps { event: MatrixEvent; @@ -104,7 +105,6 @@ export default class ThirdPartyMemberInfo extends React.Component { - const SettingsFlag = sdk.getComponent('views.elements.SettingsFlag'); return
    {_t("Encryption")} diff --git a/src/components/views/settings/EventIndexPanel.tsx b/src/components/views/settings/EventIndexPanel.tsx index 59b4a166c0..73b324b739 100644 --- a/src/components/views/settings/EventIndexPanel.tsx +++ b/src/components/views/settings/EventIndexPanel.tsx @@ -18,7 +18,6 @@ import React from 'react'; import { _t } from '../../../languageHandler'; import SdkConfig from "../../../SdkConfig"; -import * as sdk from '../../../index'; import Modal from '../../../Modal'; import SettingsStore from "../../../settings/SettingsStore"; import AccessibleButton from "../elements/AccessibleButton"; @@ -27,6 +26,7 @@ import EventIndexPeg from "../../../indexing/EventIndexPeg"; import { SettingLevel } from "../../../settings/SettingLevel"; import { replaceableComponent } from "../../../utils/replaceableComponent"; import SeshatResetDialog from '../dialogs/SeshatResetDialog'; +import InlineSpinner from '../elements/InlineSpinner'; interface IState { enabling: boolean; @@ -147,7 +147,6 @@ export default class EventIndexPanel extends React.Component<{}, IState> { render() { let eventIndexingSettings = null; - const InlineSpinner = sdk.getComponent('elements.InlineSpinner'); const brand = SdkConfig.get().brand; if (EventIndexPeg.get() !== null) { diff --git a/src/components/views/settings/SetIdServer.tsx b/src/components/views/settings/SetIdServer.tsx index 4a73c82d9b..9180c98101 100644 --- a/src/components/views/settings/SetIdServer.tsx +++ b/src/components/views/settings/SetIdServer.tsx @@ -17,7 +17,6 @@ limitations under the License. import url from 'url'; import React from 'react'; import { _t } from "../../../languageHandler"; -import * as sdk from '../../../index'; import { MatrixClientPeg } from "../../../MatrixClientPeg"; import Modal from '../../../Modal'; import dis from "../../../dispatcher/dispatcher"; @@ -28,6 +27,10 @@ import { getDefaultIdentityServerUrl, doesIdentityServerHaveTerms } from '../../ import { timeout } from "../../../utils/promise"; import { replaceableComponent } from "../../../utils/replaceableComponent"; import { ActionPayload } from '../../../dispatcher/payloads'; +import InlineSpinner from '../elements/InlineSpinner'; +import AccessibleButton from '../elements/AccessibleButton'; +import Field from '../elements/Field'; +import QuestionDialog from "../dialogs/QuestionDialog"; // We'll wait up to this long when checking for 3PID bindings on the IS. const REACHABILITY_TIMEOUT = 10000; // ms @@ -126,7 +129,6 @@ export default class SetIdServer extends React.Component { private getTooltip = () => { if (this.state.checking) { - const InlineSpinner = sdk.getComponent('views.elements.InlineSpinner'); return
    { _t("Checking server") } @@ -217,7 +219,6 @@ export default class SetIdServer extends React.Component { }; private showNoTermsWarning(fullUrl) { - const QuestionDialog = sdk.getComponent("views.dialogs.QuestionDialog"); const { finished } = Modal.createTrackedDialog('No Terms Warning', '', QuestionDialog, { title: _t("Identity server has no terms of service"), description: ( @@ -319,7 +320,6 @@ export default class SetIdServer extends React.Component { message = unboundMessage; } - const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); const { finished } = Modal.createTrackedDialog('Identity Server Bound Warning', '', QuestionDialog, { title, description: message, @@ -352,8 +352,6 @@ export default class SetIdServer extends React.Component { }; render() { - const AccessibleButton = sdk.getComponent('views.elements.AccessibleButton'); - const Field = sdk.getComponent('elements.Field'); const idServerUrl = this.state.currentClientIdServer; let sectionTitle; let bodyText; @@ -398,7 +396,6 @@ export default class SetIdServer extends React.Component { discoButtonContent = _t("Do not use an identity server"); } if (this.state.disconnectBusy) { - const InlineSpinner = sdk.getComponent('views.elements.InlineSpinner'); discoButtonContent = ; } discoSection =
    diff --git a/src/components/views/settings/tabs/room/RolesRoomSettingsTab.tsx b/src/components/views/settings/tabs/room/RolesRoomSettingsTab.tsx index 67c7998751..f12499e7f9 100644 --- a/src/components/views/settings/tabs/room/RolesRoomSettingsTab.tsx +++ b/src/components/views/settings/tabs/room/RolesRoomSettingsTab.tsx @@ -17,7 +17,6 @@ limitations under the License. import React from 'react'; import { _t, _td } from "../../../../../languageHandler"; import { MatrixClientPeg } from "../../../../../MatrixClientPeg"; -import * as sdk from "../../../../.."; import AccessibleButton from "../../../elements/AccessibleButton"; import Modal from "../../../../../Modal"; import { replaceableComponent } from "../../../../../utils/replaceableComponent"; @@ -26,6 +25,8 @@ import { RoomMember } from "matrix-js-sdk/src/models/room-member"; import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import { RoomState } from "matrix-js-sdk/src/models/room-state"; import { compare } from "../../../../../utils/strings"; +import ErrorDialog from '../../../dialogs/ErrorDialog'; +import PowerSelector from "../../../elements/PowerSelector"; const plEventsToLabels = { // These will be translated for us later. @@ -76,7 +77,6 @@ interface IBannedUserProps { export class BannedUser extends React.Component { private onUnbanClick = (e) => { MatrixClientPeg.get().unban(this.props.member.roomId, this.props.member.userId).catch((err) => { - const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); console.error("Failed to unban: " + err); Modal.createTrackedDialog('Failed to unban', '', ErrorDialog, { title: _t('Error'), @@ -176,7 +176,6 @@ export default class RolesRoomSettingsTab extends React.Component { client.sendStateEvent(this.props.roomId, "m.room.power_levels", plContent).catch(e => { console.error(e); - const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); Modal.createTrackedDialog('Power level requirement change failed', '', ErrorDialog, { title: _t('Error changing power level requirement'), description: _t( @@ -203,7 +202,6 @@ export default class RolesRoomSettingsTab extends React.Component { client.sendStateEvent(this.props.roomId, "m.room.power_levels", plContent).catch(e => { console.error(e); - const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); Modal.createTrackedDialog('Power level change failed', '', ErrorDialog, { title: _t('Error changing power level'), description: _t( @@ -215,8 +213,6 @@ export default class RolesRoomSettingsTab extends React.Component { }; render() { - const PowerSelector = sdk.getComponent('elements.PowerSelector'); - const client = MatrixClientPeg.get(); const room = client.getRoom(this.props.roomId); const plEvent = room.currentState.getStateEvents('m.room.power_levels', ''); diff --git a/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.tsx b/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.tsx index 4868ffd4fc..312d7f21a0 100644 --- a/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.tsx +++ b/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.tsx @@ -18,7 +18,6 @@ import React from 'react'; import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import { _t } from "../../../../../languageHandler"; import { MatrixClientPeg } from "../../../../../MatrixClientPeg"; -import * as sdk from "../../../../.."; import LabelledToggleSwitch from "../../../elements/LabelledToggleSwitch"; import Modal from "../../../../../Modal"; import QuestionDialog from "../../../dialogs/QuestionDialog"; @@ -27,6 +26,7 @@ import { SettingLevel } from "../../../../../settings/SettingLevel"; import SettingsStore from "../../../../../settings/SettingsStore"; import { UIFeature } from "../../../../../settings/UIFeature"; import { replaceableComponent } from "../../../../../utils/replaceableComponent"; +import SettingsFlag from '../../../elements/SettingsFlag'; // Knock and private are reserved keywords which are not yet implemented. export enum JoinRule { @@ -385,8 +385,6 @@ export default class SecurityRoomSettingsTab extends React.Component void; @@ -81,10 +82,6 @@ export default class HelpUserSettingsTab extends React.Component }; private onBugReport = (e) => { - const BugReportDialog = sdk.getComponent("dialogs.BugReportDialog"); - if (!BugReportDialog) { - return; - } Modal.createTrackedDialog('Bug Report Dialog', '', BugReportDialog, {}); }; @@ -171,7 +168,6 @@ export default class HelpUserSettingsTab extends React.Component const successful = await copyPlaintext(MatrixClientPeg.get().getAccessToken()); const buttonRect = target.getBoundingClientRect(); - const GenericTextContextMenu = sdk.getComponent('context_menus.GenericTextContextMenu'); const { close } = ContextMenu.createMenu(GenericTextContextMenu, { ...toRightOf(buttonRect, 2), message: successful ? _t('Copied!') : _t('Failed to copy'), diff --git a/src/components/views/settings/tabs/user/MjolnirUserSettingsTab.tsx b/src/components/views/settings/tabs/user/MjolnirUserSettingsTab.tsx index 0a2b9f3212..41c44e65a0 100644 --- a/src/components/views/settings/tabs/user/MjolnirUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/MjolnirUserSettingsTab.tsx @@ -22,8 +22,11 @@ import { ListRule } from "../../../../../mjolnir/ListRule"; import { BanList, RULE_SERVER, RULE_USER } from "../../../../../mjolnir/BanList"; import Modal from "../../../../../Modal"; import { MatrixClientPeg } from "../../../../../MatrixClientPeg"; -import * as sdk from "../../../../../index"; import { replaceableComponent } from "../../../../../utils/replaceableComponent"; +import ErrorDialog from "../../../dialogs/ErrorDialog"; +import QuestionDialog from "../../../dialogs/QuestionDialog"; +import AccessibleButton from "../../../elements/AccessibleButton"; +import Field from "../../../elements/Field"; interface IState { busy: boolean; @@ -68,7 +71,6 @@ export default class MjolnirUserSettingsTab extends React.Component<{}, IState> } catch (e) { console.error(e); - const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); Modal.createTrackedDialog('Failed to add Mjolnir rule', '', ErrorDialog, { title: _t('Error adding ignored user/server'), description: _t('Something went wrong. Please try again or view your console for hints.'), @@ -90,7 +92,6 @@ export default class MjolnirUserSettingsTab extends React.Component<{}, IState> } catch (e) { console.error(e); - const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); Modal.createTrackedDialog('Failed to subscribe to Mjolnir list', '', ErrorDialog, { title: _t('Error subscribing to list'), description: _t('Please verify the room ID or address and try again.'), @@ -108,7 +109,6 @@ export default class MjolnirUserSettingsTab extends React.Component<{}, IState> } catch (e) { console.error(e); - const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); Modal.createTrackedDialog('Failed to remove Mjolnir rule', '', ErrorDialog, { title: _t('Error removing ignored user/server'), description: _t('Something went wrong. Please try again or view your console for hints.'), @@ -126,7 +126,6 @@ export default class MjolnirUserSettingsTab extends React.Component<{}, IState> } catch (e) { console.error(e); - const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); Modal.createTrackedDialog('Failed to unsubscribe from Mjolnir list', '', ErrorDialog, { title: _t('Error unsubscribing from list'), description: _t('Please try again or view your console for hints.'), @@ -137,8 +136,6 @@ export default class MjolnirUserSettingsTab extends React.Component<{}, IState> } private viewListRules(list: BanList) { - const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); - const room = MatrixClientPeg.get().getRoom(list.roomId); const name = room ? room.name : list.roomId; @@ -168,8 +165,6 @@ export default class MjolnirUserSettingsTab extends React.Component<{}, IState> } private renderPersonalBanListRules() { - const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); - const list = Mjolnir.sharedInstance().getPersonalList(); const rules = list ? [...list.userRules, ...list.serverRules] : []; if (!list || rules.length <= 0) return {_t("You have not ignored anyone.")}; @@ -199,8 +194,6 @@ export default class MjolnirUserSettingsTab extends React.Component<{}, IState> } private renderSubscribedBanLists() { - const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); - const personalList = Mjolnir.sharedInstance().getPersonalList(); const lists = Mjolnir.sharedInstance().lists.filter(b => { return personalList? personalList.roomId !== b.roomId : true; @@ -241,8 +234,6 @@ export default class MjolnirUserSettingsTab extends React.Component<{}, IState> } render() { - const Field = sdk.getComponent('elements.Field'); - const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); const brand = SdkConfig.get().brand; return ( diff --git a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.tsx b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.tsx index 405125bb59..c4140153a5 100644 --- a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.tsx @@ -20,10 +20,10 @@ import { _t } from "../../../../../languageHandler"; import LabelledToggleSwitch from "../../../elements/LabelledToggleSwitch"; import SettingsStore from "../../../../../settings/SettingsStore"; import Field from "../../../elements/Field"; -import * as sdk from "../../../../.."; import PlatformPeg from "../../../../../PlatformPeg"; import { SettingLevel } from "../../../../../settings/SettingLevel"; import { replaceableComponent } from "../../../../../utils/replaceableComponent"; +import SettingsFlag from '../../../elements/SettingsFlag'; import * as KeyboardShortcuts from "../../../../../accessibility/KeyboardShortcuts"; import AccessibleButton from "../../../elements/AccessibleButton"; @@ -184,7 +184,6 @@ export default class PreferencesUserSettingsTab extends React.Component<{}, ISta }; private renderGroup(settingIds: string[]): React.ReactNodeArray { - const SettingsFlag = sdk.getComponent("views.elements.SettingsFlag"); return settingIds.filter(SettingsStore.isEnabled).map(i => { return ; }); diff --git a/src/components/views/spaces/SpaceCreateMenu.tsx b/src/components/views/spaces/SpaceCreateMenu.tsx index 4bb61d7ccb..5f16684fb8 100644 --- a/src/components/views/spaces/SpaceCreateMenu.tsx +++ b/src/components/views/spaces/SpaceCreateMenu.tsx @@ -220,6 +220,7 @@ const SpaceCreateMenu = ({ onFinished }) => { value={alias} placeholder={name ? nameToAlias(name, domain) : _t("e.g. my-space")} label={_t("Address")} + disabled={busy} /> : null } diff --git a/src/components/views/spaces/SpaceSettingsGeneralTab.tsx b/src/components/views/spaces/SpaceSettingsGeneralTab.tsx index 3afdc629e4..9f4e0ecea7 100644 --- a/src/components/views/spaces/SpaceSettingsGeneralTab.tsx +++ b/src/components/views/spaces/SpaceSettingsGeneralTab.tsx @@ -96,7 +96,7 @@ const SpaceSettingsGeneralTab = ({ matrixClient: cli, space, onFinished }: IProp { error &&
    { error }
    } - onFinished(false)} /> +
    { const startTime = CountlyAnalytics.getTimestamp(); - const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); - const Loader = sdk.getComponent("elements.Spinner"); - const client = MatrixClientPeg.get(); if (client.isGuest()) { dis.dispatch({ action: 'require_registration' }); @@ -153,7 +151,7 @@ export default function createRoom(opts: IOpts): Promise { } let modal; - if (opts.spinner) modal = Modal.createDialog(Loader, null, 'mx_Dialog_spinner'); + if (opts.spinner) modal = Modal.createDialog(Spinner, null, 'mx_Dialog_spinner'); let roomId; return client.createRoom(createOpts).finally(function() { diff --git a/src/customisations/Security.ts b/src/customisations/Security.ts index c2262e5f71..9083274aa5 100644 --- a/src/customisations/Security.ts +++ b/src/customisations/Security.ts @@ -16,7 +16,7 @@ limitations under the License. import { IMatrixClientCreds } from "../MatrixClientPeg"; import { Kind as SetupEncryptionKind } from "../toasts/SetupEncryptionToast"; -import { ISecretStorageKeyInfo } from 'matrix-js-sdk/src/matrix'; +import { ISecretStorageKeyInfo } from 'matrix-js-sdk/src/crypto/api'; /* eslint-disable-next-line @typescript-eslint/no-unused-vars */ function examineLoginResponse( diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index bbf6954435..7d4252545b 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1508,6 +1508,8 @@ "Your message was sent": "Your message was sent", "Failed to send": "Failed to send", "Scroll to most recent messages": "Scroll to most recent messages", + "Show %(count)s other previews|other": "Show %(count)s other previews", + "Show %(count)s other previews|one": "Show %(count)s other preview", "Close preview": "Close preview", "and %(count)s others...|other": "and %(count)s others...", "and %(count)s others...|one": "and one other...", diff --git a/src/rageshake/submit-rageshake.ts b/src/rageshake/submit-rageshake.ts index 5e0b2c3c4d..b629ddafd8 100644 --- a/src/rageshake/submit-rageshake.ts +++ b/src/rageshake/submit-rageshake.ts @@ -93,15 +93,15 @@ async function collectBugReport(opts: IOpts = {}, gzipLogs = true) { body.append("cross_signing_supported_by_hs", String(await client.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing"))); body.append("cross_signing_key", crossSigning.getId()); - body.append("cross_signing_pk_in_secret_storage", + body.append("cross_signing_privkey_in_secret_storage", String(!!(await crossSigning.isStoredInSecretStorage(secretStorage)))); const pkCache = client.getCrossSigningCacheCallbacks(); - body.append("cross_signing_master_pk_cached", + body.append("cross_signing_master_privkey_cached", String(!!(pkCache && await pkCache.getCrossSigningKeyCache("master")))); - body.append("cross_signing_self_signing_pk_cached", + body.append("cross_signing_self_signing_privkey_cached", String(!!(pkCache && await pkCache.getCrossSigningKeyCache("self_signing")))); - body.append("cross_signing_user_signing_pk_cached", + body.append("cross_signing_user_signing_privkey_cached", String(!!(pkCache && await pkCache.getCrossSigningKeyCache("user_signing")))); body.append("secret_storage_ready", String(await client.isSecretStorageReady())); diff --git a/src/stores/RoomViewStore.tsx b/src/stores/RoomViewStore.tsx index 87978df471..10f42f3166 100644 --- a/src/stores/RoomViewStore.tsx +++ b/src/stores/RoomViewStore.tsx @@ -164,6 +164,7 @@ class RoomViewStore extends Store { } break; case 'open_room_settings': { + // FIXME: Using an import will result in test failures const RoomSettingsDialog = sdk.getComponent("dialogs.RoomSettingsDialog"); Modal.createTrackedDialog('Room settings', '', RoomSettingsDialog, { roomId: payload.room_id || this.state.roomId, @@ -340,6 +341,7 @@ class RoomViewStore extends Store { } } + // FIXME: Using an import will result in test failures const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); Modal.createTrackedDialog('Failed to join room', '', ErrorDialog, { title: _t("Failed to join room"), diff --git a/src/stores/SetupEncryptionStore.ts b/src/stores/SetupEncryptionStore.ts index e969c64853..7197374502 100644 --- a/src/stores/SetupEncryptionStore.ts +++ b/src/stores/SetupEncryptionStore.ts @@ -17,7 +17,7 @@ limitations under the License. import EventEmitter from 'events'; import { VerificationRequest } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; import { IKeyBackupInfo } from "matrix-js-sdk/src/crypto/keybackup"; -import { ISecretStorageKeyInfo } from "matrix-js-sdk/src/matrix"; +import { ISecretStorageKeyInfo } from "matrix-js-sdk/src/crypto/api"; import { PHASE_DONE as VERIF_PHASE_DONE } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; import { MatrixClientPeg } from '../MatrixClientPeg'; diff --git a/src/toasts/SetupEncryptionToast.ts b/src/toasts/SetupEncryptionToast.ts index ade7dfe3f0..bdaeb5142f 100644 --- a/src/toasts/SetupEncryptionToast.ts +++ b/src/toasts/SetupEncryptionToast.ts @@ -15,7 +15,6 @@ limitations under the License. */ import Modal from "../Modal"; -import * as sdk from "../index"; import { _t } from "../languageHandler"; import DeviceListener from "../DeviceListener"; import SetupEncryptionDialog from "../components/views/dialogs/security/SetupEncryptionDialog"; @@ -23,6 +22,7 @@ import { accessSecretStorage } from "../SecurityManager"; import ToastStore from "../stores/ToastStore"; import GenericToast from "../components/views/toasts/GenericToast"; import SecurityCustomisations from "../customisations/Security"; +import Spinner from "../components/views/elements/Spinner"; const TOAST_KEY = "setupencryption"; @@ -88,7 +88,6 @@ export const showToast = (kind: Kind) => { Modal.createTrackedDialog("Verify session", "Verify session", SetupEncryptionDialog, {}, null, /* priority = */ false, /* static = */ true); } else { - const Spinner = sdk.getComponent("elements.Spinner"); const modal = Modal.createDialog( Spinner, null, "mx_Dialog_spinner", /* priority */ false, /* static */ true, ); diff --git a/src/verification.ts b/src/verification.ts index 22b62461a1..719c0ec5b3 100644 --- a/src/verification.ts +++ b/src/verification.ts @@ -19,7 +19,6 @@ import { User } from "matrix-js-sdk/src/models/user"; import { MatrixClientPeg } from './MatrixClientPeg'; import dis from "./dispatcher/dispatcher"; import Modal from './Modal'; -import * as sdk from './index'; import { RightPanelPhases } from "./stores/RightPanelStorePhases"; import { findDMForUser } from './createRoom'; import { accessSecretStorage } from './SecurityManager'; @@ -27,6 +26,7 @@ import { verificationMethods } from 'matrix-js-sdk/src/crypto'; import { Action } from './dispatcher/actions'; import UntrustedDeviceDialog from "./components/views/dialogs/UntrustedDeviceDialog"; import { IDevice } from "./components/views/right_panel/UserInfo"; +import ManualDeviceKeyVerificationDialog from "./components/views/dialogs/ManualDeviceKeyVerificationDialog"; async function enable4SIfNeeded() { const cli = MatrixClientPeg.get(); @@ -71,8 +71,6 @@ export async function verifyDevice(user: User, device: IDevice) { refireParams: { member: user, verificationRequestPromise }, }); } else if (action === "legacy") { - const ManualDeviceKeyVerificationDialog = - sdk.getComponent("dialogs.ManualDeviceKeyVerificationDialog"); Modal.createTrackedDialog("Legacy verify session", "legacy verify session", ManualDeviceKeyVerificationDialog, { diff --git a/test/components/views/messages/TextualBody-test.js b/test/components/views/messages/TextualBody-test.js index c9418fc557..c6a3f3c779 100644 --- a/test/components/views/messages/TextualBody-test.js +++ b/test/components/views/messages/TextualBody-test.js @@ -302,7 +302,7 @@ describe("", () => { event: true, }); - const wrapper = mount(); + const wrapper = mount( {}} />); expect(wrapper.text()).toBe(ev.getContent().body); let widgets = wrapper.find("LinkPreviewWidget"); diff --git a/test/components/views/rooms/MemberList-test.tsx b/test/components/views/rooms/MemberList-test.tsx index a169cd08e6..f720bc7a6d 100644 --- a/test/components/views/rooms/MemberList-test.tsx +++ b/test/components/views/rooms/MemberList-test.tsx @@ -14,18 +14,20 @@ See the License for the specific language governing permissions and limitations under the License. */ +import "../../../skinned-sdk"; + import React from 'react'; import ReactTestUtils from 'react-dom/test-utils'; import ReactDOM from 'react-dom'; import * as TestUtils from '../../../test-utils'; -import sdk from '../../../skinned-sdk'; import { MatrixClientPeg } from '../../../../src/MatrixClientPeg'; import { Room } from 'matrix-js-sdk/src/models/room'; import { RoomMember } from 'matrix-js-sdk/src/models/room-member'; import { User } from "matrix-js-sdk/src/models/user"; import { compare } from "../../../../src/utils/strings"; import MemberList from "../../../../src/components/views/rooms/MemberList"; +import MemberTile from '../../../../src/components/views/rooms/MemberTile'; function generateRoomId() { return '!' + Math.random().toString().slice(2, 10) + ':domain'; @@ -206,7 +208,6 @@ describe('MemberList', () => { } function itDoesOrderMembersCorrectly(enablePresence) { - const MemberTile = sdk.getComponent("rooms.MemberTile"); describe('does order members correctly', () => { // Note: even if presence is disabled, we still expect that the presence // tests will pass. All expectOrderedByPresenceAndPowerLevel does is ensure diff --git a/test/end-to-end-tests/element/install.sh b/test/end-to-end-tests/element/install.sh index e38f795df1..e1df709c68 100755 --- a/test/end-to-end-tests/element/install.sh +++ b/test/end-to-end-tests/element/install.sh @@ -12,5 +12,5 @@ unzip -q element.zip rm element.zip mv element-web-${ELEMENT_BRANCH} element-web cd element-web -yarn install +yarn install --pure-lockfile yarn run build diff --git a/test/end-to-end-tests/install.sh b/test/end-to-end-tests/install.sh index bbe7a24c9b..b2254a4ea7 100755 --- a/test/end-to-end-tests/install.sh +++ b/test/end-to-end-tests/install.sh @@ -4,4 +4,4 @@ set -e ./synapse/install.sh # local testing doesn't need a Element fetched from master, # so not installing that by default -yarn install +yarn install --pure-lockfile diff --git a/yarn.lock b/yarn.lock index c8c3315855..90f415673d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1677,6 +1677,11 @@ "@types/scheduler" "*" csstype "^3.0.2" +"@types/retry@^0.12.0": + version "0.12.0" + resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" + integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA== + "@types/sanitize-html@^2.3.1": version "2.3.1" resolved "https://registry.yarnpkg.com/@types/sanitize-html/-/sanitize-html-2.3.1.tgz#094d696b83b7394b016e96342bbffa6a028795ce" @@ -5445,10 +5450,10 @@ mathml-tag-names@^2.1.3: resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz#4ddadd67308e780cf16a47685878ee27b736a0a3" integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg== -matrix-js-sdk@12.0.0: - version "12.0.0" - resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-12.0.0.tgz#8ee7cc37661476341d0c792a1a12bc78b19f9fdd" - integrity sha512-DHeq87Sx9Dv37FYyvZkmA1VYsQUNaVgc3QzMUkFwoHt1T4EZzgyYpdsp3uYruJzUW0ACvVJcwFdrU4e1VS97dQ== +matrix-js-sdk@12.0.1: + version "12.0.1" + resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-12.0.1.tgz#3a63881f743420a4d39474daa39bd0fb90930d43" + integrity sha512-HkOWv8QHojceo3kPbC+vAIFUjsRAig6MBvEY35UygS3g2dL0UcJ5Qx09/2wcXtu6dowlDnWsz2HHk62tS2cklA== dependencies: "@babel/runtime" "^7.12.5" another-json "^0.2.0" @@ -5456,6 +5461,7 @@ matrix-js-sdk@12.0.0: bs58 "^4.0.1" content-type "^1.0.4" loglevel "^1.7.1" + p-retry "^4.5.0" qs "^6.9.6" request "^2.88.2" unhomoglyph "^1.0.6" @@ -6007,6 +6013,14 @@ p-locate@^4.1.0: dependencies: p-limit "^2.2.0" +p-retry@^4.5.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.6.0.tgz#9de15ae696278cffe86fce2d8f73b7f894f8bc9e" + integrity sha512-SAHbQEwg3X5DRNaLmWjT+DlGc93ba5i+aP3QLfVNDncQEQO4xjbYW4N/lcVTSuP0aJietGfx2t94dJLzfBMpXw== + dependencies: + "@types/retry" "^0.12.0" + retry "^0.13.1" + p-try@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" @@ -6440,6 +6454,11 @@ react-beautiful-dnd@^13.1.0: redux "^4.0.4" use-memo-one "^1.1.1" +react-blurhash@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/react-blurhash/-/react-blurhash-0.1.3.tgz#735f28f8f07fb358d7efe7e7e6dc65a7272bf89e" + integrity sha512-Q9lqbXg92NU6/2DoIl/cBM8YWL+Z4X66OiG4aT9ozOgjBwx104LHFCH5stf6aF+s0Q9Wf310Ul+dG+VXJltmPg== + react-clientside-effect@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/react-clientside-effect/-/react-clientside-effect-1.2.3.tgz#95c95f520addfb71743608b990bfe01eb002012b" @@ -6816,6 +6835,11 @@ ret@~0.1.10: resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== +retry@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" + integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== + reusify@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"