Merge branch 'develop' into gsouquet/message-bubbles-4635

pull/21833/head
Germain Souquet 2021-07-13 10:15:27 +02:00
commit ba3e7e24ba
151 changed files with 1395 additions and 984 deletions

6
__mocks__/FontManager.js Normal file
View File

@ -0,0 +1,6 @@
// Stub out FontManager for tests as it doesn't validate anything we don't already know given
// our fixed test environment and it requires the installation of node-canvas.
module.exports = {
fixupColorFonts: () => Promise.resolve(),
};

View File

@ -54,7 +54,6 @@
}, },
"dependencies": { "dependencies": {
"@babel/runtime": "^7.12.5", "@babel/runtime": "^7.12.5",
"@types/commonmark": "^0.27.4",
"await-lock": "^2.1.0", "await-lock": "^2.1.0",
"blurhash": "^1.1.3", "blurhash": "^1.1.3",
"browser-encrypt-attachment": "^0.3.0", "browser-encrypt-attachment": "^0.3.0",
@ -92,6 +91,7 @@
"re-resizable": "^6.9.0", "re-resizable": "^6.9.0",
"react": "^17.0.2", "react": "^17.0.2",
"react-beautiful-dnd": "^13.1.0", "react-beautiful-dnd": "^13.1.0",
"react-blurhash": "^0.1.3",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-focus-lock": "^2.5.0", "react-focus-lock": "^2.5.0",
"react-transition-group": "^4.4.1", "react-transition-group": "^4.4.1",
@ -124,7 +124,9 @@
"@peculiar/webcrypto": "^1.1.4", "@peculiar/webcrypto": "^1.1.4",
"@sinonjs/fake-timers": "^7.0.2", "@sinonjs/fake-timers": "^7.0.2",
"@types/classnames": "^2.2.11", "@types/classnames": "^2.2.11",
"@types/commonmark": "^0.27.4",
"@types/counterpart": "^0.18.1", "@types/counterpart": "^0.18.1",
"@types/css-font-loading-module": "^0.0.6",
"@types/diff-match-patch": "^1.0.32", "@types/diff-match-patch": "^1.0.32",
"@types/flux": "^3.1.9", "@types/flux": "^3.1.9",
"@types/jest": "^26.0.20", "@types/jest": "^26.0.20",

View File

@ -202,6 +202,7 @@
@import "./views/rooms/_GroupLayout.scss"; @import "./views/rooms/_GroupLayout.scss";
@import "./views/rooms/_IRCLayout.scss"; @import "./views/rooms/_IRCLayout.scss";
@import "./views/rooms/_JumpToBottomButton.scss"; @import "./views/rooms/_JumpToBottomButton.scss";
@import "./views/rooms/_LinkPreviewGroup.scss";
@import "./views/rooms/_LinkPreviewWidget.scss"; @import "./views/rooms/_LinkPreviewWidget.scss";
@import "./views/rooms/_MemberInfo.scss"; @import "./views/rooms/_MemberInfo.scss";
@import "./views/rooms/_MemberList.scss"; @import "./views/rooms/_MemberList.scss";
@ -262,6 +263,7 @@
@import "./views/voip/_CallContainer.scss"; @import "./views/voip/_CallContainer.scss";
@import "./views/voip/_CallView.scss"; @import "./views/voip/_CallView.scss";
@import "./views/voip/_CallViewForRoom.scss"; @import "./views/voip/_CallViewForRoom.scss";
@import "./views/voip/_CallPreview.scss";
@import "./views/voip/_DialPad.scss"; @import "./views/voip/_DialPad.scss";
@import "./views/voip/_DialPadContextMenu.scss"; @import "./views/voip/_DialPadContextMenu.scss";
@import "./views/voip/_DialPadModal.scss"; @import "./views/voip/_DialPadModal.scss";

View File

@ -72,7 +72,7 @@ limitations under the License.
.mx_AccessibleButton_kind_danger_outline { .mx_AccessibleButton_kind_danger_outline {
color: $button-danger-bg-color; color: $button-danger-bg-color;
background-color: $button-secondary-bg-color; background-color: transparent;
border: 1px solid $button-danger-bg-color; border: 1px solid $button-danger-bg-color;
} }

View File

@ -451,8 +451,7 @@ $hover-select-border: 4px;
pre, code { pre, code {
font-family: $monospace-font-family !important; font-family: $monospace-font-family !important;
// deliberate constants as we're behind an invert filter background-color: $header-panel-bg-color;
color: #333;
} }
pre { pre {
@ -462,11 +461,6 @@ $hover-select-border: 4px;
overflow-x: overlay; overflow-x: overlay;
overflow-y: visible; overflow-y: visible;
} }
code {
// deliberate constants as we're behind an invert filter
background-color: #f8f8f8;
}
} }
.mx_EventTile_lineNumbers { .mx_EventTile_lineNumbers {

View File

@ -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;
}
}

View File

@ -33,38 +33,29 @@ limitations under the License.
.mx_LinkPreviewWidget_caption { .mx_LinkPreviewWidget_caption {
margin-left: 15px; margin-left: 15px;
flex: 1 1 auto; flex: 1 1 auto;
overflow-x: hidden; // cause it to wrap rather than clip
} }
.mx_LinkPreviewWidget_title { .mx_LinkPreviewWidget_title {
display: inline;
font-weight: bold; font-weight: bold;
white-space: normal; white-space: normal;
} display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
.mx_LinkPreviewWidget_siteName { .mx_LinkPreviewWidget_siteName {
display: inline; font-weight: normal;
}
} }
.mx_LinkPreviewWidget_description { .mx_LinkPreviewWidget_description {
margin-top: 8px; margin-top: 8px;
white-space: normal; white-space: normal;
word-wrap: break-word; word-wrap: break-word;
} display: -webkit-box;
-webkit-line-clamp: 3;
.mx_LinkPreviewWidget_cancel { -webkit-box-orient: vertical;
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;
} }
.mx_MatrixChat_useCompactLayout { .mx_MatrixChat_useCompactLayout {

View File

@ -30,8 +30,8 @@ limitations under the License.
pointer-events: initial; // restore pointer events so the user can leave/interact pointer-events: initial; // restore pointer events so the user can leave/interact
cursor: pointer; cursor: pointer;
.mx_CallView_video { .mx_VideoFeed_remote.mx_VideoFeed_voice {
width: 350px; min-height: 150px;
} }
.mx_VideoFeed_local { .mx_VideoFeed_local {

View File

@ -0,0 +1,21 @@
/*
Copyright 2021 Šimon Brandner <simon.bra.ag@gmail.com>
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_CallPreview {
position: fixed;
left: 0;
top: 0;
}

View File

@ -39,7 +39,6 @@ limitations under the License.
.mx_CallView_pip { .mx_CallView_pip {
width: 320px; width: 320px;
padding-bottom: 8px; padding-bottom: 8px;
margin-top: 10px;
background-color: $voipcall-plinth-color; background-color: $voipcall-plinth-color;
box-shadow: 0px 4px 20px rgba(0, 0, 0, 0.20); box-shadow: 0px 4px 20px rgba(0, 0, 0, 0.20);
border-radius: 8px; border-radius: 8px;

View File

@ -15,8 +15,6 @@ limitations under the License.
*/ */
.mx_VideoFeed_voice { .mx_VideoFeed_voice {
// We don't want to collide with the call controls that have 52px of height
padding-bottom: 52px;
background-color: $inverted-bg-color; background-color: $inverted-bg-color;
} }

View File

@ -119,8 +119,6 @@ $voipcall-plinth-color: #394049;
$theme-button-bg-color: #e3e8f0; $theme-button-bg-color: #e3e8f0;
$dialpad-button-bg-color: #6F7882; $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-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; $roomlist-filter-active-bg-color: $bg-color;
@ -281,24 +279,7 @@ $eventbubble-reply-color: #C1C6CD;
} }
// markdown overrides: // 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 { .mx_EventTile_content .markdown-body {
pre, code {
filter: invert(1);
}
pre code {
filter: none;
}
table { table {
tr { tr {
background-color: #000000; background-color: #000000;
@ -308,18 +289,9 @@ $eventbubble-reply-color: #C1C6CD;
background-color: #080808; background-color: #080808;
} }
} }
blockquote {
color: #919191;
}
} }
// diff highlight colors // highlight.js overrides
// intentionally swapped to avoid inversion .hljs-tag {
.hljs-addition { color: inherit; // Without this they'd be weirdly blue which doesn't match the theme
background: #fdd;
}
.hljs-deletion {
background: #dfd;
} }

View File

@ -9,3 +9,4 @@
@import "_dark.scss"; @import "_dark.scss";
@import "../../light/css/_mods.scss"; @import "../../light/css/_mods.scss";
@import "../../../../res/css/_components.scss"; @import "../../../../res/css/_components.scss";
@import url("highlight.js/styles/atom-one-dark.css");

View File

@ -118,7 +118,7 @@ $voipcall-plinth-color: #394049;
$theme-button-bg-color: #e3e8f0; $theme-button-bg-color: #e3e8f0;
$dialpad-button-bg-color: #6F7882; $dialpad-button-bg-color: #6F7882;
;
$roomlist-button-bg-color: #1A1D23; // Buttons include the filter box, explore button, and sublist buttons $roomlist-button-bg-color: #1A1D23; // Buttons include the filter box, explore button, and sublist buttons
$roomlist-filter-active-bg-color: $roomlist-button-bg-color; $roomlist-filter-active-bg-color: $roomlist-button-bg-color;
@ -249,7 +249,7 @@ $composer-shadow-color: tranparent;
@define-mixin mx_DialogButton_secondary { @define-mixin mx_DialogButton_secondary {
// flip colours for the secondary ones // flip colours for the secondary ones
font-weight: 600; font-weight: 600;
border: 1px solid $accent-color ! important; border: 1px solid $accent-color !important;
color: $accent-color; color: $accent-color;
background-color: $button-secondary-bg-color; background-color: $button-secondary-bg-color;
} }
@ -267,18 +267,7 @@ $composer-shadow-color: tranparent;
} }
// markdown overrides: // markdown overrides:
.mx_EventTile_content .markdown-body pre:hover {
border-color: #808080 !important; // inverted due to rules below
}
.mx_EventTile_content .markdown-body { .mx_EventTile_content .markdown-body {
pre, code {
filter: invert(1);
}
pre code {
filter: none;
}
table { table {
tr { tr {
background-color: #000000; background-color: #000000;
@ -290,12 +279,7 @@ $composer-shadow-color: tranparent;
} }
} }
// diff highlight colors // highlight.js overrides:
// intentionally swapped to avoid inversion .hljs-tag {
.hljs-addition { color: inherit; // Without this they'd be weirdly blue which doesn't match the theme
background: #fdd;
}
.hljs-deletion {
background: #dfd;
} }

View File

@ -4,3 +4,4 @@
@import "../../legacy-light/css/_legacy-light.scss"; @import "../../legacy-light/css/_legacy-light.scss";
@import "_legacy-dark.scss"; @import "_legacy-dark.scss";
@import "../../../../res/css/_components.scss"; @import "../../../../res/css/_components.scss";
@import url("highlight.js/styles/atom-one-dark.css");

View File

@ -3,3 +3,4 @@
@import "_fonts.scss"; @import "_fonts.scss";
@import "_legacy-light.scss"; @import "_legacy-light.scss";
@import "../../../../res/css/_components.scss"; @import "../../../../res/css/_components.scss";
@import url("highlight.js/styles/atom-one-light.css");

View File

@ -4,3 +4,4 @@
@import "_light.scss"; @import "_light.scss";
@import "_mods.scss"; @import "_mods.scss";
@import "../../../../res/css/_components.scss"; @import "../../../../res/css/_components.scss";
@import url("highlight.js/styles/atom-one-light.css");

View File

@ -6,8 +6,8 @@ scripts/fetchdep.sh matrix-org matrix-js-sdk
pushd matrix-js-sdk pushd matrix-js-sdk
yarn link yarn link
yarn install $@ yarn install --pure-lockfile $@
popd popd
yarn link matrix-js-sdk yarn link matrix-js-sdk
yarn install $@ yarn install --pure-lockfile $@

View File

@ -13,13 +13,13 @@
scripts/fetchdep.sh matrix-org matrix-js-sdk scripts/fetchdep.sh matrix-org matrix-js-sdk
pushd matrix-js-sdk pushd matrix-js-sdk
yarn link yarn link
yarn install yarn install --pure-lockfile
popd popd
# Now set up the react-sdk # Now set up the react-sdk
yarn link matrix-js-sdk yarn link matrix-js-sdk
yarn link yarn link
yarn install yarn install --pure-lockfile
yarn reskindex yarn reskindex
# Finally, set up element-web # Finally, set up element-web
@ -27,6 +27,6 @@ scripts/fetchdep.sh vector-im element-web
pushd element-web pushd element-web
yarn link matrix-js-sdk yarn link matrix-js-sdk
yarn link matrix-react-sdk yarn link matrix-react-sdk
yarn install yarn install --pure-lockfile
yarn build:res yarn build:res
popd popd

View File

@ -15,7 +15,9 @@ limitations under the License.
*/ */
import "matrix-js-sdk/src/@types/global"; // load matrix-js-sdk's type extensions first import "matrix-js-sdk/src/@types/global"; // load matrix-js-sdk's type extensions first
import * as ModernizrStatic from "modernizr"; // Load types for the WG CSS Font Loading APIs https://github.com/Microsoft/TypeScript/issues/13569
import "@types/css-font-loading-module";
import "@types/modernizr";
import ContentMessages from "../ContentMessages"; import ContentMessages from "../ContentMessages";
import { IMatrixClientPeg } from "../MatrixClientPeg"; import { IMatrixClientPeg } from "../MatrixClientPeg";
@ -50,7 +52,6 @@ import { RoomScrollStateStore } from "../stores/RoomScrollStateStore";
declare global { declare global {
interface Window { interface Window {
Modernizr: ModernizrStatic;
matrixChat: ReturnType<Renderer>; matrixChat: ReturnType<Renderer>;
mxMatrixClientPeg: IMatrixClientPeg; mxMatrixClientPeg: IMatrixClientPeg;
Olm: { Olm: {

View File

@ -390,6 +390,7 @@ export class Analytics {
{ expl: _td('Your device resolution'), value: resolution }, { expl: _td('Your device resolution'), value: resolution },
]; ];
// FIXME: Using an import will result in test failures
const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog'); const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog');
Modal.createTrackedDialog('Analytics Details', '', ErrorDialog, { Modal.createTrackedDialog('Analytics Details', '', ErrorDialog, {
title: _t('Analytics'), title: _t('Analytics'),

View File

@ -77,6 +77,7 @@ export default class AsyncWrapper extends React.Component<IProps, IState> {
const Component = this.state.component; const Component = this.state.component;
return <Component {...this.props} />; return <Component {...this.props} />;
} else if (this.state.error) { } else if (this.state.error) {
// FIXME: Using an import will result in test failures
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
return <BaseDialog onFinished={this.props.onFinished} title={_t("Error")}> return <BaseDialog onFinished={this.props.onFinished} title={_t("Error")}>

View File

@ -49,8 +49,6 @@ const MAX_HEIGHT = 600;
const PHYS_HIDPI = [0x00, 0x00, 0x16, 0x25, 0x00, 0x00, 0x16, 0x25, 0x01]; const PHYS_HIDPI = [0x00, 0x00, 0x16, 0x25, 0x00, 0x00, 0x16, 0x25, 0x01];
export const BLURHASH_FIELD = "xyz.amorgan.blurhash"; // MSC2448 export const BLURHASH_FIELD = "xyz.amorgan.blurhash"; // MSC2448
const BLURHASH_X_COMPONENTS = 6;
const BLURHASH_Y_COMPONENTS = 6;
export class UploadCanceledError extends Error {} export class UploadCanceledError extends Error {}
@ -137,8 +135,9 @@ function createThumbnail(
imageData.data, imageData.data,
imageData.width, imageData.width,
imageData.height, imageData.height,
BLURHASH_X_COMPONENTS, // use 4 components on the longer dimension, if square then both
BLURHASH_Y_COMPONENTS, imageData.width >= imageData.height ? 4 : 3,
imageData.height >= imageData.width ? 4 : 3,
); );
canvas.toBlob(function(thumbnail) { canvas.toBlob(function(thumbnail) {
resolve({ resolve({
@ -419,6 +418,7 @@ export default class ContentMessages {
const isQuoting = Boolean(RoomViewStore.getQuotingEvent()); const isQuoting = Boolean(RoomViewStore.getQuotingEvent());
if (isQuoting) { if (isQuoting) {
// FIXME: Using an import will result in Element crashing
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
const { finished } = Modal.createTrackedDialog<[boolean]>('Upload Reply Warning', '', QuestionDialog, { const { finished } = Modal.createTrackedDialog<[boolean]>('Upload Reply Warning', '', QuestionDialog, {
title: _t('Replying With Files'), title: _t('Replying With Files'),
@ -453,6 +453,7 @@ export default class ContentMessages {
} }
if (tooBigFiles.length > 0) { if (tooBigFiles.length > 0) {
// FIXME: Using an import will result in Element crashing
const UploadFailureDialog = sdk.getComponent("dialogs.UploadFailureDialog"); const UploadFailureDialog = sdk.getComponent("dialogs.UploadFailureDialog");
const { finished } = Modal.createTrackedDialog<[boolean]>('Upload Failure', '', UploadFailureDialog, { const { finished } = Modal.createTrackedDialog<[boolean]>('Upload Failure', '', UploadFailureDialog, {
badFiles: tooBigFiles, badFiles: tooBigFiles,
@ -463,7 +464,6 @@ export default class ContentMessages {
if (!shouldContinue) return; if (!shouldContinue) return;
} }
const UploadConfirmDialog = sdk.getComponent("dialogs.UploadConfirmDialog");
let uploadAll = false; let uploadAll = false;
// Promise to complete before sending next file into room, used for synchronisation of file-sending // Promise to complete before sending next file into room, used for synchronisation of file-sending
// to match the order the files were specified in // 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) { for (let i = 0; i < okFiles.length; ++i) {
const file = okFiles[i]; const file = okFiles[i];
if (!uploadAll) { 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', const { finished } = Modal.createTrackedDialog<[boolean, boolean]>('Upload Files confirmation',
'', UploadConfirmDialog, { '', UploadConfirmDialog, {
file, file,
@ -567,7 +569,7 @@ export default class ContentMessages {
dis.dispatch<UploadStartedPayload>({ action: Action.UploadStarted, upload }); dis.dispatch<UploadStartedPayload>({ action: Action.UploadStarted, upload });
// Focus the composer view // Focus the composer view
dis.fire(Action.FocusComposer); dis.fire(Action.FocusSendMessageComposer);
function onProgress(ev) { function onProgress(ev) {
upload.total = ev.total; upload.total = ev.total;
@ -606,6 +608,7 @@ export default class ContentMessages {
{ fileName: upload.fileName }, { fileName: upload.fileName },
); );
} }
// FIXME: Using an import will result in Element crashing
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog('Upload failed', '', ErrorDialog, { Modal.createTrackedDialog('Upload failed', '', ErrorDialog, {
title: _t('Upload Failed'), title: _t('Upload Failed'),

View File

@ -160,7 +160,8 @@ export default class DeviceListener {
// which result in account data changes affecting checks below. // which result in account data changes affecting checks below.
if ( if (
ev.getType().startsWith('m.secret_storage.') || 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(); this._recheck();
} }

View File

@ -60,6 +60,8 @@ const COLOR_REGEX = /^#[0-9a-fA-F]{6}$/;
export const PERMITTED_URL_SCHEMES = ['http', 'https', 'ftp', 'mailto', 'magnet']; export const PERMITTED_URL_SCHEMES = ['http', 'https', 'ftp', 'mailto', 'magnet'];
const MEDIA_API_MXC_REGEX = /\/_matrix\/media\/r0\/(?:download|thumbnail)\/(.+?)\/(.+?)(?:[?/]|$)/;
/* /*
* Return true if the given string contains emoji * Return true if the given string contains emoji
* Uses a much, much simpler regex than emojibase's so will give false * Uses a much, much simpler regex than emojibase's so will give false
@ -176,18 +178,31 @@ const transformTags: IExtendedSanitizeOptions["transformTags"] = { // custom to
return { tagName, attribs }; return { tagName, attribs };
}, },
'img': function(tagName: string, attribs: sanitizeHtml.Attributes) { 'img': function(tagName: string, attribs: sanitizeHtml.Attributes) {
let src = attribs.src;
// Strip out imgs that aren't `mxc` here instead of using allowedSchemesByTag // Strip out imgs that aren't `mxc` here instead of using allowedSchemesByTag
// because transformTags is used _before_ we filter by allowedSchemesByTag and // because transformTags is used _before_ we filter by allowedSchemesByTag and
// we don't want to allow images with `https?` `src`s. // we don't want to allow images with `https?` `src`s.
// We also drop inline images (as if they were not present at all) when the "show // We also drop inline images (as if they were not present at all) when the "show
// images" preference is disabled. Future work might expose some UI to reveal them // images" preference is disabled. Future work might expose some UI to reveal them
// like standalone image events have. // like standalone image events have.
if (!attribs.src || !attribs.src.startsWith('mxc://') || !SettingsStore.getValue("showImages")) { if (!src || !SettingsStore.getValue("showImages")) {
return { tagName, attribs: {} }; return { tagName, attribs: {} };
} }
if (!src.startsWith("mxc://")) {
const match = MEDIA_API_MXC_REGEX.exec(src);
if (match) {
src = `mxc://${match[1]}/${match[2]}`;
}
}
if (!src.startsWith("mxc://")) {
return { tagName, attribs: {} };
}
const width = Number(attribs.width) || 800; const width = Number(attribs.width) || 800;
const height = Number(attribs.height) || 600; const height = Number(attribs.height) || 600;
attribs.src = mediaFromMxc(attribs.src).getThumbnailOfSourceHttp(width, height); attribs.src = mediaFromMxc(src).getThumbnailOfSourceHttp(width, height);
return { tagName, attribs }; return { tagName, attribs };
}, },
'code': function(tagName: string, attribs: sanitizeHtml.Attributes) { 'code': function(tagName: string, attribs: sanitizeHtml.Attributes) {

View File

@ -33,7 +33,6 @@ import Presence from './Presence';
import dis from './dispatcher/dispatcher'; import dis from './dispatcher/dispatcher';
import DMRoomMap from './utils/DMRoomMap'; import DMRoomMap from './utils/DMRoomMap';
import Modal from './Modal'; import Modal from './Modal';
import * as sdk from './index';
import ActiveWidgetStore from './stores/ActiveWidgetStore'; import ActiveWidgetStore from './stores/ActiveWidgetStore';
import PlatformPeg from "./PlatformPeg"; import PlatformPeg from "./PlatformPeg";
import { sendLoginRequest } from "./Login"; import { sendLoginRequest } from "./Login";
@ -52,6 +51,10 @@ import CallHandler from './CallHandler';
import LifecycleCustomisations from "./customisations/Lifecycle"; import LifecycleCustomisations from "./customisations/Lifecycle";
import ErrorDialog from "./components/views/dialogs/ErrorDialog"; import ErrorDialog from "./components/views/dialogs/ErrorDialog";
import { _t } from "./languageHandler"; import { _t } from "./languageHandler";
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 HOMESERVER_URL_KEY = "mx_hs_url";
const ID_SERVER_URL_KEY = "mx_is_url"; const ID_SERVER_URL_KEY = "mx_is_url";
@ -238,8 +241,6 @@ export function handleInvalidStoreError(e: InvalidStoreError): Promise<void> {
return Promise.resolve().then(() => { return Promise.resolve().then(() => {
const lazyLoadEnabled = e.value; const lazyLoadEnabled = e.value;
if (lazyLoadEnabled) { if (lazyLoadEnabled) {
const LazyLoadingResyncDialog =
sdk.getComponent("views.dialogs.LazyLoadingResyncDialog");
return new Promise((resolve) => { return new Promise((resolve) => {
Modal.createDialog(LazyLoadingResyncDialog, { Modal.createDialog(LazyLoadingResyncDialog, {
onFinished: resolve, onFinished: resolve,
@ -250,8 +251,6 @@ export function handleInvalidStoreError(e: InvalidStoreError): Promise<void> {
// between LL/non-LL version on same host. // between LL/non-LL version on same host.
// as disabling LL when previously enabled // as disabling LL when previously enabled
// is a strong indicator of this (/develop & /app) // is a strong indicator of this (/develop & /app)
const LazyLoadingDisabledDialog =
sdk.getComponent("views.dialogs.LazyLoadingDisabledDialog");
return new Promise((resolve) => { return new Promise((resolve) => {
Modal.createDialog(LazyLoadingDisabledDialog, { Modal.createDialog(LazyLoadingDisabledDialog, {
onFinished: resolve, onFinished: resolve,
@ -451,9 +450,6 @@ export async function restoreFromLocalStorage(opts?: { ignoreGuest?: boolean }):
async function handleLoadSessionFailure(e: Error): Promise<boolean> { async function handleLoadSessionFailure(e: Error): Promise<boolean> {
console.error("Unable to load session", e); console.error("Unable to load session", e);
const SessionRestoreErrorDialog =
sdk.getComponent('views.dialogs.SessionRestoreErrorDialog');
const modal = Modal.createTrackedDialog('Session Restore Error', '', SessionRestoreErrorDialog, { const modal = Modal.createTrackedDialog('Session Restore Error', '', SessionRestoreErrorDialog, {
error: e.message, error: e.message,
}); });
@ -612,7 +608,6 @@ async function doSetLoggedIn(
} }
function showStorageEvictedDialog(): Promise<boolean> { function showStorageEvictedDialog(): Promise<boolean> {
const StorageEvictedDialog = sdk.getComponent('views.dialogs.StorageEvictedDialog');
return new Promise(resolve => { return new Promise(resolve => {
Modal.createTrackedDialog('Storage evicted', '', StorageEvictedDialog, { Modal.createTrackedDialog('Storage evicted', '', StorageEvictedDialog, {
onFinished: resolve, onFinished: resolve,

View File

@ -219,6 +219,7 @@ class _MatrixClientPeg implements IMatrixClientPeg {
} catch (e) { } catch (e) {
if (e && e.name === 'InvalidCryptoStoreError') { if (e && e.name === 'InvalidCryptoStoreError') {
// The js-sdk found a crypto DB too new for it to use // The js-sdk found a crypto DB too new for it to use
// FIXME: Using an import will result in test failures
const CryptoStoreTooNewDialog = const CryptoStoreTooNewDialog =
sdk.getComponent("views.dialogs.CryptoStoreTooNewDialog"); sdk.getComponent("views.dialogs.CryptoStoreTooNewDialog");
Modal.createDialog(CryptoStoreTooNewDialog); Modal.createDialog(CryptoStoreTooNewDialog);

View File

@ -20,12 +20,15 @@ import { SettingLevel } from "./settings/SettingLevel";
import { setMatrixCallAudioInput, setMatrixCallVideoInput } from "matrix-js-sdk/src/matrix"; import { setMatrixCallAudioInput, setMatrixCallVideoInput } from "matrix-js-sdk/src/matrix";
import EventEmitter from 'events'; import EventEmitter from 'events';
interface IMediaDevices { // XXX: MediaDeviceKind is a union type, so we make our own enum
audioOutput: Array<MediaDeviceInfo>; export enum MediaDeviceKindEnum {
audioInput: Array<MediaDeviceInfo>; AudioOutput = "audiooutput",
videoInput: Array<MediaDeviceInfo>; AudioInput = "audioinput",
VideoInput = "videoinput",
} }
export type IMediaDevices = Record<MediaDeviceKindEnum, Array<MediaDeviceInfo>>;
export enum MediaDeviceHandlerEvent { export enum MediaDeviceHandlerEvent {
AudioOutputChanged = "audio_output_changed", AudioOutputChanged = "audio_output_changed",
} }
@ -51,20 +54,14 @@ export default class MediaDeviceHandler extends EventEmitter {
try { try {
const devices = await navigator.mediaDevices.enumerateDevices(); const devices = await navigator.mediaDevices.enumerateDevices();
const output = {
[MediaDeviceKindEnum.AudioOutput]: [],
[MediaDeviceKindEnum.AudioInput]: [],
[MediaDeviceKindEnum.VideoInput]: [],
};
const audioOutput = []; devices.forEach((device) => output[device.kind].push(device));
const audioInput = []; return output;
const videoInput = [];
devices.forEach((device) => {
switch (device.kind) {
case 'audiooutput': audioOutput.push(device); break;
case 'audioinput': audioInput.push(device); break;
case 'videoinput': videoInput.push(device); break;
}
});
return { audioOutput, audioInput, videoInput };
} catch (error) { } catch (error) {
console.warn('Unable to refresh WebRTC Devices: ', error); console.warn('Unable to refresh WebRTC Devices: ', error);
} }
@ -106,6 +103,14 @@ export default class MediaDeviceHandler extends EventEmitter {
setMatrixCallVideoInput(deviceId); setMatrixCallVideoInput(deviceId);
} }
public setDevice(deviceId: string, kind: MediaDeviceKindEnum): void {
switch (kind) {
case MediaDeviceKindEnum.AudioOutput: this.setAudioOutput(deviceId); break;
case MediaDeviceKindEnum.AudioInput: this.setAudioInput(deviceId); break;
case MediaDeviceKindEnum.VideoInput: this.setVideoInput(deviceId); break;
}
}
public static getAudioOutput(): string { public static getAudioOutput(): string {
return SettingsStore.getValueAt(SettingLevel.DEVICE, "webrtc_audiooutput"); return SettingsStore.getValueAt(SettingLevel.DEVICE, "webrtc_audiooutput");
} }

View File

@ -27,7 +27,6 @@ import * as TextForEvent from './TextForEvent';
import Analytics from './Analytics'; import Analytics from './Analytics';
import * as Avatar from './Avatar'; import * as Avatar from './Avatar';
import dis from './dispatcher/dispatcher'; import dis from './dispatcher/dispatcher';
import * as sdk from './index';
import { _t } from './languageHandler'; import { _t } from './languageHandler';
import Modal from './Modal'; import Modal from './Modal';
import SettingsStore from "./settings/SettingsStore"; import SettingsStore from "./settings/SettingsStore";
@ -37,6 +36,7 @@ import { isPushNotifyDisabled } from "./settings/controllers/NotificationControl
import RoomViewStore from "./stores/RoomViewStore"; import RoomViewStore from "./stores/RoomViewStore";
import UserActivity from "./UserActivity"; import UserActivity from "./UserActivity";
import { mediaFromMxc } from "./customisations/Media"; import { mediaFromMxc } from "./customisations/Media";
import ErrorDialog from "./components/views/dialogs/ErrorDialog";
/* /*
* Dispatches: * Dispatches:
@ -240,7 +240,6 @@ export const Notifier = {
? _t('%(brand)s does not have permission to send you notifications - ' + ? _t('%(brand)s does not have permission to send you notifications - ' +
'please check your browser settings', { brand }) 'please check your browser settings', { brand })
: _t('%(brand)s was not given permission to send notifications - please try again', { 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, { Modal.createTrackedDialog('Unable to enable Notifications', result, ErrorDialog, {
title: _t('Unable to enable Notifications'), title: _t('Unable to enable Notifications'),
description, description,

View File

@ -22,13 +22,13 @@ import { User } from "matrix-js-sdk/src/models/user";
import { MatrixClientPeg } from './MatrixClientPeg'; import { MatrixClientPeg } from './MatrixClientPeg';
import MultiInviter, { CompletionStates } from './utils/MultiInviter'; import MultiInviter, { CompletionStates } from './utils/MultiInviter';
import Modal from './Modal'; import Modal from './Modal';
import * as sdk from './';
import { _t } from './languageHandler'; import { _t } from './languageHandler';
import InviteDialog, { KIND_DM, KIND_INVITE, Member } from "./components/views/dialogs/InviteDialog"; import InviteDialog, { KIND_DM, KIND_INVITE, Member } from "./components/views/dialogs/InviteDialog";
import CommunityPrototypeInviteDialog from "./components/views/dialogs/CommunityPrototypeInviteDialog"; import CommunityPrototypeInviteDialog from "./components/views/dialogs/CommunityPrototypeInviteDialog";
import { CommunityPrototypeStore } from "./stores/CommunityPrototypeStore"; import { CommunityPrototypeStore } from "./stores/CommunityPrototypeStore";
import BaseAvatar from "./components/views/avatars/BaseAvatar"; import BaseAvatar from "./components/views/avatars/BaseAvatar";
import { mediaFromMxc } from "./customisations/Media"; import { mediaFromMxc } from "./customisations/Media";
import ErrorDialog from "./components/views/dialogs/ErrorDialog";
export interface IInviteResult { export interface IInviteResult {
states: CompletionStates; states: CompletionStates;
@ -51,7 +51,6 @@ export function inviteMultipleToRoom(roomId: string, addresses: string[]): Promi
export function showStartChatInviteDialog(initialText = ""): void { export function showStartChatInviteDialog(initialText = ""): void {
// This dialog handles the room creation internally - we don't need to worry about it. // This dialog handles the room creation internally - we don't need to worry about it.
const InviteDialog = sdk.getComponent("dialogs.InviteDialog");
Modal.createTrackedDialog( Modal.createTrackedDialog(
'Start DM', '', InviteDialog, { kind: KIND_DM, initialText }, 'Start DM', '', InviteDialog, { kind: KIND_DM, initialText },
/*className=*/null, /*isPriority=*/false, /*isStatic=*/true, /*className=*/null, /*isPriority=*/false, /*isStatic=*/true,
@ -111,7 +110,6 @@ export function inviteUsersToRoom(roomId: string, userIds: string[]): Promise<vo
showAnyInviteErrors(result.states, room, result.inviter); showAnyInviteErrors(result.states, room, result.inviter);
}).catch((err) => { }).catch((err) => {
console.error(err.stack); console.error(err.stack);
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog('Failed to invite', '', ErrorDialog, { Modal.createTrackedDialog('Failed to invite', '', ErrorDialog, {
title: _t("Failed to invite"), title: _t("Failed to invite"),
description: ((err && err.message) ? err.message : _t("Operation failed")), 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 // 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 // user. This usually means that no other users were attempted, making it
// pointless for us to list who failed exactly. // pointless for us to list who failed exactly.
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog('Failed to invite users to the room', '', ErrorDialog, { Modal.createTrackedDialog('Failed to invite users to the room', '', ErrorDialog, {
title: _t("Failed to invite users to the room:", { roomName: room.name }), title: _t("Failed to invite users to the room:", { roomName: room.name }),
description: inviter.getErrorText(failedUsers[0]), description: inviter.getErrorText(failedUsers[0]),
@ -178,7 +175,6 @@ export function showAnyInviteErrors(
</div> </div>
</div>; </div>;
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog("Some invites could not be sent", "", ErrorDialog, { Modal.createTrackedDialog("Some invites could not be sent", "", ErrorDialog, {
title: _t("Some invites couldn't be sent"), title: _t("Some invites couldn't be sent"),
description, description,

View File

@ -17,6 +17,7 @@ limitations under the License.
import { Room } from "matrix-js-sdk/src/models/room"; import { Room } from "matrix-js-sdk/src/models/room";
import { MatrixClientPeg } from './MatrixClientPeg'; import { MatrixClientPeg } from './MatrixClientPeg';
import AliasCustomisations from './customisations/Alias';
/** /**
* Given a room object, return the alias we should use for it, * Given a room object, return the alias we should use for it,
@ -28,7 +29,18 @@ import { MatrixClientPeg } from './MatrixClientPeg';
* @returns {string} A display alias for the given room * @returns {string} A display alias for the given room
*/ */
export function getDisplayAliasForRoom(room: Room): string { export function getDisplayAliasForRoom(room: Room): string {
return room.getCanonicalAlias() || room.getAltAliases()[0]; return getDisplayAliasForAliasSet(
room.getCanonicalAlias(), room.getAltAliases(),
);
}
// The various display alias getters should all feed through this one path so
// there's a single place to change the logic.
export function getDisplayAliasForAliasSet(canonicalAlias: string, altAliases: string[]): string {
if (AliasCustomisations.getDisplayAliasForAliasSet) {
return AliasCustomisations.getDisplayAliasForAliasSet(canonicalAlias, altAliases);
}
return canonicalAlias || altAliases?.[0];
} }
export function looksLikeDirectMessageRoom(room: Room, myUserId: string): boolean { export function looksLikeDirectMessageRoom(room: Room, myUserId: string): boolean {

View File

@ -14,7 +14,8 @@ See the License for the specific language governing permissions and
limitations under the License. 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 { MatrixClient } from 'matrix-js-sdk/src/client';
import Modal from './Modal'; import Modal from './Modal';
import * as sdk from './index'; import * as sdk from './index';
@ -354,6 +355,7 @@ export async function accessSecretStorage(func = async () => { }, forceReset = f
throw new Error("Secret storage creation canceled"); throw new Error("Secret storage creation canceled");
} }
} else { } else {
// FIXME: Using an import will result in test failures
const InteractiveAuthDialog = sdk.getComponent("dialogs.InteractiveAuthDialog"); const InteractiveAuthDialog = sdk.getComponent("dialogs.InteractiveAuthDialog");
await cli.bootstrapCrossSigning({ await cli.bootstrapCrossSigning({
authUploadDeviceSigningKeys: async (makeRequest) => { authUploadDeviceSigningKeys: async (makeRequest) => {

View File

@ -23,7 +23,6 @@ import { User } from "matrix-js-sdk/src/models/user";
import * as ContentHelpers from 'matrix-js-sdk/src/content-helpers'; import * as ContentHelpers from 'matrix-js-sdk/src/content-helpers';
import { MatrixClientPeg } from './MatrixClientPeg'; import { MatrixClientPeg } from './MatrixClientPeg';
import dis from './dispatcher/dispatcher'; import dis from './dispatcher/dispatcher';
import * as sdk from './index';
import { _t, _td } from './languageHandler'; import { _t, _td } from './languageHandler';
import Modal from './Modal'; import Modal from './Modal';
import MultiInviter from './utils/MultiInviter'; import MultiInviter from './utils/MultiInviter';
@ -50,6 +49,12 @@ import { UIFeature } from "./settings/UIFeature";
import { CHAT_EFFECTS } from "./effects"; import { CHAT_EFFECTS } from "./effects";
import CallHandler from "./CallHandler"; import CallHandler from "./CallHandler";
import { guessAndSetDMRoom } from "./Rooms"; import { guessAndSetDMRoom } from "./Rooms";
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 // XXX: workaround for https://github.com/microsoft/TypeScript/issues/31816
interface HTMLInputEvent extends Event { interface HTMLInputEvent extends Event {
@ -63,7 +68,6 @@ const singleMxcUpload = async (): Promise<any> => {
fileSelector.onchange = (ev: HTMLInputEvent) => { fileSelector.onchange = (ev: HTMLInputEvent) => {
const file = ev.target.files[0]; const file = ev.target.files[0];
const UploadConfirmDialog = sdk.getComponent("dialogs.UploadConfirmDialog");
Modal.createTrackedDialog('Upload Files confirmation', '', UploadConfirmDialog, { Modal.createTrackedDialog('Upload Files confirmation', '', UploadConfirmDialog, {
file, file,
onFinished: (shouldContinue) => { onFinished: (shouldContinue) => {
@ -246,7 +250,6 @@ export const Commands = [
args: '<query>', args: '<query>',
description: _td('Searches DuckDuckGo for results'), description: _td('Searches DuckDuckGo for results'),
runFn: function() { runFn: function() {
const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog');
// TODO Don't explain this away, actually show a search UI here. // TODO Don't explain this away, actually show a search UI here.
Modal.createTrackedDialog('Slash Commands', '/ddg is not a command', ErrorDialog, { Modal.createTrackedDialog('Slash Commands', '/ddg is not a command', ErrorDialog, {
title: _t('/ddg is not a command'), 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.")); 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', const { finished } = Modal.createTrackedDialog('Slash Commands', 'upgrade room confirmation',
RoomUpgradeWarningDialog, { roomId: roomId, targetVersion: args }, /*className=*/null, RoomUpgradeWarningDialog, { roomId: roomId, targetVersion: args }, /*className=*/null,
/*isPriority=*/false, /*isStatic=*/true); /*isPriority=*/false, /*isStatic=*/true);
@ -314,7 +315,6 @@ export const Commands = [
if (checkForUpgradeFn) cli.removeListener('Room', checkForUpgradeFn); if (checkForUpgradeFn) cli.removeListener('Room', checkForUpgradeFn);
const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog');
Modal.createTrackedDialog('Slash Commands', 'room upgrade error', ErrorDialog, { Modal.createTrackedDialog('Slash Commands', 'room upgrade error', ErrorDialog, {
title: _t('Error upgrading room'), title: _t('Error upgrading room'),
description: _t( description: _t(
@ -434,7 +434,6 @@ export const Commands = [
const topic = topicEvents && topicEvents.getContent().topic; const topic = topicEvents && topicEvents.getContent().topic;
const topicHtml = topic ? linkifyAndSanitizeHtml(topic) : _t('This room has no topic.'); const topicHtml = topic ? linkifyAndSanitizeHtml(topic) : _t('This room has no topic.');
const InfoDialog = sdk.getComponent('dialogs.InfoDialog');
Modal.createTrackedDialog('Slash Commands', 'Topic', InfoDialog, { Modal.createTrackedDialog('Slash Commands', 'Topic', InfoDialog, {
title: room.name, title: room.name,
description: <div dangerouslySetInnerHTML={{ __html: topicHtml }} />, description: <div dangerouslySetInnerHTML={{ __html: topicHtml }} />,
@ -737,7 +736,6 @@ export const Commands = [
ignoredUsers.push(userId); // de-duped internally in the js-sdk ignoredUsers.push(userId); // de-duped internally in the js-sdk
return success( return success(
cli.setIgnoredUsers(ignoredUsers).then(() => { cli.setIgnoredUsers(ignoredUsers).then(() => {
const InfoDialog = sdk.getComponent('dialogs.InfoDialog');
Modal.createTrackedDialog('Slash Commands', 'User ignored', InfoDialog, { Modal.createTrackedDialog('Slash Commands', 'User ignored', InfoDialog, {
title: _t('Ignored user'), title: _t('Ignored user'),
description: <div> description: <div>
@ -768,7 +766,6 @@ export const Commands = [
if (index !== -1) ignoredUsers.splice(index, 1); if (index !== -1) ignoredUsers.splice(index, 1);
return success( return success(
cli.setIgnoredUsers(ignoredUsers).then(() => { cli.setIgnoredUsers(ignoredUsers).then(() => {
const InfoDialog = sdk.getComponent('dialogs.InfoDialog');
Modal.createTrackedDialog('Slash Commands', 'User unignored', InfoDialog, { Modal.createTrackedDialog('Slash Commands', 'User unignored', InfoDialog, {
title: _t('Unignored user'), title: _t('Unignored user'),
description: <div> description: <div>
@ -838,7 +835,6 @@ export const Commands = [
command: 'devtools', command: 'devtools',
description: _td('Opens the Developer Tools dialog'), description: _td('Opens the Developer Tools dialog'),
runFn: function(roomId) { runFn: function(roomId) {
const DevtoolsDialog = sdk.getComponent('dialogs.DevtoolsDialog');
Modal.createDialog(DevtoolsDialog, { roomId }); Modal.createDialog(DevtoolsDialog, { roomId });
return success(); return success();
}, },
@ -943,7 +939,6 @@ export const Commands = [
await cli.setDeviceVerified(userId, deviceId, true); await cli.setDeviceVerified(userId, deviceId, true);
// Tell the user we verified everything // Tell the user we verified everything
const InfoDialog = sdk.getComponent('dialogs.InfoDialog');
Modal.createTrackedDialog('Slash Commands', 'Verified key', InfoDialog, { Modal.createTrackedDialog('Slash Commands', 'Verified key', InfoDialog, {
title: _t('Verified key'), title: _t('Verified key'),
description: <div> description: <div>
@ -1000,8 +995,6 @@ export const Commands = [
command: "help", command: "help",
description: _td("Displays list of commands with usages and descriptions"), description: _td("Displays list of commands with usages and descriptions"),
runFn: function() { runFn: function() {
const SlashCommandHelpDialog = sdk.getComponent('dialogs.SlashCommandHelpDialog');
Modal.createTrackedDialog('Slash Commands', 'Help', SlashCommandHelpDialog); Modal.createTrackedDialog('Slash Commands', 'Help', SlashCommandHelpDialog);
return success(); return success();
}, },

View File

@ -189,6 +189,7 @@ export function dialogTermsInteractionCallback(
): Promise<string[]> { ): Promise<string[]> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
console.log("Terms that need agreement", policiesAndServicePairs); console.log("Terms that need agreement", policiesAndServicePairs);
// FIXME: Using an import will result in test failures
const TermsDialog = sdk.getComponent("views.dialogs.TermsDialog"); const TermsDialog = sdk.getComponent("views.dialogs.TermsDialog");
Modal.createTrackedDialog('Terms of Service', '', TermsDialog, { Modal.createTrackedDialog('Terms of Service', '', TermsDialog, {

View File

@ -447,7 +447,8 @@ function textForPowerEvent(event): () => string | null {
!event.getContent() || !event.getContent().users) { !event.getContent() || !event.getContent().users) {
return null; return null;
} }
const userDefault = event.getContent().users_default || 0; const previousUserDefault = event.getPrevContent().users_default || 0;
const currentUserDefault = event.getContent().users_default || 0;
// Construct set of userIds // Construct set of userIds
const users = []; const users = [];
Object.keys(event.getContent().users).forEach( Object.keys(event.getContent().users).forEach(
@ -463,9 +464,16 @@ function textForPowerEvent(event): () => string | null {
const diffs = []; const diffs = [];
users.forEach((userId) => { users.forEach((userId) => {
// Previous power level // Previous power level
const from = event.getPrevContent().users[userId]; let from = event.getPrevContent().users[userId];
if (!Number.isInteger(from)) {
from = previousUserDefault;
}
// Current power level // Current power level
const to = event.getContent().users[userId]; let to = event.getContent().users[userId];
if (!Number.isInteger(to)) {
to = currentUserDefault;
}
if (from === previousUserDefault && to === currentUserDefault) { return; }
if (to !== from) { if (to !== from) {
diffs.push({ userId, from, to }); diffs.push({ userId, from, to });
} }
@ -479,8 +487,8 @@ function textForPowerEvent(event): () => string | null {
powerLevelDiffText: diffs.map(diff => powerLevelDiffText: diffs.map(diff =>
_t('%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s', { _t('%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s', {
userId: diff.userId, userId: diff.userId,
fromPowerLevel: Roles.textualPowerLevel(diff.from, userDefault), fromPowerLevel: Roles.textualPowerLevel(diff.from, previousUserDefault),
toPowerLevel: Roles.textualPowerLevel(diff.to, userDefault), toPowerLevel: Roles.textualPowerLevel(diff.to, currentUserDefault),
}), }),
).join(", "), ).join(", "),
}); });

View File

@ -17,10 +17,10 @@ limitations under the License.
import * as React from "react"; import * as React from "react";
import classNames from "classnames"; import classNames from "classnames";
import * as sdk from "../index";
import Modal from "../Modal"; import Modal from "../Modal";
import { _t, _td } from "../languageHandler"; import { _t, _td } from "../languageHandler";
import { isMac, Key } from "../Keyboard"; import { isMac, Key } from "../Keyboard";
import InfoDialog from "../components/views/dialogs/InfoDialog";
// TS: once languageHandler is TS we can probably inline this into the enum // TS: once languageHandler is TS we can probably inline this into the enum
_td("Navigation"); _td("Navigation");
@ -375,7 +375,6 @@ export const toggleDialog = () => {
</div>; </div>;
}); });
const InfoDialog = sdk.getComponent('dialogs.InfoDialog');
activeModal = Modal.createTrackedDialog("Keyboard Shortcuts", "", InfoDialog, { activeModal = Modal.createTrackedDialog("Keyboard Shortcuts", "", InfoDialog, {
className: "mx_KeyboardShortcutsDialog", className: "mx_KeyboardShortcutsDialog",
title: _t("Keyboard Shortcuts"), title: _t("Keyboard Shortcuts"),

View File

@ -19,13 +19,13 @@ import { asyncAction } from './actionCreators';
import Modal from '../Modal'; import Modal from '../Modal';
import * as Rooms from '../Rooms'; import * as Rooms from '../Rooms';
import { _t } from '../languageHandler'; import { _t } from '../languageHandler';
import * as sdk from '../index';
import { MatrixClient } from "matrix-js-sdk/src/client"; import { MatrixClient } from "matrix-js-sdk/src/client";
import { Room } from "matrix-js-sdk/src/models/room"; import { Room } from "matrix-js-sdk/src/models/room";
import { AsyncActionPayload } from "../dispatcher/payloads"; import { AsyncActionPayload } from "../dispatcher/payloads";
import RoomListStore from "../stores/room-list/RoomListStore"; import RoomListStore from "../stores/room-list/RoomListStore";
import { SortAlgorithm } from "../stores/room-list/algorithms/models"; import { SortAlgorithm } from "../stores/room-list/algorithms/models";
import { DefaultTagID } from "../stores/room-list/models"; import { DefaultTagID } from "../stores/room-list/models";
import ErrorDialog from '../components/views/dialogs/ErrorDialog';
export default class RoomListActions { export default class RoomListActions {
/** /**
@ -88,7 +88,6 @@ export default class RoomListActions {
return Rooms.guessAndSetDMRoom( return Rooms.guessAndSetDMRoom(
room, newTag === DefaultTagID.DM, room, newTag === DefaultTagID.DM,
).catch((err) => { ).catch((err) => {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
console.error("Failed to set direct chat tag " + err); console.error("Failed to set direct chat tag " + err);
Modal.createTrackedDialog('Failed to set direct chat tag', '', ErrorDialog, { Modal.createTrackedDialog('Failed to set direct chat tag', '', ErrorDialog, {
title: _t('Failed to set direct chat tag'), title: _t('Failed to set direct chat tag'),
@ -109,7 +108,6 @@ export default class RoomListActions {
const promiseToDelete = matrixClient.deleteRoomTag( const promiseToDelete = matrixClient.deleteRoomTag(
roomId, oldTag, roomId, oldTag,
).catch(function(err) { ).catch(function(err) {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
console.error("Failed to remove tag " + oldTag + " from room: " + err); console.error("Failed to remove tag " + oldTag + " from room: " + err);
Modal.createTrackedDialog('Failed to remove tag from room', '', ErrorDialog, { Modal.createTrackedDialog('Failed to remove tag from room', '', ErrorDialog, {
title: _t('Failed to remove tag %(tagName)s from room', { tagName: oldTag }), title: _t('Failed to remove tag %(tagName)s from room', { tagName: oldTag }),
@ -129,7 +127,6 @@ export default class RoomListActions {
metaData = metaData || {}; metaData = metaData || {};
const promiseToAdd = matrixClient.setRoomTag(roomId, newTag, metaData).catch(function(err) { 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); console.error("Failed to add tag " + newTag + " to room: " + err);
Modal.createTrackedDialog('Failed to add tag to room', '', ErrorDialog, { Modal.createTrackedDialog('Failed to add tag to room', '', ErrorDialog, {
title: _t('Failed to add tag %(tagName)s to room', { tagName: newTag }), title: _t('Failed to add tag %(tagName)s to room', { tagName: newTag }),

View File

@ -15,7 +15,6 @@ limitations under the License.
*/ */
import React from 'react'; import React from 'react';
import * as sdk from '../../../../index';
import { _t } from '../../../../languageHandler'; import { _t } from '../../../../languageHandler';
import SdkConfig from '../../../../SdkConfig'; import SdkConfig from '../../../../SdkConfig';
import SettingsStore from "../../../../settings/SettingsStore"; import SettingsStore from "../../../../settings/SettingsStore";
@ -24,6 +23,9 @@ import Modal from '../../../../Modal';
import { formatBytes, formatCountLong } from "../../../../utils/FormattingUtils"; import { formatBytes, formatCountLong } from "../../../../utils/FormattingUtils";
import EventIndexPeg from "../../../../indexing/EventIndexPeg"; import EventIndexPeg from "../../../../indexing/EventIndexPeg";
import { SettingLevel } from "../../../../settings/SettingLevel"; import { SettingLevel } from "../../../../settings/SettingLevel";
import Field from '../../../../components/views/elements/Field';
import BaseDialog from "../../../../components/views/dialogs/BaseDialog";
import DialogButtons from "../../../../components/views/elements/DialogButtons";
interface IProps { interface IProps {
onFinished: (confirmed: boolean) => void; onFinished: (confirmed: boolean) => void;
@ -145,7 +147,6 @@ export default class ManageEventIndexDialog extends React.Component<IProps, ISta
render() { render() {
const brand = SdkConfig.get().brand; const brand = SdkConfig.get().brand;
const Field = sdk.getComponent('views.elements.Field');
let crawlerState; let crawlerState;
if (this.state.currentRoom === null) { if (this.state.currentRoom === null) {
@ -176,15 +177,12 @@ export default class ManageEventIndexDialog extends React.Component<IProps, ISta
<Field <Field
label={_t('Message downloading sleep time(ms)')} label={_t('Message downloading sleep time(ms)')}
type='number' type='number'
value={this.state.crawlerSleepTime} value={this.state.crawlerSleepTime.toString()}
onChange={this.onCrawlerSleepTimeChange} /> onChange={this.onCrawlerSleepTimeChange} />
</div> </div>
</div> </div>
); );
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
return ( return (
<BaseDialog className='mx_ManageEventIndexDialog' <BaseDialog className='mx_ManageEventIndexDialog'
onFinished={this.props.onFinished} onFinished={this.props.onFinished}

View File

@ -22,12 +22,12 @@ import AutocompleteProvider from './AutocompleteProvider';
import { MatrixClientPeg } from '../MatrixClientPeg'; import { MatrixClientPeg } from '../MatrixClientPeg';
import QueryMatcher from './QueryMatcher'; import QueryMatcher from './QueryMatcher';
import { PillCompletion } from './Components'; import { PillCompletion } from './Components';
import * as sdk from '../index';
import { sortBy } from "lodash"; import { sortBy } from "lodash";
import { makeGroupPermalink } from "../utils/permalinks/Permalinks"; import { makeGroupPermalink } from "../utils/permalinks/Permalinks";
import { ICompletion, ISelectionRange } from "./Autocompleter"; import { ICompletion, ISelectionRange } from "./Autocompleter";
import FlairStore from "../stores/FlairStore"; import FlairStore from "../stores/FlairStore";
import { mediaFromMxc } from "../customisations/Media"; import { mediaFromMxc } from "../customisations/Media";
import BaseAvatar from '../components/views/avatars/BaseAvatar';
const COMMUNITY_REGEX = /\B\+\S*/g; const COMMUNITY_REGEX = /\B\+\S*/g;
@ -56,8 +56,6 @@ export default class CommunityProvider extends AutocompleteProvider {
force = false, force = false,
limit = -1, limit = -1,
): Promise<ICompletion[]> { ): Promise<ICompletion[]> {
const BaseAvatar = sdk.getComponent('views.avatars.BaseAvatar');
// Disable autocompletions when composing commands because of various issues // Disable autocompletions when composing commands because of various issues
// (see https://github.com/vector-im/element-web/issues/4762) // (see https://github.com/vector-im/element-web/issues/4762)
if (/^(\/join|\/leave)/.test(query)) { if (/^(\/join|\/leave)/.test(query)) {

View File

@ -21,8 +21,8 @@ import AutocompleteProvider from './AutocompleteProvider';
import { _t } from '../languageHandler'; import { _t } from '../languageHandler';
import { MatrixClientPeg } from '../MatrixClientPeg'; import { MatrixClientPeg } from '../MatrixClientPeg';
import { PillCompletion } from './Components'; import { PillCompletion } from './Components';
import * as sdk from '../index';
import { ICompletion, ISelectionRange } from "./Autocompleter"; import { ICompletion, ISelectionRange } from "./Autocompleter";
import RoomAvatar from '../components/views/avatars/RoomAvatar';
const AT_ROOM_REGEX = /@\S*/g; const AT_ROOM_REGEX = /@\S*/g;
@ -40,8 +40,6 @@ export default class NotifProvider extends AutocompleteProvider {
force = false, force = false,
limit = -1, limit = -1,
): Promise<ICompletion[]> { ): Promise<ICompletion[]> {
const RoomAvatar = sdk.getComponent('views.avatars.RoomAvatar');
const client = MatrixClientPeg.get(); const client = MatrixClientPeg.get();
if (!this.room.currentState.mayTriggerNotifOfType('room', client.credentials.userId)) return []; if (!this.room.currentState.mayTriggerNotifOfType('room', client.credentials.userId)) return [];

View File

@ -21,7 +21,6 @@ import React from 'react';
import { _t } from '../languageHandler'; import { _t } from '../languageHandler';
import AutocompleteProvider from './AutocompleteProvider'; import AutocompleteProvider from './AutocompleteProvider';
import { PillCompletion } from './Components'; import { PillCompletion } from './Components';
import * as sdk from '../index';
import QueryMatcher from './QueryMatcher'; import QueryMatcher from './QueryMatcher';
import { sortBy } from 'lodash'; import { sortBy } from 'lodash';
import { MatrixClientPeg } from '../MatrixClientPeg'; 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 { EventTimeline } from "matrix-js-sdk/src/models/event-timeline";
import { makeUserPermalink } from "../utils/permalinks/Permalinks"; import { makeUserPermalink } from "../utils/permalinks/Permalinks";
import { ICompletion, ISelectionRange } from "./Autocompleter"; import { ICompletion, ISelectionRange } from "./Autocompleter";
import MemberAvatar from '../components/views/avatars/MemberAvatar';
const USER_REGEX = /\B@\S*/g; const USER_REGEX = /\B@\S*/g;
@ -108,8 +108,6 @@ export default class UserProvider extends AutocompleteProvider {
force = false, force = false,
limit = -1, limit = -1,
): Promise<ICompletion[]> { ): Promise<ICompletion[]> {
const MemberAvatar = sdk.getComponent('views.avatars.MemberAvatar');
// lazy-load user list into matcher // lazy-load user list into matcher
if (!this.users) this._makeUsers(); if (!this.users) this._makeUsers();

View File

@ -24,7 +24,6 @@ import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { Room } from 'matrix-js-sdk/src/models/room'; import { Room } from 'matrix-js-sdk/src/models/room';
import { TimelineWindow } from 'matrix-js-sdk/src/timeline-window'; import { TimelineWindow } from 'matrix-js-sdk/src/timeline-window';
import * as sdk from '../../index';
import { MatrixClientPeg } from '../../MatrixClientPeg'; import { MatrixClientPeg } from '../../MatrixClientPeg';
import EventIndexPeg from "../../indexing/EventIndexPeg"; import EventIndexPeg from "../../indexing/EventIndexPeg";
import { _t } from '../../languageHandler'; import { _t } from '../../languageHandler';
@ -34,6 +33,9 @@ import DesktopBuildsNotice, { WarningKind } from "../views/elements/DesktopBuild
import { replaceableComponent } from "../../utils/replaceableComponent"; import { replaceableComponent } from "../../utils/replaceableComponent";
import ResizeNotifier from '../../utils/ResizeNotifier'; import ResizeNotifier from '../../utils/ResizeNotifier';
import TimelinePanel from "./TimelinePanel";
import Spinner from "../views/elements/Spinner";
import { TileShape } from '../views/rooms/EventTile';
interface IProps { interface IProps {
roomId: string; roomId: string;
@ -237,8 +239,6 @@ class FilePanel extends React.Component<IProps, IState> {
} }
// wrap a TimelinePanel with the jump-to-event bits turned off. // 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 = (<div className="mx_RightPanel_empty mx_FilePanel_empty"> const emptyState = (<div className="mx_RightPanel_empty mx_FilePanel_empty">
<h2>{_t('No files visible in this room')}</h2> <h2>{_t('No files visible in this room')}</h2>
@ -264,7 +264,7 @@ class FilePanel extends React.Component<IProps, IState> {
timelineSet={this.state.timelineSet} timelineSet={this.state.timelineSet}
showUrlPreview = {false} showUrlPreview = {false}
onPaginationRequest={this.onPaginationRequest} onPaginationRequest={this.onPaginationRequest}
tileShape="file_grid" tileShape={TileShape.FileGrid}
resizeNotifier={this.props.resizeNotifier} resizeNotifier={this.props.resizeNotifier}
empty={emptyState} empty={emptyState}
/> />
@ -277,7 +277,7 @@ class FilePanel extends React.Component<IProps, IState> {
onClose={this.props.onClose} onClose={this.props.onClose}
previousPhase={RightPanelPhases.RoomSummary} previousPhase={RightPanelPhases.RoomSummary}
> >
<Loader /> <Spinner />
</BaseCard> </BaseCard>
); );
} }

View File

@ -96,6 +96,7 @@ const HomePage: React.FC<IProps> = ({ justRegistered = false }) => {
const pageUrl = getHomePageUrl(config); const pageUrl = getHomePageUrl(config);
if (pageUrl) { if (pageUrl) {
// FIXME: Using an import will result in wrench-element-tests failures
const EmbeddedPage = sdk.getComponent('structures.EmbeddedPage'); const EmbeddedPage = sdk.getComponent('structures.EmbeddedPage');
return <EmbeddedPage className="mx_HomePage" url={pageUrl} scrollbar={true} />; return <EmbeddedPage className="mx_HomePage" url={pageUrl} scrollbar={true} />;
} }

View File

@ -24,7 +24,6 @@ import { Key } from '../../Keyboard';
import PageTypes from '../../PageTypes'; import PageTypes from '../../PageTypes';
import MediaDeviceHandler from '../../MediaDeviceHandler'; import MediaDeviceHandler from '../../MediaDeviceHandler';
import { fixupColorFonts } from '../../utils/FontManager'; import { fixupColorFonts } from '../../utils/FontManager';
import * as sdk from '../../index';
import dis from '../../dispatcher/dispatcher'; import dis from '../../dispatcher/dispatcher';
import { IMatrixClientCreds } from '../../MatrixClientPeg'; import { IMatrixClientCreds } from '../../MatrixClientPeg';
import SettingsStore from "../../settings/SettingsStore"; import SettingsStore from "../../settings/SettingsStore";
@ -59,6 +58,11 @@ import { replaceableComponent } from "../../utils/replaceableComponent";
import CallHandler, { CallHandlerEvent } from '../../CallHandler'; import CallHandler, { CallHandlerEvent } from '../../CallHandler';
import { MatrixCall } from 'matrix-js-sdk/src/webrtc/call'; import { MatrixCall } from 'matrix-js-sdk/src/webrtc/call';
import AudioFeedArrayForCall from '../views/voip/AudioFeedArrayForCall'; import AudioFeedArrayForCall from '../views/voip/AudioFeedArrayForCall';
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) // 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. // so each pinned message may trigger a request. Limit the number per room for sanity.
@ -78,8 +82,8 @@ interface IProps {
hideToSRUsers: boolean; hideToSRUsers: boolean;
resizeNotifier: ResizeNotifier; resizeNotifier: ResizeNotifier;
// eslint-disable-next-line camelcase // eslint-disable-next-line camelcase
page_type: string; page_type?: string;
autoJoin: boolean; autoJoin?: boolean;
threepidInvite?: IThreepidInvite; threepidInvite?: IThreepidInvite;
roomOobData?: IOOBData; roomOobData?: IOOBData;
currentRoomId: string; currentRoomId: string;
@ -394,7 +398,7 @@ class LoggedInView extends React.Component<IProps, IState> {
// refocusing during a paste event will make the // refocusing during a paste event will make the
// paste end up in the newly focused element, // paste end up in the newly focused element,
// so dispatch synchronously before paste happens // so dispatch synchronously before paste happens
dis.fire(Action.FocusComposer, true); dis.fire(Action.FocusSendMessageComposer, true);
} }
}; };
@ -548,7 +552,7 @@ class LoggedInView extends React.Component<IProps, IState> {
if (!isClickShortcut && ev.key !== Key.TAB && !canElementReceiveInput(ev.target)) { if (!isClickShortcut && ev.key !== Key.TAB && !canElementReceiveInput(ev.target)) {
// synchronous dispatch so we focus before key generates input // synchronous dispatch so we focus before key generates input
dis.fire(Action.FocusComposer, true); dis.fire(Action.FocusSendMessageComposer, true);
ev.stopPropagation(); ev.stopPropagation();
// we should *not* preventDefault() here as // we should *not* preventDefault() here as
// that would prevent typing in the now-focussed composer // that would prevent typing in the now-focussed composer
@ -567,12 +571,6 @@ class LoggedInView extends React.Component<IProps, IState> {
}; };
render() { 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; let pageElement;
switch (this.props.page_type) { switch (this.props.page_type) {

View File

@ -36,7 +36,6 @@ import dis from "../../dispatcher/dispatcher";
import Notifier from '../../Notifier'; import Notifier from '../../Notifier';
import Modal from "../../Modal"; import Modal from "../../Modal";
import * as sdk from '../../index';
import { showRoomInviteDialog, showStartChatInviteDialog } from '../../RoomInvite'; import { showRoomInviteDialog, showStartChatInviteDialog } from '../../RoomInvite';
import * as Rooms from '../../Rooms'; import * as Rooms from '../../Rooms';
import linkifyMatrix from "../../linkify-matrix"; import linkifyMatrix from "../../linkify-matrix";
@ -85,9 +84,27 @@ import RoomListStore from "../../stores/room-list/RoomListStore";
import { RoomUpdateCause } from "../../stores/room-list/models"; import { RoomUpdateCause } from "../../stores/room-list/models";
import defaultDispatcher from "../../dispatcher/dispatcher"; import defaultDispatcher from "../../dispatcher/dispatcher";
import SecurityCustomisations from "../../customisations/Security"; import SecurityCustomisations from "../../customisations/Security";
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 PerformanceMonitor, { PerformanceEntryNames } from "../../performance";
import UIStore, { UI_EVENTS } from "../../stores/UIStore"; import UIStore, { UI_EVENTS } from "../../stores/UIStore";
import SoftLogout from './auth/SoftLogout';
/** constants for MatrixChat.state.view */ /** constants for MatrixChat.state.view */
export enum Views { export enum Views {
@ -156,7 +173,12 @@ interface IRoomInfo {
/* eslint-enable camelcase */ /* eslint-enable camelcase */
interface IProps { // TODO type things better interface IProps { // TODO type things better
config: Record<string, any>; config: {
piwik: {
policyUrl: string;
};
[key: string]: any;
};
serverConfig?: ValidatedServerConfig; serverConfig?: ValidatedServerConfig;
onNewScreen: (screen: string, replaceLast: boolean) => void; onNewScreen: (screen: string, replaceLast: boolean) => void;
enableGuest?: boolean; enableGuest?: boolean;
@ -421,7 +443,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
CountlyAnalytics.instance.trackPageChange(durationMs); CountlyAnalytics.instance.trackPageChange(durationMs);
} }
if (this.focusComposer) { if (this.focusComposer) {
dis.fire(Action.FocusComposer); dis.fire(Action.FocusSendMessageComposer);
this.focusComposer = false; this.focusComposer = false;
} }
} }
@ -519,7 +541,6 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
onAction = (payload) => { onAction = (payload) => {
// console.log(`MatrixClientPeg.onAction: ${payload.action}`); // console.log(`MatrixClientPeg.onAction: ${payload.action}`);
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
// Start the onboarding process for certain actions // Start the onboarding process for certain actions
if (MatrixClientPeg.get() && MatrixClientPeg.get().isGuest() && if (MatrixClientPeg.get() && MatrixClientPeg.get().isGuest() &&
@ -613,8 +634,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
onFinished: (confirm) => { onFinished: (confirm) => {
if (confirm) { if (confirm) {
// FIXME: controller shouldn't be loading a view :( // FIXME: controller shouldn't be loading a view :(
const Loader = sdk.getComponent("elements.Spinner"); const modal = Modal.createDialog(Spinner, null, 'mx_Dialog_spinner');
const modal = Modal.createDialog(Loader, null, 'mx_Dialog_spinner');
MatrixClientPeg.get().leave(payload.room_id).then(() => { MatrixClientPeg.get().leave(payload.room_id).then(() => {
modal.close(); modal.close();
@ -650,7 +670,6 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
} }
case Action.ViewUserSettings: { case Action.ViewUserSettings: {
const tabPayload = payload as OpenToTabPayload; const tabPayload = payload as OpenToTabPayload;
const UserSettingsDialog = sdk.getComponent("dialogs.UserSettingsDialog");
Modal.createTrackedDialog('User settings', '', UserSettingsDialog, Modal.createTrackedDialog('User settings', '', UserSettingsDialog,
{ initialTabId: tabPayload.initialTabId }, { initialTabId: tabPayload.initialTabId },
/*className=*/null, /*isPriority=*/false, /*isStatic=*/true); /*className=*/null, /*isPriority=*/false, /*isStatic=*/true);
@ -663,11 +682,12 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
this.createRoom(payload.public, payload.defaultName); this.createRoom(payload.public, payload.defaultName);
break; break;
case 'view_create_group': { case 'view_create_group': {
let CreateGroupDialog = sdk.getComponent("dialogs.CreateGroupDialog"); const prototype = SettingsStore.getValue("feature_communities_v2_prototypes");
if (SettingsStore.getValue("feature_communities_v2_prototypes")) { Modal.createTrackedDialog(
CreateGroupDialog = CreateCommunityPrototypeDialog; 'Create Community',
} '',
Modal.createTrackedDialog('Create Community', '', CreateGroupDialog); prototype ? CreateCommunityPrototypeDialog : CreateGroupDialog,
);
break; break;
} }
case Action.ViewRoomDirectory: { case Action.ViewRoomDirectory: {
@ -677,7 +697,6 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
room_id: SpaceStore.instance.activeSpace.roomId, room_id: SpaceStore.instance.activeSpace.roomId,
}); });
} else { } else {
const RoomDirectory = sdk.getComponent("structures.RoomDirectory");
Modal.createTrackedDialog('Room directory', '', RoomDirectory, { Modal.createTrackedDialog('Room directory', '', RoomDirectory, {
initialText: payload.initialText, initialText: payload.initialText,
}, 'mx_RoomDirectory_dialogWrapper', false, true); }, 'mx_RoomDirectory_dialogWrapper', false, true);
@ -1019,7 +1038,6 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
} }
} }
const CreateRoomDialog = sdk.getComponent('dialogs.CreateRoomDialog');
const modal = Modal.createTrackedDialog('Create Room', '', CreateRoomDialog, { const modal = Modal.createTrackedDialog('Create Room', '', CreateRoomDialog, {
defaultPublic, defaultPublic,
defaultName, defaultName,
@ -1116,7 +1134,6 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
} }
private leaveRoom(roomId: string) { private leaveRoom(roomId: string) {
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
const roomToLeave = MatrixClientPeg.get().getRoom(roomId); const roomToLeave = MatrixClientPeg.get().getRoom(roomId);
const warnings = this.leaveRoomWarnings(roomId); const warnings = this.leaveRoomWarnings(roomId);
@ -1143,8 +1160,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
const d = leaveRoomBehaviour(roomId); const d = leaveRoomBehaviour(roomId);
// FIXME: controller shouldn't be loading a view :( // FIXME: controller shouldn't be loading a view :(
const Loader = sdk.getComponent("elements.Spinner"); const modal = Modal.createDialog(Spinner, null, 'mx_Dialog_spinner');
const modal = Modal.createDialog(Loader, null, 'mx_Dialog_spinner');
d.finally(() => modal.close()); d.finally(() => modal.close());
dis.dispatch({ dis.dispatch({
@ -1411,7 +1427,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
showNotificationsToast(false); showNotificationsToast(false);
} }
dis.fire(Action.FocusComposer); dis.fire(Action.FocusSendMessageComposer);
this.setState({ this.setState({
ready: true, ready: true,
}); });
@ -1439,7 +1455,6 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
}); });
}); });
cli.on('no_consent', function(message, consentUri) { cli.on('no_consent', function(message, consentUri) {
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
Modal.createTrackedDialog('No Consent Dialog', '', QuestionDialog, { Modal.createTrackedDialog('No Consent Dialog', '', QuestionDialog, {
title: _t('Terms and Conditions'), title: _t('Terms and Conditions'),
description: <div> description: <div>
@ -1548,8 +1563,6 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
}); });
cli.on("crypto.keySignatureUploadFailure", (failures, source, continuation) => { cli.on("crypto.keySignatureUploadFailure", (failures, source, continuation) => {
const KeySignatureUploadFailedDialog =
sdk.getComponent('views.dialogs.KeySignatureUploadFailedDialog');
Modal.createTrackedDialog( Modal.createTrackedDialog(
'Failed to upload key signatures', 'Failed to upload key signatures',
'Failed to upload key signatures', 'Failed to upload key signatures',
@ -1559,7 +1572,6 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
cli.on("crypto.verification.request", request => { cli.on("crypto.verification.request", request => {
if (request.verifier) { if (request.verifier) {
const IncomingSasDialog = sdk.getComponent("views.dialogs.IncomingSasDialog");
Modal.createTrackedDialog('Incoming Verification', '', IncomingSasDialog, { Modal.createTrackedDialog('Incoming Verification', '', IncomingSasDialog, {
verifier: request.verifier, verifier: request.verifier,
}, null, /* priority = */ false, /* static = */ true); }, null, /* priority = */ false, /* static = */ true);
@ -1569,7 +1581,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
title: _t("Verification requested"), title: _t("Verification requested"),
icon: "verification", icon: "verification",
props: { request }, props: { request },
component: sdk.getComponent("toasts.VerificationRequestToast"), component: VerificationRequestToast,
priority: 90, priority: 90,
}); });
} }
@ -1977,21 +1989,18 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
let view = null; let view = null;
if (this.state.view === Views.LOADING) { if (this.state.view === Views.LOADING) {
const Spinner = sdk.getComponent('elements.Spinner');
view = ( view = (
<div className="mx_MatrixChat_splash"> <div className="mx_MatrixChat_splash">
<Spinner /> <Spinner />
</div> </div>
); );
} else if (this.state.view === Views.COMPLETE_SECURITY) { } else if (this.state.view === Views.COMPLETE_SECURITY) {
const CompleteSecurity = sdk.getComponent('structures.auth.CompleteSecurity');
view = ( view = (
<CompleteSecurity <CompleteSecurity
onFinished={this.onCompleteSecurityE2eSetupFinished} onFinished={this.onCompleteSecurityE2eSetupFinished}
/> />
); );
} else if (this.state.view === Views.E2E_SETUP) { } else if (this.state.view === Views.E2E_SETUP) {
const E2eSetup = sdk.getComponent('structures.auth.E2eSetup');
view = ( view = (
<E2eSetup <E2eSetup
onFinished={this.onCompleteSecurityE2eSetupFinished} onFinished={this.onCompleteSecurityE2eSetupFinished}
@ -2012,7 +2021,6 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
* we should go through and figure out what we actually need to pass down, as well * 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. * as using something like redux to avoid having a billion bits of state kicking around.
*/ */
const LoggedInView = sdk.getComponent('structures.LoggedInView');
view = ( view = (
<LoggedInView <LoggedInView
{...this.props} {...this.props}
@ -2020,14 +2028,12 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
ref={this.loggedInView} ref={this.loggedInView}
matrixClient={MatrixClientPeg.get()} matrixClient={MatrixClientPeg.get()}
onRoomCreated={this.onRoomCreated} onRoomCreated={this.onRoomCreated}
onCloseAllSettings={this.onCloseAllSettings}
onRegistered={this.onRegistered} onRegistered={this.onRegistered}
currentRoomId={this.state.currentRoomId} currentRoomId={this.state.currentRoomId}
/> />
); );
} else { } else {
// we think we are logged in, but are still waiting for the /sync to complete // we think we are logged in, but are still waiting for the /sync to complete
const Spinner = sdk.getComponent('elements.Spinner');
let errorBox; let errorBox;
if (this.state.syncError && !isStoreError) { if (this.state.syncError && !isStoreError) {
errorBox = <div className="mx_MatrixChat_syncError"> errorBox = <div className="mx_MatrixChat_syncError">
@ -2045,10 +2051,8 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
); );
} }
} else if (this.state.view === Views.WELCOME) { } else if (this.state.view === Views.WELCOME) {
const Welcome = sdk.getComponent('auth.Welcome');
view = <Welcome />; view = <Welcome />;
} else if (this.state.view === Views.REGISTER && SettingsStore.getValue(UIFeature.Registration)) { } else if (this.state.view === Views.REGISTER && SettingsStore.getValue(UIFeature.Registration)) {
const Registration = sdk.getComponent('structures.auth.Registration');
const email = ThreepidInviteStore.instance.pickBestInvite()?.toEmail; const email = ThreepidInviteStore.instance.pickBestInvite()?.toEmail;
view = ( view = (
<Registration <Registration
@ -2067,7 +2071,6 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
/> />
); );
} else if (this.state.view === Views.FORGOT_PASSWORD && SettingsStore.getValue(UIFeature.PasswordReset)) { } else if (this.state.view === Views.FORGOT_PASSWORD && SettingsStore.getValue(UIFeature.PasswordReset)) {
const ForgotPassword = sdk.getComponent('structures.auth.ForgotPassword');
view = ( view = (
<ForgotPassword <ForgotPassword
onComplete={this.onLoginClick} onComplete={this.onLoginClick}
@ -2078,7 +2081,6 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
); );
} else if (this.state.view === Views.LOGIN) { } else if (this.state.view === Views.LOGIN) {
const showPasswordReset = SettingsStore.getValue(UIFeature.PasswordReset); const showPasswordReset = SettingsStore.getValue(UIFeature.PasswordReset);
const Login = sdk.getComponent('structures.auth.Login');
view = ( view = (
<Login <Login
isSyncing={this.state.pendingInitialSync} isSyncing={this.state.pendingInitialSync}
@ -2094,7 +2096,6 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
/> />
); );
} else if (this.state.view === Views.SOFT_LOGOUT) { } else if (this.state.view === Views.SOFT_LOGOUT) {
const SoftLogout = sdk.getComponent('structures.auth.SoftLogout');
view = ( view = (
<SoftLogout <SoftLogout
realQueryParams={this.props.realQueryParams} realQueryParams={this.props.realQueryParams}
@ -2106,7 +2107,6 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
console.error(`Unknown view ${this.state.view}`); console.error(`Unknown view ${this.state.view}`);
} }
const ErrorBoundary = sdk.getComponent('elements.ErrorBoundary');
return <ErrorBoundary> return <ErrorBoundary>
{view} {view}
</ErrorBoundary>; </ErrorBoundary>;

View File

@ -44,10 +44,14 @@ import NetworkDropdown from "../views/directory/NetworkDropdown";
import ScrollPanel from "./ScrollPanel"; import ScrollPanel from "./ScrollPanel";
import Spinner from "../views/elements/Spinner"; import Spinner from "../views/elements/Spinner";
import { ActionPayload } from "../../dispatcher/payloads"; import { ActionPayload } from "../../dispatcher/payloads";
import { getDisplayAliasForAliasSet } from "../../Rooms";
const MAX_NAME_LENGTH = 80; const MAX_NAME_LENGTH = 80;
const MAX_TOPIC_LENGTH = 800; const MAX_TOPIC_LENGTH = 800;
const LAST_SERVER_KEY = "mx_last_room_directory_server";
const LAST_INSTANCE_KEY = "mx_last_room_directory_instance";
function track(action: string) { function track(action: string) {
Analytics.trackEvent('RoomDirectory', action); Analytics.trackEvent('RoomDirectory', action);
} }
@ -61,7 +65,7 @@ interface IState {
loading: boolean; loading: boolean;
protocolsLoading: boolean; protocolsLoading: boolean;
error?: string; error?: string;
instanceId: string | symbol; instanceId: string;
roomServer: string; roomServer: string;
filterString: string; filterString: string;
selectedCommunityId?: string; selectedCommunityId?: string;
@ -116,6 +120,36 @@ export default class RoomDirectory extends React.Component<IProps, IState> {
} else if (!selectedCommunityId) { } else if (!selectedCommunityId) {
MatrixClientPeg.get().getThirdpartyProtocols().then((response) => { MatrixClientPeg.get().getThirdpartyProtocols().then((response) => {
this.protocols = response; this.protocols = response;
const myHomeserver = MatrixClientPeg.getHomeserverName();
const lsRoomServer = localStorage.getItem(LAST_SERVER_KEY);
const lsInstanceId = localStorage.getItem(LAST_INSTANCE_KEY);
let roomServer = myHomeserver;
if (
SdkConfig.get().roomDirectory?.servers?.includes(lsRoomServer) ||
SettingsStore.getValue("room_directory_servers")?.includes(lsRoomServer)
) {
roomServer = lsRoomServer;
}
let instanceId: string = null;
if (roomServer === myHomeserver && (
lsInstanceId === ALL_ROOMS ||
Object.values(this.protocols).some(p => p.instances.some(i => i.instance_id === lsInstanceId))
)) {
instanceId = lsInstanceId;
}
// Refresh the room list only if validation failed and we had to change these
if (this.state.instanceId !== instanceId || this.state.roomServer !== roomServer) {
this.setState({
protocolsLoading: false,
instanceId,
roomServer,
});
this.refreshRoomList();
return;
}
this.setState({ protocolsLoading: false }); this.setState({ protocolsLoading: false });
}, (err) => { }, (err) => {
console.warn(`error loading third party protocols: ${err}`); console.warn(`error loading third party protocols: ${err}`);
@ -150,8 +184,8 @@ export default class RoomDirectory extends React.Component<IProps, IState> {
publicRooms: [], publicRooms: [],
loading: true, loading: true,
error: null, error: null,
instanceId: undefined, instanceId: localStorage.getItem(LAST_INSTANCE_KEY),
roomServer: MatrixClientPeg.getHomeserverName(), roomServer: localStorage.getItem(LAST_SERVER_KEY),
filterString: this.props.initialText || "", filterString: this.props.initialText || "",
selectedCommunityId, selectedCommunityId,
communityName: null, communityName: null,
@ -342,7 +376,7 @@ export default class RoomDirectory extends React.Component<IProps, IState> {
} }
}; };
private onOptionChange = (server: string, instanceId?: string | symbol) => { private onOptionChange = (server: string, instanceId?: string) => {
// clear next batch so we don't try to load more rooms // clear next batch so we don't try to load more rooms
this.nextBatch = null; this.nextBatch = null;
this.setState({ this.setState({
@ -360,6 +394,14 @@ export default class RoomDirectory extends React.Component<IProps, IState> {
// find the five gitter ones, at which point we do not want // find the five gitter ones, at which point we do not want
// to render all those rooms when switching back to 'all networks'. // to render all those rooms when switching back to 'all networks'.
// Easiest to just blow away the state & re-fetch. // Easiest to just blow away the state & re-fetch.
// We have to be careful here so that we don't set instanceId = "undefined"
localStorage.setItem(LAST_SERVER_KEY, server);
if (instanceId) {
localStorage.setItem(LAST_INSTANCE_KEY, instanceId);
} else {
localStorage.removeItem(LAST_INSTANCE_KEY);
}
}; };
private onFillRequest = (backwards: boolean) => { private onFillRequest = (backwards: boolean) => {
@ -813,5 +855,5 @@ export default class RoomDirectory extends React.Component<IProps, IState> {
// Similar to matrix-react-sdk's MatrixTools.getDisplayAliasForRoom // Similar to matrix-react-sdk's MatrixTools.getDisplayAliasForRoom
// but works with the objects we get from the public room list // but works with the objects we get from the public room list
function getDisplayAliasForRoom(room: IRoom) { function getDisplayAliasForRoom(room: IRoom) {
return room.canonical_alias || room.aliases?.[0] || ""; return getDisplayAliasForAliasSet(room.canonical_alias, room.aliases);
} }

View File

@ -131,7 +131,7 @@ export default class RoomSearch extends React.PureComponent<IProps, IState> {
switch (action) { switch (action) {
case RoomListAction.ClearSearch: case RoomListAction.ClearSearch:
this.clearInput(); this.clearInput();
defaultDispatcher.fire(Action.FocusComposer); defaultDispatcher.fire(Action.FocusSendMessageComposer);
break; break;
case RoomListAction.NextRoom: case RoomListAction.NextRoom:
case RoomListAction.PrevRoom: case RoomListAction.PrevRoom:

View File

@ -118,12 +118,12 @@ export default class RoomStatusBar extends React.PureComponent {
this.setState({ isResending: false }); this.setState({ isResending: false });
}); });
this.setState({ isResending: true }); this.setState({ isResending: true });
dis.fire(Action.FocusComposer); dis.fire(Action.FocusSendMessageComposer);
}; };
_onCancelAllClick = () => { _onCancelAllClick = () => {
Resend.cancelUnsentEvents(this.props.room); Resend.cancelUnsentEvents(this.props.room);
dis.fire(Action.FocusComposer); dis.fire(Action.FocusSendMessageComposer);
}; };
_onRoomLocalEchoUpdated = (event, room, oldEventId, oldStatus) => { _onRoomLocalEchoUpdated = (event, room, oldEventId, oldStatus) => {

View File

@ -34,7 +34,6 @@ import { RoomPermalinkCreator } from '../../utils/permalinks/Permalinks';
import ResizeNotifier from '../../utils/ResizeNotifier'; import ResizeNotifier from '../../utils/ResizeNotifier';
import ContentMessages from '../../ContentMessages'; import ContentMessages from '../../ContentMessages';
import Modal from '../../Modal'; import Modal from '../../Modal';
import * as sdk from '../../index';
import CallHandler, { PlaceCallType } from '../../CallHandler'; import CallHandler, { PlaceCallType } from '../../CallHandler';
import dis from '../../dispatcher/dispatcher'; import dis from '../../dispatcher/dispatcher';
import * as Rooms from '../../Rooms'; import * as Rooms from '../../Rooms';
@ -82,6 +81,14 @@ import { replaceableComponent } from "../../utils/replaceableComponent";
import UIStore from "../../stores/UIStore"; import UIStore from "../../stores/UIStore";
import EditorStateTransfer from "../../utils/EditorStateTransfer"; import EditorStateTransfer from "../../utils/EditorStateTransfer";
import { throttle } from "lodash"; 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; const DEBUG = false;
let debuglog = function(msg: string) {}; let debuglog = function(msg: string) {};
@ -811,17 +818,16 @@ export default class RoomView extends React.Component<IProps, IState> {
case Action.ComposerInsert: { case Action.ComposerInsert: {
// re-dispatch to the correct composer // re-dispatch to the correct composer
if (this.state.editState) { dis.dispatch({
dis.dispatch({ ...payload,
...payload, action: this.state.editState ? "edit_composer_insert" : "send_composer_insert",
action: "edit_composer_insert", });
}); break;
} else { }
dis.dispatch({
...payload, case Action.FocusAComposer: {
action: "send_composer_insert", // re-dispatch to the correct composer
}); dis.fire(this.state.editState ? Action.FocusEditMessageComposer : Action.FocusSendMessageComposer);
}
break; break;
} }
@ -1239,7 +1245,7 @@ export default class RoomView extends React.Component<IProps, IState> {
ContentMessages.sharedInstance().sendContentListToRoom( ContentMessages.sharedInstance().sendContentListToRoom(
ev.dataTransfer.files, this.state.room.roomId, this.context, ev.dataTransfer.files, this.state.room.roomId, this.context,
); );
dis.fire(Action.FocusComposer); dis.fire(Action.FocusSendMessageComposer);
this.setState({ this.setState({
draggingFile: false, draggingFile: false,
@ -1328,7 +1334,6 @@ export default class RoomView extends React.Component<IProps, IState> {
searchResults: results, searchResults: results,
}); });
}, (error) => { }, (error) => {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
console.error("Search failed", error); console.error("Search failed", error);
Modal.createTrackedDialog('Search failed', '', ErrorDialog, { Modal.createTrackedDialog('Search failed', '', ErrorDialog, {
title: _t("Search failed"), title: _t("Search failed"),
@ -1344,9 +1349,6 @@ export default class RoomView extends React.Component<IProps, IState> {
} }
private getSearchResultTiles() { private getSearchResultTiles() {
const SearchResultTile = sdk.getComponent('rooms.SearchResultTile');
const Spinner = sdk.getComponent("elements.Spinner");
// XXX: todo: merge overlapping results somehow? // XXX: todo: merge overlapping results somehow?
// XXX: why doesn't searching on name work? // XXX: why doesn't searching on name work?
@ -1466,7 +1468,6 @@ export default class RoomView extends React.Component<IProps, IState> {
console.error("Failed to reject invite: %s", error); console.error("Failed to reject invite: %s", error);
const msg = error.message ? error.message : JSON.stringify(error); const msg = error.message ? error.message : JSON.stringify(error);
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog('Failed to reject invite', '', ErrorDialog, { Modal.createTrackedDialog('Failed to reject invite', '', ErrorDialog, {
title: _t("Failed to reject invite"), title: _t("Failed to reject invite"),
description: msg, description: msg,
@ -1500,7 +1501,6 @@ export default class RoomView extends React.Component<IProps, IState> {
console.error("Failed to reject invite: %s", error); console.error("Failed to reject invite: %s", error);
const msg = error.message ? error.message : JSON.stringify(error); const msg = error.message ? error.message : JSON.stringify(error);
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog('Failed to reject invite', '', ErrorDialog, { Modal.createTrackedDialog('Failed to reject invite', '', ErrorDialog, {
title: _t("Failed to reject invite"), title: _t("Failed to reject invite"),
description: msg, description: msg,
@ -1547,7 +1547,7 @@ export default class RoomView extends React.Component<IProps, IState> {
} else { } else {
// Otherwise we have to jump manually // Otherwise we have to jump manually
this.messagePanel.jumpToLiveTimeline(); this.messagePanel.jumpToLiveTimeline();
dis.fire(Action.FocusComposer); dis.fire(Action.FocusSendMessageComposer);
} }
}; };
@ -1834,10 +1834,8 @@ export default class RoomView extends React.Component<IProps, IState> {
let isStatusAreaExpanded = true; let isStatusAreaExpanded = true;
if (ContentMessages.sharedInstance().getCurrentUploads().length > 0) { if (ContentMessages.sharedInstance().getCurrentUploads().length > 0) {
const UploadBar = sdk.getComponent('structures.UploadBar');
statusBar = <UploadBar room={this.state.room} />; statusBar = <UploadBar room={this.state.room} />;
} else if (!this.state.searchResults) { } else if (!this.state.searchResults) {
const RoomStatusBar = sdk.getComponent('structures.RoomStatusBar');
isStatusAreaExpanded = this.state.statusBarVisible; isStatusAreaExpanded = this.state.statusBarVisible;
statusBar = <RoomStatusBar statusBar = <RoomStatusBar
room={this.state.room} room={this.state.room}
@ -1943,12 +1941,9 @@ export default class RoomView extends React.Component<IProps, IState> {
myMembership === 'join' && !this.state.searchResults myMembership === 'join' && !this.state.searchResults
); );
if (canSpeak) { if (canSpeak) {
const MessageComposer = sdk.getComponent('rooms.MessageComposer');
messageComposer = messageComposer =
<MessageComposer <MessageComposer
room={this.state.room} room={this.state.room}
callState={this.state.callState}
showApps={this.state.showApps}
e2eStatus={this.state.e2eStatus} e2eStatus={this.state.e2eStatus}
resizeNotifier={this.props.resizeNotifier} resizeNotifier={this.props.resizeNotifier}
replyToEvent={this.state.replyToEvent} replyToEvent={this.state.replyToEvent}
@ -2034,7 +2029,6 @@ export default class RoomView extends React.Component<IProps, IState> {
let topUnreadMessagesBar = null; let topUnreadMessagesBar = null;
// Do not show TopUnreadMessagesBar if we have search results showing, it makes no sense // Do not show TopUnreadMessagesBar if we have search results showing, it makes no sense
if (this.state.showTopUnreadMessagesBar && !this.state.searchResults) { if (this.state.showTopUnreadMessagesBar && !this.state.searchResults) {
const TopUnreadMessagesBar = sdk.getComponent('rooms.TopUnreadMessagesBar');
topUnreadMessagesBar = ( topUnreadMessagesBar = (
<TopUnreadMessagesBar onScrollUpClick={this.jumpToReadMarker} onCloseClick={this.forgetReadMarker} /> <TopUnreadMessagesBar onScrollUpClick={this.jumpToReadMarker} onCloseClick={this.forgetReadMarker} />
); );
@ -2042,7 +2036,6 @@ export default class RoomView extends React.Component<IProps, IState> {
let jumpToBottom; let jumpToBottom;
// Do not show JumpToBottomButton if we have search results showing, it makes no sense // Do not show JumpToBottomButton if we have search results showing, it makes no sense
if (!this.state.atEndOfLiveTimeline && !this.state.searchResults) { if (!this.state.atEndOfLiveTimeline && !this.state.searchResults) {
const JumpToBottomButton = sdk.getComponent('rooms.JumpToBottomButton');
jumpToBottom = (<JumpToBottomButton jumpToBottom = (<JumpToBottomButton
highlight={this.state.room.getUnreadNotificationCount(NotificationCountType.Highlight) > 0} highlight={this.state.room.getUnreadNotificationCount(NotificationCountType.Highlight) > 0}
numUnreadMessages={this.state.numUnreadMessages} numUnreadMessages={this.state.numUnreadMessages}

View File

@ -42,6 +42,7 @@ import { useStateToggle } from "../../hooks/useStateToggle";
import { getChildOrder } from "../../stores/SpaceStore"; import { getChildOrder } from "../../stores/SpaceStore";
import AccessibleTooltipButton from "../views/elements/AccessibleTooltipButton"; import AccessibleTooltipButton from "../views/elements/AccessibleTooltipButton";
import { linkifyElement } from "../../HtmlUtils"; import { linkifyElement } from "../../HtmlUtils";
import { getDisplayAliasForAliasSet } from "../../Rooms";
interface IHierarchyProps { interface IHierarchyProps {
space: Room; space: Room;
@ -666,5 +667,5 @@ export default SpaceRoomDirectory;
// Similar to matrix-react-sdk's MatrixTools.getDisplayAliasForRoom // Similar to matrix-react-sdk's MatrixTools.getDisplayAliasForRoom
// but works with the objects we get from the public room list // but works with the objects we get from the public room list
function getDisplayAliasForRoom(room: ISpaceSummaryRoom) { function getDisplayAliasForRoom(room: ISpaceSummaryRoom) {
return room.canonical_alias || (room.aliases ? room.aliases[0] : ""); return getDisplayAliasForAliasSet(room.canonical_alias, room.aliases);
} }

View File

@ -18,9 +18,9 @@ limitations under the License.
import * as React from "react"; import * as React from "react";
import { _t } from '../../languageHandler'; import { _t } from '../../languageHandler';
import * as sdk from "../../index";
import AutoHideScrollbar from './AutoHideScrollbar'; import AutoHideScrollbar from './AutoHideScrollbar';
import { replaceableComponent } from "../../utils/replaceableComponent"; import { replaceableComponent } from "../../utils/replaceableComponent";
import AccessibleButton from "../views/elements/AccessibleButton";
/** /**
* Represents a tab for the TabbedView. * Represents a tab for the TabbedView.
@ -82,8 +82,6 @@ export default class TabbedView extends React.Component<IProps, IState> {
} }
private _renderTabLabel(tab: Tab) { private _renderTabLabel(tab: Tab) {
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
let classes = "mx_TabbedView_tabLabel "; let classes = "mx_TabbedView_tabLabel ";
const idx = this.props.tabs.indexOf(tab); const idx = this.props.tabs.indexOf(tab);

View File

@ -32,7 +32,6 @@ import RoomContext from "../../contexts/RoomContext";
import UserActivity from "../../UserActivity"; import UserActivity from "../../UserActivity";
import Modal from "../../Modal"; import Modal from "../../Modal";
import dis from "../../dispatcher/dispatcher"; import dis from "../../dispatcher/dispatcher";
import * as sdk from "../../index";
import { Key } from '../../Keyboard'; import { Key } from '../../Keyboard';
import Timer from '../../utils/Timer'; import Timer from '../../utils/Timer';
import shouldHideEvent from '../../shouldHideEvent'; import shouldHideEvent from '../../shouldHideEvent';
@ -47,6 +46,7 @@ import ResizeNotifier from "../../utils/ResizeNotifier";
import { RoomPermalinkCreator } from "../../utils/permalinks/Permalinks"; import { RoomPermalinkCreator } from "../../utils/permalinks/Permalinks";
import Spinner from "../views/elements/Spinner"; import Spinner from "../views/elements/Spinner";
import EditorStateTransfer from '../../utils/EditorStateTransfer'; import EditorStateTransfer from '../../utils/EditorStateTransfer';
import ErrorDialog from '../views/dialogs/ErrorDialog';
const PAGINATE_SIZE = 20; const PAGINATE_SIZE = 20;
const INITIAL_SIZE = 20; const INITIAL_SIZE = 20;
@ -1096,7 +1096,6 @@ class TimelinePanel extends React.Component<IProps, IState> {
console.error( console.error(
`Error loading timeline panel at ${eventId}: ${error}`, `Error loading timeline panel at ${eventId}: ${error}`,
); );
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
let onFinished; let onFinished;

View File

@ -45,7 +45,7 @@ enum Phase {
interface IProps { interface IProps {
serverConfig: ValidatedServerConfig; serverConfig: ValidatedServerConfig;
onServerConfigChange: () => void; onServerConfigChange: (serverConfig: ValidatedServerConfig) => void;
onLoginClick?: () => void; onLoginClick?: () => void;
onComplete: () => void; onComplete: () => void;
} }

View File

@ -18,7 +18,6 @@ import React, { ReactNode } from 'react';
import { MatrixError } from "matrix-js-sdk/src/http-api"; import { MatrixError } from "matrix-js-sdk/src/http-api";
import { _t, _td } from '../../../languageHandler'; import { _t, _td } from '../../../languageHandler';
import * as sdk from '../../../index';
import Login, { ISSOFlow, LoginFlow } from '../../../Login'; import Login, { ISSOFlow, LoginFlow } from '../../../Login';
import SdkConfig from '../../../SdkConfig'; import SdkConfig from '../../../SdkConfig';
import { messageForResourceLimitError } from '../../../utils/ErrorUtils'; import { messageForResourceLimitError } from '../../../utils/ErrorUtils';
@ -36,6 +35,8 @@ import Spinner from "../../views/elements/Spinner";
import SSOButtons from "../../views/elements/SSOButtons"; import SSOButtons from "../../views/elements/SSOButtons";
import ServerPicker from "../../views/elements/ServerPicker"; import ServerPicker from "../../views/elements/ServerPicker";
import { replaceableComponent } from "../../../utils/replaceableComponent"; 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 // These are used in several places, and come from the js-sdk's autodiscovery
// stuff. We define them here so that they'll be picked up by i18n. // stuff. We define them here so that they'll be picked up by i18n.
@ -541,8 +542,6 @@ export default class LoginComponent extends React.PureComponent<IProps, IState>
}; };
render() { render() {
const AuthHeader = sdk.getComponent("auth.AuthHeader");
const AuthBody = sdk.getComponent("auth.AuthBody");
const loader = this.isBusy() && !this.state.busyLoggingIn ? const loader = this.isBusy() && !this.state.busyLoggingIn ?
<div className="mx_Login_loader"><Spinner /></div> : null; <div className="mx_Login_loader"><Spinner /></div> : null;

View File

@ -18,19 +18,24 @@ import { createClient } from 'matrix-js-sdk/src/matrix';
import React, { ReactNode } from 'react'; import React, { ReactNode } from 'react';
import { MatrixClient } from "matrix-js-sdk/src/client"; import { MatrixClient } from "matrix-js-sdk/src/client";
import * as sdk from '../../../index';
import { _t, _td } from '../../../languageHandler'; import { _t, _td } from '../../../languageHandler';
import { messageForResourceLimitError } from '../../../utils/ErrorUtils'; import { messageForResourceLimitError } from '../../../utils/ErrorUtils';
import AutoDiscoveryUtils, { ValidatedServerConfig } from "../../../utils/AutoDiscoveryUtils"; import AutoDiscoveryUtils, { ValidatedServerConfig } from "../../../utils/AutoDiscoveryUtils";
import classNames from "classnames"; import classNames from "classnames";
import * as Lifecycle from '../../../Lifecycle'; import * as Lifecycle from '../../../Lifecycle';
import { MatrixClientPeg } from "../../../MatrixClientPeg"; import { IMatrixClientCreds, MatrixClientPeg } from "../../../MatrixClientPeg";
import AuthPage from "../../views/auth/AuthPage"; import AuthPage from "../../views/auth/AuthPage";
import Login, { ISSOFlow } from "../../../Login"; import Login, { ISSOFlow } from "../../../Login";
import dis from "../../../dispatcher/dispatcher"; import dis from "../../../dispatcher/dispatcher";
import SSOButtons from "../../views/elements/SSOButtons"; import SSOButtons from "../../views/elements/SSOButtons";
import ServerPicker from '../../views/elements/ServerPicker'; import ServerPicker from '../../views/elements/ServerPicker';
import { replaceableComponent } from "../../../utils/replaceableComponent"; 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 { interface IProps {
serverConfig: ValidatedServerConfig; serverConfig: ValidatedServerConfig;
@ -47,13 +52,7 @@ interface IProps {
// - The user's password, if available and applicable (may be cached in memory // - 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 a short time so the user is not required to re-enter their password
// for operations like uploading cross-signing keys). // for operations like uploading cross-signing keys).
onLoggedIn(params: { onLoggedIn(params: IMatrixClientCreds, password: string): void;
userId: string;
deviceId: string;
homeserverUrl: string;
identityServerUrl?: string;
accessToken: string;
}, password: string): void;
makeRegistrationUrl(params: { makeRegistrationUrl(params: {
/* eslint-disable camelcase */ /* eslint-disable camelcase */
client_secret: string; client_secret: string;
@ -246,7 +245,7 @@ export default class Registration extends React.Component<IProps, IState> {
} }
} }
private onFormSubmit = formVals => { private onFormSubmit = async (formVals): Promise<void> => {
this.setState({ this.setState({
errorText: "", errorText: "",
busy: true, busy: true,
@ -442,10 +441,6 @@ export default class Registration extends React.Component<IProps, IState> {
}; };
private renderRegisterComponent() { 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) { if (this.state.matrixClient && this.state.doingUIAuth) {
return <InteractiveAuth return <InteractiveAuth
matrixClient={this.state.matrixClient} matrixClient={this.state.matrixClient}
@ -516,10 +511,6 @@ export default class Registration extends React.Component<IProps, IState> {
} }
render() { render() {
const AuthHeader = sdk.getComponent('auth.AuthHeader');
const AuthBody = sdk.getComponent("auth.AuthBody");
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
let errorText; let errorText;
const err = this.state.errorText; const err = this.state.errorText;
if (err) { if (err) {

View File

@ -21,7 +21,7 @@ import Modal from '../../../Modal';
import VerificationRequestDialog from '../../views/dialogs/VerificationRequestDialog'; import VerificationRequestDialog from '../../views/dialogs/VerificationRequestDialog';
import { SetupEncryptionStore, Phase } from '../../../stores/SetupEncryptionStore'; import { SetupEncryptionStore, Phase } from '../../../stores/SetupEncryptionStore';
import { replaceableComponent } from "../../../utils/replaceableComponent"; 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 EncryptionPanel from "../../views/right_panel/EncryptionPanel";
import AccessibleButton from '../../views/elements/AccessibleButton'; import AccessibleButton from '../../views/elements/AccessibleButton';
import Spinner from '../../views/elements/Spinner'; import Spinner from '../../views/elements/Spinner';

View File

@ -16,7 +16,6 @@ limitations under the License.
import React from 'react'; import React from 'react';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
import * as sdk from '../../../index';
import dis from '../../../dispatcher/dispatcher'; import dis from '../../../dispatcher/dispatcher';
import * as Lifecycle from '../../../Lifecycle'; import * as Lifecycle from '../../../Lifecycle';
import Modal from '../../../Modal'; 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 { SSO_HOMESERVER_URL_KEY, SSO_ID_SERVER_URL_KEY } from "../../../BasePlatform";
import SSOButtons from "../../views/elements/SSOButtons"; import SSOButtons from "../../views/elements/SSOButtons";
import { replaceableComponent } from "../../../utils/replaceableComponent"; 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 = { const LOGIN_VIEW = {
LOADING: 1, LOADING: 1,
@ -94,7 +99,6 @@ export default class SoftLogout extends React.Component<IProps, IState> {
} }
onClearAll = () => { onClearAll = () => {
const ConfirmWipeDeviceDialog = sdk.getComponent('dialogs.ConfirmWipeDeviceDialog');
Modal.createTrackedDialog('Clear Data', 'Soft Logout', ConfirmWipeDeviceDialog, { Modal.createTrackedDialog('Clear Data', 'Soft Logout', ConfirmWipeDeviceDialog, {
onFinished: (wipeData) => { onFinished: (wipeData) => {
if (!wipeData) return; if (!wipeData) return;
@ -202,7 +206,6 @@ export default class SoftLogout extends React.Component<IProps, IState> {
private renderSignInSection() { private renderSignInSection() {
if (this.state.loginView === LOGIN_VIEW.LOADING) { if (this.state.loginView === LOGIN_VIEW.LOADING) {
const Spinner = sdk.getComponent("elements.Spinner");
return <Spinner />; return <Spinner />;
} }
@ -214,9 +217,6 @@ export default class SoftLogout extends React.Component<IProps, IState> {
} }
if (this.state.loginView === LOGIN_VIEW.PASSWORD) { if (this.state.loginView === LOGIN_VIEW.PASSWORD) {
const Field = sdk.getComponent("elements.Field");
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
let error = null; let error = null;
if (this.state.errorText) { if (this.state.errorText) {
error = <span className='mx_Login_error'>{this.state.errorText}</span>; error = <span className='mx_Login_error'>{this.state.errorText}</span>;
@ -286,10 +286,6 @@ export default class SoftLogout extends React.Component<IProps, IState> {
} }
render() { render() {
const AuthHeader = sdk.getComponent("auth.AuthHeader");
const AuthBody = sdk.getComponent("auth.AuthBody");
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
return ( return (
<AuthPage> <AuthPage>
<AuthHeader /> <AuthHeader />

View File

@ -18,7 +18,6 @@ import React, { ChangeEvent, createRef, FormEvent, MouseEvent } from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import { MatrixClient } from "matrix-js-sdk/src/client"; import { MatrixClient } from "matrix-js-sdk/src/client";
import * as sdk from '../../../index';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
import SettingsStore from "../../../settings/SettingsStore"; import SettingsStore from "../../../settings/SettingsStore";
import AccessibleButton from "../elements/AccessibleButton"; import AccessibleButton from "../elements/AccessibleButton";
@ -26,6 +25,8 @@ import Spinner from "../elements/Spinner";
import CountlyAnalytics from "../../../CountlyAnalytics"; import CountlyAnalytics from "../../../CountlyAnalytics";
import { replaceableComponent } from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";
import { LocalisedPolicy, Policies } from '../../../Terms'; 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 /* This file contains a collection of components which are used by the
* InteractiveAuth to prompt the user to enter the information needed * InteractiveAuth to prompt the user to enter the information needed
@ -164,8 +165,7 @@ export class PasswordAuthEntry extends React.Component<IAuthEntryProps, IPasswor
let submitButtonOrSpinner; let submitButtonOrSpinner;
if (this.props.busy) { if (this.props.busy) {
const Loader = sdk.getComponent("elements.Spinner"); submitButtonOrSpinner = <Spinner />;
submitButtonOrSpinner = <Loader />;
} else { } else {
submitButtonOrSpinner = ( submitButtonOrSpinner = (
<input type="submit" <input type="submit"
@ -185,8 +185,6 @@ export class PasswordAuthEntry extends React.Component<IAuthEntryProps, IPasswor
); );
} }
const Field = sdk.getComponent('elements.Field');
return ( return (
<div> <div>
<p>{ _t("Confirm your identity by entering your account password below.") }</p> <p>{ _t("Confirm your identity by entering your account password below.") }</p>
@ -236,13 +234,11 @@ export class RecaptchaAuthEntry extends React.Component<IRecaptchaAuthEntryProps
render() { render() {
if (this.props.busy) { if (this.props.busy) {
const Loader = sdk.getComponent("elements.Spinner"); return <Spinner />;
return <Loader />;
} }
let errorText = this.props.errorText; let errorText = this.props.errorText;
const CaptchaForm = sdk.getComponent("views.auth.CaptchaForm");
let sitePublicKey; let sitePublicKey;
if (!this.props.stageParams || !this.props.stageParams.public_key) { if (!this.props.stageParams || !this.props.stageParams.public_key) {
errorText = _t( errorText = _t(
@ -390,8 +386,7 @@ export class TermsAuthEntry extends React.Component<ITermsAuthEntryProps, ITerms
render() { render() {
if (this.props.busy) { if (this.props.busy) {
const Loader = sdk.getComponent("elements.Spinner"); return <Spinner />;
return <Loader />;
} }
const checkboxes = []; const checkboxes = [];
@ -590,8 +585,7 @@ export class MsisdnAuthEntry extends React.Component<IMsisdnAuthEntryProps, IMsi
render() { render() {
if (this.state.requestingToken) { if (this.state.requestingToken) {
const Loader = sdk.getComponent("elements.Spinner"); return <Spinner />;
return <Loader />;
} else { } else {
const enableSubmit = Boolean(this.state.token); const enableSubmit = Boolean(this.state.token);
const submitClasses = classNames({ const submitClasses = classNames({

View File

@ -17,7 +17,6 @@ limitations under the License.
import React from 'react'; import React from 'react';
import * as sdk from '../../../index';
import * as Email from '../../../email'; import * as Email from '../../../email';
import { looksValid as phoneNumberLooksValid } from '../../../phonenumber'; import { looksValid as phoneNumberLooksValid } from '../../../phonenumber';
import Modal from '../../../Modal'; import Modal from '../../../Modal';
@ -31,6 +30,7 @@ import CountlyAnalytics from "../../../CountlyAnalytics";
import Field from '../elements/Field'; import Field from '../elements/Field';
import RegistrationEmailPromptDialog from '../dialogs/RegistrationEmailPromptDialog'; import RegistrationEmailPromptDialog from '../dialogs/RegistrationEmailPromptDialog';
import { replaceableComponent } from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";
import CountryDropdown from "./CountryDropdown";
enum RegistrationField { enum RegistrationField {
Email = "field_email", Email = "field_email",
@ -471,7 +471,6 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
if (!this.showPhoneNumber()) { if (!this.showPhoneNumber()) {
return null; return null;
} }
const CountryDropdown = sdk.getComponent('views.auth.CountryDropdown');
const phoneLabel = this.authStepIsRequired('m.login.msisdn') ? const phoneLabel = this.authStepIsRequired('m.login.msisdn') ?
_t("Phone") : _t("Phone") :
_t("Phone (optional)"); _t("Phone (optional)");

View File

@ -1,6 +1,6 @@
/* /*
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com> Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
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"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with 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 React from 'react';
import PropTypes from 'prop-types'; import { EventStatus, MatrixEvent } from 'matrix-js-sdk/src/models/event';
import { EventStatus } from 'matrix-js-sdk/src/models/event'; import { EventType, RelationType } from "matrix-js-sdk/src/@types/event";
import { MatrixClientPeg } from '../../../MatrixClientPeg'; import { MatrixClientPeg } from '../../../MatrixClientPeg';
import dis from '../../../dispatcher/dispatcher'; import dis from '../../../dispatcher/dispatcher';
import * as sdk from '../../../index';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
import Modal from '../../../Modal'; import Modal from '../../../Modal';
import Resend from '../../../Resend'; import Resend from '../../../Resend';
@ -29,53 +28,65 @@ import SettingsStore from '../../../settings/SettingsStore';
import { isUrlPermitted } from '../../../HtmlUtils'; import { isUrlPermitted } from '../../../HtmlUtils';
import { isContentActionable } from '../../../utils/EventUtils'; import { isContentActionable } from '../../../utils/EventUtils';
import IconizedContextMenu, { IconizedContextMenuOption, IconizedContextMenuOptionList } from './IconizedContextMenu'; import IconizedContextMenu, { IconizedContextMenuOption, IconizedContextMenuOptionList } from './IconizedContextMenu';
import { EventType } from "matrix-js-sdk/src/@types/event";
import { replaceableComponent } from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";
import { ReadPinsEventId } from "../right_panel/PinnedMessagesCard"; import { ReadPinsEventId } from "../right_panel/PinnedMessagesCard";
import ForwardDialog from "../dialogs/ForwardDialog"; import ForwardDialog from "../dialogs/ForwardDialog";
import { Action } from "../../../dispatcher/actions"; 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; 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") @replaceableComponent("views.context_menus.MessageContextMenu")
export default class MessageContextMenu extends React.Component { export default class MessageContextMenu extends React.Component<IProps, IState> {
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,
};
state = { state = {
canRedact: false, canRedact: false,
canPin: false, canPin: false,
}; };
componentDidMount() { componentDidMount() {
MatrixClientPeg.get().on('RoomMember.powerLevel', this._checkPermissions); MatrixClientPeg.get().on('RoomMember.powerLevel', this.checkPermissions);
this._checkPermissions(); this.checkPermissions();
} }
componentWillUnmount() { componentWillUnmount() {
const cli = MatrixClientPeg.get(); const cli = MatrixClientPeg.get();
if (cli) { if (cli) {
cli.removeListener('RoomMember.powerLevel', this._checkPermissions); cli.removeListener('RoomMember.powerLevel', this.checkPermissions);
} }
} }
_checkPermissions = () => { private checkPermissions = (): void => {
const cli = MatrixClientPeg.get(); const cli = MatrixClientPeg.get();
const room = cli.getRoom(this.props.mxEvent.getRoomId()); const room = cli.getRoom(this.props.mxEvent.getRoomId());
@ -93,7 +104,7 @@ export default class MessageContextMenu extends React.Component {
this.setState({ canRedact, canPin }); this.setState({ canRedact, canPin });
}; };
_isPinned() { private isPinned(): boolean {
const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId()); const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId());
const pinnedEvent = room.currentState.getStateEvents(EventType.RoomPinnedEvents, ''); const pinnedEvent = room.currentState.getStateEvents(EventType.RoomPinnedEvents, '');
if (!pinnedEvent) return false; 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()); return content.pinned && Array.isArray(content.pinned) && content.pinned.includes(this.props.mxEvent.getId());
} }
onResendReactionsClick = () => { private onResendReactionsClick = (): void => {
for (const reaction of this._getUnsentReactions()) { for (const reaction of this.getUnsentReactions()) {
Resend.resend(reaction); Resend.resend(reaction);
} }
this.closeMenu(); this.closeMenu();
}; };
onReportEventClick = () => { private onReportEventClick = (): void => {
const ReportEventDialog = sdk.getComponent("dialogs.ReportEventDialog");
Modal.createTrackedDialog('Report Event', '', ReportEventDialog, { Modal.createTrackedDialog('Report Event', '', ReportEventDialog, {
mxEvent: this.props.mxEvent, mxEvent: this.props.mxEvent,
}, 'mx_Dialog_reportEvent'); }, 'mx_Dialog_reportEvent');
this.closeMenu(); this.closeMenu();
}; };
onViewSourceClick = () => { private onViewSourceClick = (): void => {
const ViewSource = sdk.getComponent('structures.ViewSource');
Modal.createTrackedDialog('View Event Source', '', ViewSource, { Modal.createTrackedDialog('View Event Source', '', ViewSource, {
mxEvent: this.props.mxEvent, mxEvent: this.props.mxEvent,
}, 'mx_Dialog_viewsource'); }, 'mx_Dialog_viewsource');
this.closeMenu(); this.closeMenu();
}; };
onRedactClick = () => { private onRedactClick = (): void => {
const ConfirmRedactDialog = sdk.getComponent("dialogs.ConfirmRedactDialog");
Modal.createTrackedDialog('Confirm Redact Dialog', '', ConfirmRedactDialog, { Modal.createTrackedDialog('Confirm Redact Dialog', '', ConfirmRedactDialog, {
onFinished: async (proceed, reason) => { onFinished: async (proceed: boolean, reason?: string) => {
if (!proceed) return; if (!proceed) return;
const cli = MatrixClientPeg.get(); const cli = MatrixClientPeg.get();
try { try {
if (this.props.onCloseDialog) this.props.onCloseDialog(); this.props.onCloseDialog?.();
await cli.redactEvent( await cli.redactEvent(
this.props.mxEvent.getRoomId(), this.props.mxEvent.getRoomId(),
this.props.mxEvent.getId(), 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 // (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 // detached queue and we show the room status bar to allow retry
if (typeof code !== "undefined") { if (typeof code !== "undefined") {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
// display error message stating you couldn't delete this. // display error message stating you couldn't delete this.
Modal.createTrackedDialog('You cannot delete this message', '', ErrorDialog, { Modal.createTrackedDialog('You cannot delete this message', '', ErrorDialog, {
title: _t('Error'), title: _t('Error'),
@ -158,7 +165,7 @@ export default class MessageContextMenu extends React.Component {
this.closeMenu(); this.closeMenu();
}; };
onForwardClick = () => { private onForwardClick = (): void => {
Modal.createTrackedDialog('Forward Message', '', ForwardDialog, { Modal.createTrackedDialog('Forward Message', '', ForwardDialog, {
matrixClient: MatrixClientPeg.get(), matrixClient: MatrixClientPeg.get(),
event: this.props.mxEvent, event: this.props.mxEvent,
@ -167,12 +174,12 @@ export default class MessageContextMenu extends React.Component {
this.closeMenu(); this.closeMenu();
}; };
onPinClick = () => { private onPinClick = (): void => {
const cli = MatrixClientPeg.get(); const cli = MatrixClientPeg.get();
const room = cli.getRoom(this.props.mxEvent.getRoomId()); const room = cli.getRoom(this.props.mxEvent.getRoomId());
const eventId = this.props.mxEvent.getId(); 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)) { if (pinnedIds.includes(eventId)) {
pinnedIds.splice(pinnedIds.indexOf(eventId), 1); pinnedIds.splice(pinnedIds.indexOf(eventId), 1);
} else { } else {
@ -188,18 +195,16 @@ export default class MessageContextMenu extends React.Component {
this.closeMenu(); this.closeMenu();
}; };
closeMenu = () => { private closeMenu = (): void => {
if (this.props.onFinished) this.props.onFinished(); this.props.onFinished();
}; };
onUnhidePreviewClick = () => { private onUnhidePreviewClick = (): void => {
if (this.props.eventTileOps) { this.props.eventTileOps?.unhideWidget();
this.props.eventTileOps.unhideWidget();
}
this.closeMenu(); this.closeMenu();
}; };
onQuoteClick = () => { private onQuoteClick = (): void => {
dis.dispatch({ dis.dispatch({
action: Action.ComposerInsert, action: Action.ComposerInsert,
event: this.props.mxEvent, event: this.props.mxEvent,
@ -207,9 +212,8 @@ export default class MessageContextMenu extends React.Component {
this.closeMenu(); this.closeMenu();
}; };
onPermalinkClick = (e) => { private onPermalinkClick = (e: React.MouseEvent): void => {
e.preventDefault(); e.preventDefault();
const ShareDialog = sdk.getComponent("dialogs.ShareDialog");
Modal.createTrackedDialog('share room message dialog', '', ShareDialog, { Modal.createTrackedDialog('share room message dialog', '', ShareDialog, {
target: this.props.mxEvent, target: this.props.mxEvent,
permalinkCreator: this.props.permalinkCreator, permalinkCreator: this.props.permalinkCreator,
@ -217,30 +221,27 @@ export default class MessageContextMenu extends React.Component {
this.closeMenu(); this.closeMenu();
}; };
onCollapseReplyThreadClick = () => { private onCollapseReplyThreadClick = (): void => {
this.props.collapseReplyThread(); this.props.collapseReplyThread();
this.closeMenu(); this.closeMenu();
}; };
_getReactions(filter) { private getReactions(filter: (e: MatrixEvent) => boolean): MatrixEvent[] {
const cli = MatrixClientPeg.get(); const cli = MatrixClientPeg.get();
const room = cli.getRoom(this.props.mxEvent.getRoomId()); const room = cli.getRoom(this.props.mxEvent.getRoomId());
const eventId = this.props.mxEvent.getId(); const eventId = this.props.mxEvent.getId();
return room.getPendingEvents().filter(e => { return room.getPendingEvents().filter(e => {
const relation = e.getRelation(); const relation = e.getRelation();
return relation && return relation?.rel_type === RelationType.Annotation && relation.event_id === eventId && filter(e);
relation.rel_type === "m.annotation" &&
relation.event_id === eventId &&
filter(e);
}); });
} }
_getPendingReactions() { private getPendingReactions(): MatrixEvent[] {
return this._getReactions(e => canCancel(e.status)); return this.getReactions(e => canCancel(e.status));
} }
_getUnsentReactions() { private getUnsentReactions(): MatrixEvent[] {
return this._getReactions(e => e.status === EventStatus.NOT_SENT); return this.getReactions(e => e.status === EventStatus.NOT_SENT);
} }
render() { render() {
@ -248,16 +249,17 @@ export default class MessageContextMenu extends React.Component {
const me = cli.getUserId(); const me = cli.getUserId();
const mxEvent = this.props.mxEvent; const mxEvent = this.props.mxEvent;
const eventStatus = mxEvent.status; const eventStatus = mxEvent.status;
const unsentReactionsCount = this._getUnsentReactions().length; const unsentReactionsCount = this.getUnsentReactions().length;
let resendReactionsButton;
let redactButton; let resendReactionsButton: JSX.Element;
let forwardButton; let redactButton: JSX.Element;
let pinButton; let forwardButton: JSX.Element;
let unhidePreviewButton; let pinButton: JSX.Element;
let externalURLButton; let unhidePreviewButton: JSX.Element;
let quoteButton; let externalURLButton: JSX.Element;
let collapseReplyThread; let quoteButton: JSX.Element;
let redactItemList; let collapseReplyThread: JSX.Element;
let redactItemList: JSX.Element;
// status is SENT before remote-echo, null after // status is SENT before remote-echo, null after
const isSent = !eventStatus || eventStatus === EventStatus.SENT; const isSent = !eventStatus || eventStatus === EventStatus.SENT;
@ -296,7 +298,7 @@ export default class MessageContextMenu extends React.Component {
pinButton = ( pinButton = (
<IconizedContextMenuOption <IconizedContextMenuOption
iconClassName="mx_MessageContextMenu_iconPin" iconClassName="mx_MessageContextMenu_iconPin"
label={ this._isPinned() ? _t('Unpin') : _t('Pin') } label={ this.isPinned() ? _t('Unpin') : _t('Pin') }
onClick={this.onPinClick} onClick={this.onPinClick}
/> />
); );
@ -327,16 +329,20 @@ export default class MessageContextMenu extends React.Component {
if (this.props.permalinkCreator) { if (this.props.permalinkCreator) {
permalink = this.props.permalinkCreator.forEvent(this.props.mxEvent.getId()); 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 = ( const permalinkButton = (
<IconizedContextMenuOption <IconizedContextMenuOption
iconClassName="mx_MessageContextMenu_iconPermalink" iconClassName="mx_MessageContextMenu_iconPermalink"
onClick={this.onPermalinkClick} onClick={this.onPermalinkClick}
label= {_t('Share')} label= {_t('Share')}
element="a" element="a"
href={permalink} {
target="_blank" // XXX: Typescript signature for AccessibleButton doesn't work properly for non-inputs like `a`
rel="noreferrer noopener" ...{
href: permalink,
target: "_blank",
rel: "noreferrer noopener",
}
}
/> />
); );
@ -351,8 +357,8 @@ export default class MessageContextMenu extends React.Component {
} }
// Bridges can provide a 'external_url' to link back to the source. // Bridges can provide a 'external_url' to link back to the source.
if (typeof (mxEvent.event.content.external_url) === "string" && if (typeof (mxEvent.getContent().external_url) === "string" &&
isUrlPermitted(mxEvent.event.content.external_url) isUrlPermitted(mxEvent.getContent().external_url)
) { ) {
externalURLButton = ( externalURLButton = (
<IconizedContextMenuOption <IconizedContextMenuOption
@ -360,9 +366,14 @@ export default class MessageContextMenu extends React.Component {
onClick={this.closeMenu} onClick={this.closeMenu}
label={ _t('Source URL') } label={ _t('Source URL') }
element="a" element="a"
target="_blank" {
rel="noreferrer noopener" // XXX: Typescript signature for AccessibleButton doesn't work properly for non-inputs like `a`
href={mxEvent.event.content.external_url} ...{
target: "_blank",
rel: "noreferrer noopener",
href: mxEvent.getContent().external_url,
}
}
/> />
); );
} }
@ -377,7 +388,7 @@ export default class MessageContextMenu extends React.Component {
); );
} }
let reportEventButton; let reportEventButton: JSX.Element;
if (mxEvent.getSender() !== me) { if (mxEvent.getSender() !== me) {
reportEventButton = ( reportEventButton = (
<IconizedContextMenuOption <IconizedContextMenuOption

View File

@ -15,11 +15,11 @@ limitations under the License.
*/ */
import React from 'react'; import React from 'react';
import * as sdk from '../../../index';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
import SettingsStore from "../../../settings/SettingsStore"; import SettingsStore from "../../../settings/SettingsStore";
import { SettingLevel } from "../../../settings/SettingLevel"; import { SettingLevel } from "../../../settings/SettingLevel";
import { replaceableComponent } from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";
import BaseDialog from "./BaseDialog";
interface IProps { interface IProps {
unknownProfileUsers: Array<{ unknownProfileUsers: Array<{
@ -50,8 +50,6 @@ export default class AskInviteAnywayDialog extends React.Component<IProps> {
}; };
public render() { public render() {
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
const errorList = this.props.unknownProfileUsers const errorList = this.props.unknownProfileUsers
.map(address => <li key={address.userId}>{address.userId}: {address.errorText}</li>); .map(address => <li key={address.userId}>{address.userId}: {address.errorText}</li>);

View File

@ -18,13 +18,17 @@ limitations under the License.
*/ */
import React from 'react'; import React from 'react';
import * as sdk from '../../../index';
import SdkConfig from '../../../SdkConfig'; import SdkConfig from '../../../SdkConfig';
import Modal from '../../../Modal'; import Modal from '../../../Modal';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
import sendBugReport, { downloadBugReport } from '../../../rageshake/submit-rageshake'; import sendBugReport, { downloadBugReport } from '../../../rageshake/submit-rageshake';
import AccessibleButton from "../elements/AccessibleButton"; import AccessibleButton from "../elements/AccessibleButton";
import { replaceableComponent } from "../../../utils/replaceableComponent"; 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 { interface IProps {
onFinished: (success: boolean) => void; onFinished: (success: boolean) => void;
@ -93,7 +97,6 @@ export default class BugReportDialog extends React.Component<IProps, IState> {
}).then(() => { }).then(() => {
if (!this.unmounted) { if (!this.unmounted) {
this.props.onFinished(false); this.props.onFinished(false);
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
// N.B. first param is passed to piwik and so doesn't want i18n // N.B. first param is passed to piwik and so doesn't want i18n
Modal.createTrackedDialog('Bug report sent', '', QuestionDialog, { Modal.createTrackedDialog('Bug report sent', '', QuestionDialog, {
title: _t('Logs sent'), title: _t('Logs sent'),
@ -160,11 +163,6 @@ export default class BugReportDialog extends React.Component<IProps, IState> {
}; };
public render() { 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; let error = null;
if (this.state.err) { if (this.state.err) {
error = <div className="error"> error = <div className="error">
@ -176,7 +174,7 @@ export default class BugReportDialog extends React.Component<IProps, IState> {
if (this.state.busy) { if (this.state.busy) {
progress = ( progress = (
<div className="progress"> <div className="progress">
<Loader /> <Spinner />
{this.state.progress} ... {this.state.progress} ...
</div> </div>
); );

View File

@ -16,9 +16,10 @@ Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
*/ */
import React from 'react'; import React from 'react';
import * as sdk from '../../../index';
import request from 'browser-request'; import request from 'browser-request';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
import QuestionDialog from "./QuestionDialog";
import Spinner from "../elements/Spinner";
interface IProps { interface IProps {
newVersion: string; newVersion: string;
@ -65,9 +66,6 @@ export default class ChangelogDialog extends React.Component<IProps> {
} }
public render() { public render() {
const Spinner = sdk.getComponent('views.elements.Spinner');
const QuestionDialog = sdk.getComponent('dialogs.QuestionDialog');
const logs = REPOS.map(repo => { const logs = REPOS.map(repo => {
let content; let content;
if (this.state[repo] == null) { if (this.state[repo] == null) {

View File

@ -15,9 +15,12 @@ limitations under the License.
*/ */
import React from 'react'; import React from 'react';
import * as sdk from '../../../index';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
import { replaceableComponent } from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";
import ConfirmRedactDialog from './ConfirmRedactDialog';
import ErrorDialog from './ErrorDialog';
import BaseDialog from "./BaseDialog";
import Spinner from "../elements/Spinner";
interface IProps { interface IProps {
redact: () => Promise<void>; redact: () => Promise<void>;
@ -73,7 +76,6 @@ export default class ConfirmAndWaitRedactDialog extends React.PureComponent<IPro
public render() { public render() {
if (this.state.isRedacting) { if (this.state.isRedacting) {
if (this.state.redactionErrorCode) { if (this.state.redactionErrorCode) {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
const code = this.state.redactionErrorCode; const code = this.state.redactionErrorCode;
return ( return (
<ErrorDialog <ErrorDialog
@ -83,8 +85,6 @@ export default class ConfirmAndWaitRedactDialog extends React.PureComponent<IPro
/> />
); );
} else { } else {
const BaseDialog = sdk.getComponent("dialogs.BaseDialog");
const Spinner = sdk.getComponent('elements.Spinner');
return ( return (
<BaseDialog <BaseDialog
onFinished={this.props.onFinished} onFinished={this.props.onFinished}
@ -95,7 +95,6 @@ export default class ConfirmAndWaitRedactDialog extends React.PureComponent<IPro
); );
} }
} else { } else {
const ConfirmRedactDialog = sdk.getComponent("dialogs.ConfirmRedactDialog");
return <ConfirmRedactDialog onFinished={this.onParentFinished} />; return <ConfirmRedactDialog onFinished={this.onParentFinished} />;
} }
} }

View File

@ -15,9 +15,9 @@ limitations under the License.
*/ */
import React from 'react'; import React from 'react';
import * as sdk from '../../../index';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
import { replaceableComponent } from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";
import TextInputDialog from "./TextInputDialog";
interface IProps { interface IProps {
onFinished: (success: boolean) => void; onFinished: (success: boolean) => void;
@ -29,7 +29,6 @@ interface IProps {
@replaceableComponent("views.dialogs.ConfirmRedactDialog") @replaceableComponent("views.dialogs.ConfirmRedactDialog")
export default class ConfirmRedactDialog extends React.Component<IProps> { export default class ConfirmRedactDialog extends React.Component<IProps> {
render() { render() {
const TextInputDialog = sdk.getComponent('views.dialogs.TextInputDialog');
return ( return (
<TextInputDialog onFinished={this.props.onFinished} <TextInputDialog onFinished={this.props.onFinished}
title={_t("Confirm Removal")} title={_t("Confirm Removal")}

View File

@ -17,11 +17,14 @@ limitations under the License.
import React from 'react'; import React from 'react';
import { MatrixClient } from 'matrix-js-sdk/src/client'; import { MatrixClient } from 'matrix-js-sdk/src/client';
import { RoomMember } from "matrix-js-sdk/src/models/room-member"; import { RoomMember } from "matrix-js-sdk/src/models/room-member";
import * as sdk from '../../../index';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
import { GroupMemberType } from '../../../groups'; import { GroupMemberType } from '../../../groups';
import { replaceableComponent } from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";
import { mediaFromMxc } from "../../../customisations/Media"; import { mediaFromMxc } from "../../../customisations/Media";
import MemberAvatar from '../avatars/MemberAvatar';
import BaseAvatar from '../avatars/BaseAvatar';
import BaseDialog from "./BaseDialog";
import DialogButtons from "../elements/DialogButtons";
interface IProps { interface IProps {
// matrix-js-sdk (room) member object. Supply either this or 'groupMember' // matrix-js-sdk (room) member object. Supply either this or 'groupMember'
@ -67,11 +70,6 @@ export default class ConfirmUserActionDialog extends React.Component<IProps> {
}; };
public render() { 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' : ''; const confirmButtonClass = this.props.danger ? 'danger' : '';
let reasonBox; let reasonBox;

View File

@ -16,8 +16,9 @@ limitations under the License.
import React from 'react'; import React from 'react';
import { _t } from "../../../languageHandler"; import { _t } from "../../../languageHandler";
import * as sdk from "../../../index";
import { replaceableComponent } from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";
import BaseDialog from "./BaseDialog";
import DialogButtons from "../elements/DialogButtons";
interface IProps { interface IProps {
onFinished: (success: boolean) => void; onFinished: (success: boolean) => void;
@ -34,9 +35,6 @@ export default class ConfirmWipeDeviceDialog extends React.Component<IProps> {
}; };
render() { render() {
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
return ( return (
<BaseDialog <BaseDialog
className='mx_ConfirmWipeDeviceDialog' className='mx_ConfirmWipeDeviceDialog'

View File

@ -15,11 +15,12 @@ limitations under the License.
*/ */
import React from 'react'; import React from 'react';
import * as sdk from '../../../index';
import dis from '../../../dispatcher/dispatcher'; import dis from '../../../dispatcher/dispatcher';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
import { MatrixClientPeg } from '../../../MatrixClientPeg'; import { MatrixClientPeg } from '../../../MatrixClientPeg';
import { replaceableComponent } from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";
import BaseDialog from "./BaseDialog";
import Spinner from "../elements/Spinner";
interface IProps { interface IProps {
onFinished: (success: boolean) => void; onFinished: (success: boolean) => void;
@ -106,9 +107,6 @@ export default class CreateGroupDialog extends React.Component<IProps, IState> {
}; };
render() { render() {
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
const Spinner = sdk.getComponent('elements.Spinner');
if (this.state.creating) { if (this.state.creating) {
return <Spinner />; return <Spinner />;
} }

View File

@ -16,21 +16,22 @@ limitations under the License.
*/ */
import React from 'react'; import React from 'react';
import * as sdk from '../../../index';
import dis from '../../../dispatcher/dispatcher'; import dis from '../../../dispatcher/dispatcher';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
import SdkConfig from '../../../SdkConfig'; import SdkConfig from '../../../SdkConfig';
import Modal from '../../../Modal'; import Modal from '../../../Modal';
import BaseDialog from "./BaseDialog";
import DialogButtons from "../elements/DialogButtons";
import QuestionDialog from "./QuestionDialog";
interface IProps { interface IProps {
onFinished: (success: boolean) => void; onFinished: (success: boolean) => void;
} }
export default (props: IProps) => { const CryptoStoreTooNewDialog: React.FC<IProps> = (props: IProps) => {
const brand = SdkConfig.get().brand; const brand = SdkConfig.get().brand;
const _onLogoutClicked = () => { const _onLogoutClicked = () => {
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
Modal.createTrackedDialog('Logout e2e db too new', '', QuestionDialog, { Modal.createTrackedDialog('Logout e2e db too new', '', QuestionDialog, {
title: _t("Sign out"), title: _t("Sign out"),
description: _t( description: _t(
@ -58,8 +59,6 @@ export default (props: IProps) => {
{ brand }, { brand },
); );
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
return (<BaseDialog className="mx_CryptoStoreTooNewDialog" return (<BaseDialog className="mx_CryptoStoreTooNewDialog"
contentId='mx_Dialog_content' contentId='mx_Dialog_content'
title={_t("Incompatible Database")} title={_t("Incompatible Database")}
@ -79,3 +78,5 @@ export default (props: IProps) => {
</DialogButtons> </DialogButtons>
</BaseDialog>); </BaseDialog>);
}; };
export default CryptoStoreTooNewDialog;

View File

@ -17,7 +17,6 @@ limitations under the License.
import React from 'react'; import React from 'react';
import * as sdk from '../../../index';
import Analytics from '../../../Analytics'; import Analytics from '../../../Analytics';
import { MatrixClientPeg } from '../../../MatrixClientPeg'; import { MatrixClientPeg } from '../../../MatrixClientPeg';
import * as Lifecycle from '../../../Lifecycle'; 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 { DEFAULT_PHASE, PasswordAuthEntry, SSOAuthEntry } from "../auth/InteractiveAuthEntryComponents";
import StyledCheckbox from "../elements/StyledCheckbox"; import StyledCheckbox from "../elements/StyledCheckbox";
import { replaceableComponent } from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";
import BaseDialog from "./BaseDialog";
interface IProps { interface IProps {
onFinished: (success: boolean) => void; onFinished: (success: boolean) => void;
@ -165,8 +165,6 @@ export default class DeactivateAccountDialog extends React.Component<IProps, ISt
} }
public render() { public render() {
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
let error = null; let error = null;
if (this.state.errStr) { if (this.state.errStr) {
error = <div className="error"> error = <div className="error">

View File

@ -16,7 +16,6 @@ limitations under the License.
*/ */
import React, { useState, useEffect, ChangeEvent, MouseEvent } from 'react'; import React, { useState, useEffect, ChangeEvent, MouseEvent } from 'react';
import * as sdk from '../../../index';
import SyntaxHighlight from '../elements/SyntaxHighlight'; import SyntaxHighlight from '../elements/SyntaxHighlight';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
import Field from "../elements/Field"; import Field from "../elements/Field";
@ -42,6 +41,8 @@ import { replaceableComponent } from "../../../utils/replaceableComponent";
import { Room } from "matrix-js-sdk/src/models/room"; import { Room } from "matrix-js-sdk/src/models/room";
import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { SettingLevel } from '../../../settings/SettingLevel'; import { SettingLevel } from '../../../settings/SettingLevel';
import BaseDialog from "./BaseDialog";
import TruncatedList from "../elements/TruncatedList";
interface IGenericEditorProps { interface IGenericEditorProps {
onBack: () => void; onBack: () => void;
@ -369,7 +370,6 @@ class FilteredList extends React.PureComponent<IFilteredListProps, IFilteredList
}; };
render() { render() {
const TruncatedList = sdk.getComponent("elements.TruncatedList");
return <div> return <div>
<Field label={_t('Filter results')} autoFocus={true} size={64} <Field label={_t('Filter results')} autoFocus={true} size={64}
type="text" autoComplete="off" value={this.props.query} onChange={this.onQuery} type="text" autoComplete="off" value={this.props.query} onChange={this.onQuery}
@ -1261,7 +1261,6 @@ export default class DevtoolsDialog extends React.PureComponent<IProps, IState>
</React.Fragment>; </React.Fragment>;
} }
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
return ( return (
<BaseDialog className="mx_QuestionDialog" onFinished={this.props.onFinished} title={_t('Developer Tools')}> <BaseDialog className="mx_QuestionDialog" onFinished={this.props.onFinished} title={_t('Developer Tools')}>
{ body } { body }

View File

@ -26,9 +26,9 @@ limitations under the License.
*/ */
import React from 'react'; import React from 'react';
import * as sdk from '../../../index';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
import { replaceableComponent } from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";
import BaseDialog from "./BaseDialog";
interface IProps { interface IProps {
onFinished: (success: boolean) => void; onFinished: (success: boolean) => void;
@ -57,7 +57,6 @@ export default class ErrorDialog extends React.Component<IProps, IState> {
}; };
public render() { public render() {
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
return ( return (
<BaseDialog <BaseDialog
className="mx_ErrorDialog" className="mx_ErrorDialog"

View File

@ -18,7 +18,6 @@ import React, { createRef } from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import { _t, _td } from "../../../languageHandler"; import { _t, _td } from "../../../languageHandler";
import * as sdk from "../../../index";
import { MatrixClientPeg } from "../../../MatrixClientPeg"; import { MatrixClientPeg } from "../../../MatrixClientPeg";
import { makeRoomPermalink, makeUserPermalink } from "../../../utils/permalinks/Permalinks"; import { makeRoomPermalink, makeUserPermalink } from "../../../utils/permalinks/Permalinks";
import DMRoomMap from "../../../utils/DMRoomMap"; import DMRoomMap from "../../../utils/DMRoomMap";
@ -65,6 +64,9 @@ import { copyPlaintext, selectText } from "../../../utils/strings";
import * as ContextMenu from "../../structures/ContextMenu"; import * as ContextMenu from "../../structures/ContextMenu";
import { toRightOf } from "../../structures/ContextMenu"; import { toRightOf } from "../../structures/ContextMenu";
import GenericTextContextMenu from "../context_menus/GenericTextContextMenu"; import GenericTextContextMenu from "../context_menus/GenericTextContextMenu";
import QuestionDialog from "./QuestionDialog";
import Spinner from "../elements/Spinner";
import BaseDialog from "./BaseDialog";
// we have a number of types defined from the Matrix spec which can't reasonably be altered here. // we have a number of types defined from the Matrix spec which can't reasonably be altered here.
/* eslint-disable camelcase */ /* eslint-disable camelcase */
@ -1046,7 +1048,6 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
if (this.unmounted) return; if (this.unmounted) return;
if (failed.length > 0) { if (failed.length > 0) {
const QuestionDialog = sdk.getComponent('dialogs.QuestionDialog');
Modal.createTrackedDialog('Invite Paste Fail', '', QuestionDialog, { Modal.createTrackedDialog('Invite Paste Fail', '', QuestionDialog, {
title: _t('Failed to find the following users'), title: _t('Failed to find the following users'),
description: _t( description: _t(
@ -1158,7 +1159,6 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
const toRender = sourceMembers.slice(0, showNum); const toRender = sourceMembers.slice(0, showNum);
const hasMore = toRender.length < sourceMembers.length; const hasMore = toRender.length < sourceMembers.length;
const AccessibleButton = sdk.getComponent("elements.AccessibleButton");
let showMore = null; let showMore = null;
if (hasMore) { if (hasMore) {
showMore = ( showMore = (
@ -1269,10 +1269,6 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
}; };
render() { render() {
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
const AccessibleButton = sdk.getComponent("elements.AccessibleButton");
const Spinner = sdk.getComponent("elements.Spinner");
let spinner = null; let spinner = null;
if (this.state.busy) { if (this.state.busy) {
spinner = <Spinner w={20} h={20} />; spinner = <Spinner w={20} h={20} />;

View File

@ -15,7 +15,6 @@ limitations under the License.
*/ */
import React from 'react'; import React from 'react';
import * as sdk from '../../../index';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
import { ensureDMExists } from "../../../createRoom"; import { ensureDMExists } from "../../../createRoom";
import { IDialogProps } from "./IDialogProps"; import { IDialogProps } from "./IDialogProps";
@ -26,6 +25,10 @@ import Markdown from '../../../Markdown';
import { replaceableComponent } from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";
import SettingsStore from "../../../settings/SettingsStore"; import SettingsStore from "../../../settings/SettingsStore";
import StyledRadioButton from "../elements/StyledRadioButton"; 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 { interface IProps extends IDialogProps {
mxEvent: MatrixEvent; mxEvent: MatrixEvent;
@ -239,11 +242,6 @@ export default class ReportEventDialog extends React.Component<IProps, IState> {
}; };
render() { 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; let error = null;
if (this.state.err) { if (this.state.err) {
error = <div className="error"> error = <div className="error">
@ -255,7 +253,7 @@ export default class ReportEventDialog extends React.Component<IProps, IState> {
if (this.state.busy) { if (this.state.busy) {
progress = ( progress = (
<div className="progress"> <div className="progress">
<Loader /> <Spinner />
</div> </div>
); );
} }

View File

@ -24,12 +24,12 @@ import GeneralRoomSettingsTab from "../settings/tabs/room/GeneralRoomSettingsTab
import SecurityRoomSettingsTab from "../settings/tabs/room/SecurityRoomSettingsTab"; import SecurityRoomSettingsTab from "../settings/tabs/room/SecurityRoomSettingsTab";
import NotificationSettingsTab from "../settings/tabs/room/NotificationSettingsTab"; import NotificationSettingsTab from "../settings/tabs/room/NotificationSettingsTab";
import BridgeSettingsTab from "../settings/tabs/room/BridgeSettingsTab"; import BridgeSettingsTab from "../settings/tabs/room/BridgeSettingsTab";
import * as sdk from "../../../index";
import { MatrixClientPeg } from "../../../MatrixClientPeg"; import { MatrixClientPeg } from "../../../MatrixClientPeg";
import dis from "../../../dispatcher/dispatcher"; import dis from "../../../dispatcher/dispatcher";
import SettingsStore from "../../../settings/SettingsStore"; import SettingsStore from "../../../settings/SettingsStore";
import { UIFeature } from "../../../settings/UIFeature"; import { UIFeature } from "../../../settings/UIFeature";
import { replaceableComponent } from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";
import BaseDialog from "./BaseDialog";
export const ROOM_GENERAL_TAB = "ROOM_GENERAL_TAB"; export const ROOM_GENERAL_TAB = "ROOM_GENERAL_TAB";
export const ROOM_SECURITY_TAB = "ROOM_SECURITY_TAB"; export const ROOM_SECURITY_TAB = "ROOM_SECURITY_TAB";
@ -119,8 +119,6 @@ export default class RoomSettingsDialog extends React.Component<IProps> {
} }
render() { render() {
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
const roomName = MatrixClientPeg.get().getRoom(this.props.roomId).name; const roomName = MatrixClientPeg.get().getRoom(this.props.roomId).name;
return ( return (
<BaseDialog <BaseDialog

View File

@ -22,7 +22,6 @@ import { User } from "matrix-js-sdk/src/models/user";
import { Group } from "matrix-js-sdk/src/models/group"; import { Group } from "matrix-js-sdk/src/models/group";
import { RoomMember } from "matrix-js-sdk/src/models/room-member"; import { RoomMember } from "matrix-js-sdk/src/models/room-member";
import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import * as sdk from '../../../index';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
import QRCode from "../elements/QRCode"; import QRCode from "../elements/QRCode";
import { RoomPermalinkCreator, makeGroupPermalink, makeUserPermalink } from "../../../utils/permalinks/Permalinks"; import { RoomPermalinkCreator, makeGroupPermalink, makeUserPermalink } from "../../../utils/permalinks/Permalinks";
@ -35,6 +34,8 @@ import { IDialogProps } from "./IDialogProps";
import SettingsStore from "../../../settings/SettingsStore"; import SettingsStore from "../../../settings/SettingsStore";
import { UIFeature } from "../../../settings/UIFeature"; import { UIFeature } from "../../../settings/UIFeature";
import { replaceableComponent } from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";
import BaseDialog from "./BaseDialog";
import GenericTextContextMenu from "../context_menus/GenericTextContextMenu.js";
const socials = [ const socials = [
{ {
@ -119,7 +120,6 @@ export default class ShareDialog extends React.PureComponent<IProps, IState> {
const successful = await copyPlaintext(this.getUrl()); const successful = await copyPlaintext(this.getUrl());
const buttonRect = target.getBoundingClientRect(); const buttonRect = target.getBoundingClientRect();
const GenericTextContextMenu = sdk.getComponent('context_menus.GenericTextContextMenu');
const { close } = ContextMenu.createMenu(GenericTextContextMenu, { const { close } = ContextMenu.createMenu(GenericTextContextMenu, {
...toRightOf(buttonRect, 2), ...toRightOf(buttonRect, 2),
message: successful ? _t('Copied!') : _t('Failed to copy'), message: successful ? _t('Copied!') : _t('Failed to copy'),
@ -230,7 +230,6 @@ export default class ShareDialog extends React.PureComponent<IProps, IState> {
</>; </>;
} }
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
return <BaseDialog return <BaseDialog
title={title} title={title}
className='mx_ShareDialog' className='mx_ShareDialog'

View File

@ -16,11 +16,12 @@ limitations under the License.
import url from 'url'; import url from 'url';
import React from 'react'; import React from 'react';
import * as sdk from '../../../index';
import { _t, pickBestLanguage } from '../../../languageHandler'; import { _t, pickBestLanguage } from '../../../languageHandler';
import { replaceableComponent } from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";
import { SERVICE_TYPES } from "matrix-js-sdk/src/service-types"; import { SERVICE_TYPES } from "matrix-js-sdk/src/service-types";
import DialogButtons from "../elements/DialogButtons";
import BaseDialog from "./BaseDialog";
interface ITermsCheckboxProps { interface ITermsCheckboxProps {
onChange: (url: string, checked: boolean) => void; onChange: (url: string, checked: boolean) => void;
@ -117,9 +118,6 @@ export default class TermsDialog extends React.PureComponent<ITermsDialogProps,
}; };
public render() { public render() {
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
const rows = []; const rows = [];
for (const policiesAndService of this.props.policiesAndServicePairs) { for (const policiesAndService of this.props.policiesAndServicePairs) {
const parsedBaseUrl = url.parse(policiesAndService.service.baseUrl); const parsedBaseUrl = url.parse(policiesAndService.service.baseUrl);

View File

@ -16,11 +16,12 @@ limitations under the License.
*/ */
import React from 'react'; import React from 'react';
import * as sdk from '../../../index';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
import filesize from "filesize"; import filesize from "filesize";
import { replaceableComponent } from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";
import { getBlobSafeMimeType } from '../../../utils/blobs'; import { getBlobSafeMimeType } from '../../../utils/blobs';
import BaseDialog from "./BaseDialog";
import DialogButtons from "../elements/DialogButtons";
interface IProps { interface IProps {
file: File; file: File;
@ -67,9 +68,6 @@ export default class UploadConfirmDialog extends React.Component<IProps> {
}; };
render() { render() {
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
let title; let title;
if (this.props.totalFiles > 1 && this.props.currentIndex !== undefined) { if (this.props.totalFiles > 1 && this.props.currentIndex !== undefined) {
title = _t( title = _t(

View File

@ -28,11 +28,11 @@ import PreferencesUserSettingsTab from "../settings/tabs/user/PreferencesUserSet
import VoiceUserSettingsTab from "../settings/tabs/user/VoiceUserSettingsTab"; import VoiceUserSettingsTab from "../settings/tabs/user/VoiceUserSettingsTab";
import HelpUserSettingsTab from "../settings/tabs/user/HelpUserSettingsTab"; import HelpUserSettingsTab from "../settings/tabs/user/HelpUserSettingsTab";
import FlairUserSettingsTab from "../settings/tabs/user/FlairUserSettingsTab"; import FlairUserSettingsTab from "../settings/tabs/user/FlairUserSettingsTab";
import * as sdk from "../../../index";
import SdkConfig from "../../../SdkConfig"; import SdkConfig from "../../../SdkConfig";
import MjolnirUserSettingsTab from "../settings/tabs/user/MjolnirUserSettingsTab"; import MjolnirUserSettingsTab from "../settings/tabs/user/MjolnirUserSettingsTab";
import { UIFeature } from "../../../settings/UIFeature"; import { UIFeature } from "../../../settings/UIFeature";
import { replaceableComponent } from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";
import BaseDialog from "./BaseDialog";
export enum UserTab { export enum UserTab {
General = "USER_GENERAL_TAB", General = "USER_GENERAL_TAB",
@ -162,8 +162,6 @@ export default class UserSettingsDialog extends React.Component<IProps, IState>
} }
render() { render() {
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
return ( return (
<BaseDialog <BaseDialog
className='mx_UserSettingsDialog' className='mx_UserSettingsDialog'

View File

@ -17,7 +17,7 @@ limitations under the License.
import { debounce } from "lodash"; import { debounce } from "lodash";
import classNames from 'classnames'; import classNames from 'classnames';
import React, { ChangeEvent, FormEvent } from 'react'; import React, { ChangeEvent, FormEvent } from 'react';
import { ISecretStorageKeyInfo } from "matrix-js-sdk/src"; import { ISecretStorageKeyInfo } from "matrix-js-sdk/src/crypto/api";
import * as sdk from '../../../../index'; import * as sdk from '../../../../index';
import { MatrixClientPeg } from '../../../../MatrixClientPeg'; import { MatrixClientPeg } from '../../../../MatrixClientPeg';

View File

@ -16,8 +16,9 @@ limitations under the License.
import React from 'react'; import React from 'react';
import { _t } from "../../../../languageHandler"; import { _t } from "../../../../languageHandler";
import * as sdk from "../../../../index";
import { replaceableComponent } from "../../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../../utils/replaceableComponent";
import BaseDialog from "../BaseDialog";
import DialogButtons from "../../elements/DialogButtons";
interface IProps { interface IProps {
onFinished: (success: boolean) => void; onFinished: (success: boolean) => void;
@ -34,9 +35,6 @@ export default class ConfirmDestroyCrossSigningDialog extends React.Component<IP
}; };
render() { render() {
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
return ( return (
<BaseDialog <BaseDialog
className='mx_ConfirmDestroyCrossSigningDialog' className='mx_ConfirmDestroyCrossSigningDialog'

View File

@ -41,7 +41,8 @@ import QuestionDialog from "../dialogs/QuestionDialog";
import UIStore from "../../../stores/UIStore"; import UIStore from "../../../stores/UIStore";
import { compare } from "../../../utils/strings"; import { compare } from "../../../utils/strings";
export const ALL_ROOMS = Symbol("ALL_ROOMS"); // XXX: We would ideally use a symbol here but we can't since we save this value to localStorage
export const ALL_ROOMS = "ALL_ROOMS";
const SETTING_NAME = "room_directory_servers"; const SETTING_NAME = "room_directory_servers";
@ -94,8 +95,7 @@ export interface IInstance {
fields: object; fields: object;
network_id: string; network_id: string;
// XXX: this is undocumented but we rely on it. // XXX: this is undocumented but we rely on it.
// we inject a fake entry with a symbolic instance_id. instance_id: string;
instance_id: string | symbol;
} }
export interface IProtocol { export interface IProtocol {
@ -112,8 +112,8 @@ export type Protocols = Record<string, IProtocol>;
interface IProps { interface IProps {
protocols: Protocols; protocols: Protocols;
selectedServerName: string; selectedServerName: string;
selectedInstanceId: string | symbol; selectedInstanceId: string;
onOptionChange(server: string, instanceId?: string | symbol): void; onOptionChange(server: string, instanceId?: string): void;
} }
// This dropdown sources homeservers from three places: // This dropdown sources homeservers from three places:
@ -171,7 +171,7 @@ const NetworkDropdown = ({ onOptionChange, protocols = {}, selectedServerName, s
const protocolsList = server === hsName ? Object.values(protocols) : []; const protocolsList = server === hsName ? Object.values(protocols) : [];
if (protocolsList.length > 0) { if (protocolsList.length > 0) {
// add a fake protocol with the ALL_ROOMS symbol // add a fake protocol with ALL_ROOMS
protocolsList.push({ protocolsList.push({
instances: [{ instances: [{
fields: [], fields: [],

View File

@ -14,7 +14,7 @@
limitations under the License. limitations under the License.
*/ */
import React from 'react'; import React, { ReactHTML } from 'react';
import { Key } from '../../../Keyboard'; import { Key } from '../../../Keyboard';
import classnames from 'classnames'; import classnames from 'classnames';
@ -29,7 +29,7 @@ export type ButtonEvent = React.MouseEvent<Element> | React.KeyboardEvent<Elemen
*/ */
interface IProps extends React.InputHTMLAttributes<Element> { interface IProps extends React.InputHTMLAttributes<Element> {
inputRef?: React.Ref<Element>; inputRef?: React.Ref<Element>;
element?: string; element?: keyof ReactHTML;
// The kind of button, similar to how Bootstrap works. // The kind of button, similar to how Bootstrap works.
// See available classes for AccessibleButton for options. // See available classes for AccessibleButton for options.
kind?: string; kind?: string;
@ -122,7 +122,7 @@ export default function AccessibleButton({
} }
AccessibleButton.defaultProps = { AccessibleButton.defaultProps = {
element: 'div', element: 'div' as keyof ReactHTML,
role: 'button', role: 'button',
tabIndex: 0, tabIndex: 0,
}; };

View File

@ -238,6 +238,7 @@ export default class AppTile extends React.Component {
case 'm.sticker': case 'm.sticker':
if (this._sgWidget.widgetApi.hasCapability(MatrixCapabilities.StickerSending)) { if (this._sgWidget.widgetApi.hasCapability(MatrixCapabilities.StickerSending)) {
dis.dispatch({ action: 'post_sticker_message', data: payload.data }); dis.dispatch({ action: 'post_sticker_message', data: payload.data });
dis.dispatch({ action: 'stickerpicker_close' });
} else { } else {
console.warn('Ignoring sticker message. Invalid capability'); console.warn('Ignoring sticker message. Invalid capability');
} }

View File

@ -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<IProps> {
private canvas: React.RefObject<HTMLCanvasElement> = 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 <canvas height={this.props.height} width={this.props.width} ref={this.canvas} />;
}
}

View File

@ -260,6 +260,7 @@ export default class Field extends React.PureComponent<PropShapes, IState> {
}); });
// Handle displaying feedback on validity // Handle displaying feedback on validity
// FIXME: Using an import will result in test failures
const Tooltip = sdk.getComponent("elements.Tooltip"); const Tooltip = sdk.getComponent("elements.Tooltip");
let fieldTooltip; let fieldTooltip;
if (tooltipContent || this.state.feedback) { if (tooltipContent || this.state.feedback) {

View File

@ -334,7 +334,7 @@ export default class ReplyThread extends React.Component {
events, events,
}); });
dis.fire(Action.FocusComposer); dis.fire(Action.FocusSendMessageComposer);
} }
render() { render() {

View File

@ -27,6 +27,7 @@ interface IProps {
value: string; value: string;
label?: string; label?: string;
placeholder?: string; placeholder?: string;
disabled?: boolean;
onChange?(value: string): void; onChange?(value: string): void;
} }
@ -68,6 +69,7 @@ export default class RoomAliasField extends React.PureComponent<IProps, IState>
onChange={this.onChange} onChange={this.onChange}
value={this.props.value.substring(1, this.props.value.length - this.props.domain.length - 1)} value={this.props.value.substring(1, this.props.value.length - this.props.domain.length - 1)}
maxLength={maxlength} maxLength={maxlength}
disabled={this.props.disabled}
/> />
); );
} }

View File

@ -17,11 +17,11 @@ limitations under the License.
import React from 'react'; import React from 'react';
import Dropdown from "../../views/elements/Dropdown"; import Dropdown from "../../views/elements/Dropdown";
import * as sdk from '../../../index';
import PlatformPeg from "../../../PlatformPeg"; import PlatformPeg from "../../../PlatformPeg";
import SettingsStore from "../../../settings/SettingsStore"; import SettingsStore from "../../../settings/SettingsStore";
import { _t } from "../../../languageHandler"; import { _t } from "../../../languageHandler";
import { replaceableComponent } from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";
import Spinner from "./Spinner";
function languageMatchesSearchQuery(query, language) { function languageMatchesSearchQuery(query, language) {
if (language.label.toUpperCase().includes(query.toUpperCase())) return true; if (language.label.toUpperCase().includes(query.toUpperCase())) return true;
@ -84,7 +84,6 @@ export default class SpellCheckLanguagesDropdown extends React.Component<SpellCh
render() { render() {
if (this.state.languages === null) { if (this.state.languages === null) {
const Spinner = sdk.getComponent('elements.Spinner');
return <Spinner />; return <Spinner />;
} }

View File

@ -17,7 +17,7 @@ limitations under the License.
import React from "react"; import React from "react";
import classNames from "classnames"; import classNames from "classnames";
import * as sdk from "../../../index"; import AccessibleButton from "./AccessibleButton";
interface IProps { interface IProps {
// Whether or not this toggle is in the 'on' position. // 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, "mx_ToggleSwitch_enabled": !disabled,
}); });
const AccessibleButton = sdk.getComponent("elements.AccessibleButton");
return ( return (
<AccessibleButton {...props} <AccessibleButton {...props}
className={classes} className={classes}

View File

@ -16,11 +16,11 @@ limitations under the License.
*/ */
import React from 'react'; import React from 'react';
import * as sdk from '../../../index';
import { replaceableComponent } from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";
import Tooltip from './Tooltip';
interface IProps { interface IProps {
helpText: string; helpText: React.ReactNode | string;
} }
interface IState { interface IState {
@ -49,7 +49,6 @@ export default class TooltipButton extends React.Component<IProps, IState> {
}; };
render() { render() {
const Tooltip = sdk.getComponent("elements.Tooltip");
const tip = this.state.hover ? <Tooltip const tip = this.state.hover ? <Tooltip
className="mx_TooltipButton_container" className="mx_TooltipButton_container"
tooltipClassName="mx_TooltipButton_helpText" tooltipClassName="mx_TooltipButton_helpText"

View File

@ -33,7 +33,7 @@ export const EMOJI_HEIGHT = 37;
export const EMOJIS_PER_ROW = 8; export const EMOJIS_PER_ROW = 8;
interface IProps { interface IProps {
selectedEmojis: Set<string>; selectedEmojis?: Set<string>;
showQuickReactions?: boolean; showQuickReactions?: boolean;
onChoose(unicode: string): boolean; onChoose(unicode: string): boolean;
} }

View File

@ -22,6 +22,7 @@ import EmojiPicker from "./EmojiPicker";
import { MatrixClientPeg } from "../../../MatrixClientPeg"; import { MatrixClientPeg } from "../../../MatrixClientPeg";
import dis from "../../../dispatcher/dispatcher"; import dis from "../../../dispatcher/dispatcher";
import { replaceableComponent } from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";
import { Action } from '../../../dispatcher/actions';
interface IProps { interface IProps {
mxEvent: MatrixEvent; mxEvent: MatrixEvent;
@ -93,6 +94,7 @@ class ReactionPicker extends React.Component<IProps, IState> {
this.props.mxEvent.getRoomId(), this.props.mxEvent.getRoomId(),
myReactions[reaction], myReactions[reaction],
); );
dis.dispatch({ action: Action.FocusAComposer });
// Tell the emoji picker not to bump this in the more frequently used list. // Tell the emoji picker not to bump this in the more frequently used list.
return false; return false;
} else { } else {
@ -104,6 +106,7 @@ class ReactionPicker extends React.Component<IProps, IState> {
}, },
}); });
dis.dispatch({ action: "message_sent" }); dis.dispatch({ action: "message_sent" });
dis.dispatch({ action: Action.FocusAComposer });
return true; return true;
} }
}; };

View File

@ -18,6 +18,7 @@ limitations under the License.
import React, { createRef } from 'react'; import React, { createRef } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Blurhash } from "react-blurhash";
import MFileBody from './MFileBody'; import MFileBody from './MFileBody';
import Modal from '../../../Modal'; import Modal from '../../../Modal';
@ -29,7 +30,6 @@ import MatrixClientContext from "../../../contexts/MatrixClientContext";
import InlineSpinner from '../elements/InlineSpinner'; import InlineSpinner from '../elements/InlineSpinner';
import { replaceableComponent } from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";
import { mediaFromContent } from "../../../customisations/Media"; import { mediaFromContent } from "../../../customisations/Media";
import BlurhashPlaceholder from "../elements/BlurhashPlaceholder";
import { BLURHASH_FIELD } from "../../../ContentMessages"; import { BLURHASH_FIELD } from "../../../ContentMessages";
@replaceableComponent("views.messages.MImageBody") @replaceableComponent("views.messages.MImageBody")
@ -436,7 +436,7 @@ export default class MImageBody extends React.Component {
// Overidden by MStickerBody // Overidden by MStickerBody
getPlaceholder(width, height) { getPlaceholder(width, height) {
const blurhash = this.props.mxEvent.getContent().info[BLURHASH_FIELD]; const blurhash = this.props.mxEvent.getContent().info[BLURHASH_FIELD];
if (blurhash) return <BlurhashPlaceholder blurhash={blurhash} width={width} height={height} />; if (blurhash) return <Blurhash hash={blurhash} width={width} height={height} />;
return <div className="mx_MImageBody_thumbnail_spinner"> return <div className="mx_MImageBody_thumbnail_spinner">
<InlineSpinner w={32} h={32} /> <InlineSpinner w={32} h={32} />
</div>; </div>;

View File

@ -17,7 +17,6 @@ limitations under the License.
import React from 'react'; import React from 'react';
import { MatrixEvent } from 'matrix-js-sdk/src'; import { MatrixEvent } from 'matrix-js-sdk/src';
import { MatrixClientPeg } from '../../../MatrixClientPeg'; import { MatrixClientPeg } from '../../../MatrixClientPeg';
import * as sdk from '../../../index';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
import { getNameForEventRoom, userLabelForEventRoom } import { getNameForEventRoom, userLabelForEventRoom }
from '../../../utils/KeyVerificationStateObserver'; from '../../../utils/KeyVerificationStateObserver';
@ -26,6 +25,7 @@ import { RightPanelPhases } from "../../../stores/RightPanelStorePhases";
import { Action } from "../../../dispatcher/actions"; import { Action } from "../../../dispatcher/actions";
import EventTileBubble from "./EventTileBubble"; import EventTileBubble from "./EventTileBubble";
import { replaceableComponent } from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";
import AccessibleButton from '../elements/AccessibleButton';
interface IProps { interface IProps {
mxEvent: MatrixEvent; mxEvent: MatrixEvent;
@ -115,8 +115,6 @@ export default class MKeyVerificationRequest extends React.Component<IProps> {
} }
public render() { public render() {
const AccessibleButton = sdk.getComponent("elements.AccessibleButton");
const { mxEvent } = this.props; const { mxEvent } = this.props;
const request = mxEvent.verificationRequest; const request = mxEvent.verificationRequest;

View File

@ -24,7 +24,7 @@ import { MatrixEvent } from "matrix-js-sdk/src/models/event";
interface IProps { interface IProps {
mxEvent: MatrixEvent; mxEvent: MatrixEvent;
onClick(): void; onClick?(): void;
enableFlair: boolean; enableFlair: boolean;
} }

View File

@ -45,7 +45,7 @@ import Spoiler from "../elements/Spoiler";
import QuestionDialog from "../dialogs/QuestionDialog"; import QuestionDialog from "../dialogs/QuestionDialog";
import MessageEditHistoryDialog from "../dialogs/MessageEditHistoryDialog"; import MessageEditHistoryDialog from "../dialogs/MessageEditHistoryDialog";
import EditMessageComposer from '../rooms/EditMessageComposer'; import EditMessageComposer from '../rooms/EditMessageComposer';
import LinkPreviewWidget from '../rooms/LinkPreviewWidget'; import LinkPreviewGroup from '../rooms/LinkPreviewGroup';
interface IProps { interface IProps {
/* the MatrixEvent to show */ /* the MatrixEvent to show */
@ -244,7 +244,11 @@ export default class TextualBody extends React.Component<IProps, IState> {
} }
private highlightCode(code: HTMLElement): void { private highlightCode(code: HTMLElement): void {
if (SettingsStore.getValue("enableSyntaxHighlightLanguageDetection")) { // Auto-detect language only if enabled and only for codeblocks
if (
SettingsStore.getValue("enableSyntaxHighlightLanguageDetection") &&
code.parentElement instanceof HTMLPreElement
) {
highlight.highlightBlock(code); highlight.highlightBlock(code);
} else { } else {
// Only syntax highlight if there's a class starting with language- // Only syntax highlight if there's a class starting with language-
@ -294,14 +298,8 @@ export default class TextualBody extends React.Component<IProps, IState> {
// pass only the first child which is the event tile otherwise this recurses on edited events // pass only the first child which is the event tile otherwise this recurses on edited events
let links = this.findLinks([this.contentRef.current]); let links = this.findLinks([this.contentRef.current]);
if (links.length) { if (links.length) {
// de-duplicate the links after stripping hashes as they don't affect the preview // de-duplicate the links using a set here maintains the order
// using a set here maintains the order links = Array.from(new Set(links));
links = Array.from(new Set(links.map(link => {
const url = new URL(link);
url.hash = "";
return url.toString();
})));
this.setState({ links }); this.setState({ links });
// lazy-load the hidden state of the preview widget from localstorage // lazy-load the hidden state of the preview widget from localstorage
@ -530,15 +528,12 @@ export default class TextualBody extends React.Component<IProps, IState> {
let widgets; let widgets;
if (this.state.links.length && !this.state.widgetHidden && this.props.showUrlPreview) { if (this.state.links.length && !this.state.widgetHidden && this.props.showUrlPreview) {
widgets = this.state.links.map((link)=>{ widgets = <LinkPreviewGroup
return <LinkPreviewWidget links={this.state.links}
key={link} mxEvent={this.props.mxEvent}
link={link} onCancelClick={this.onCancelClick}
mxEvent={this.props.mxEvent} onHeightChanged={this.props.onHeightChanged}
onCancelClick={this.onCancelClick} />;
onHeightChanged={this.props.onHeightChanged}
/>;
});
} }
switch (content.msgtype) { switch (content.msgtype) {

View File

@ -16,13 +16,13 @@ limitations under the License.
import React from "react"; import React from "react";
import * as sdk from "../../../index";
import { _t } from "../../../languageHandler"; import { _t } from "../../../languageHandler";
import { RoomMember } from "matrix-js-sdk/src/models/room-member"; import { RoomMember } from "matrix-js-sdk/src/models/room-member";
import { User } from "matrix-js-sdk/src/models/user"; import { User } from "matrix-js-sdk/src/models/user";
import AccessibleButton from "../elements/AccessibleButton";
import Spinner from "../elements/Spinner";
export const PendingActionSpinner = ({ text }) => { export const PendingActionSpinner = ({ text }) => {
const Spinner = sdk.getComponent('elements.Spinner');
return <div className="mx_EncryptionInfo_spinner"> return <div className="mx_EncryptionInfo_spinner">
<Spinner /> <Spinner />
{ text } { text }
@ -64,7 +64,6 @@ const EncryptionInfo: React.FC<IProps> = ({
} }
content = <PendingActionSpinner text={text} />; content = <PendingActionSpinner text={text} />;
} else { } else {
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
content = ( content = (
<AccessibleButton kind="primary" className="mx_UserInfo_wideButton" onClick={onStartVerification}> <AccessibleButton kind="primary" className="mx_UserInfo_wideButton" onClick={onStartVerification}>
{_t("Start Verification")} {_t("Start Verification")}

View File

@ -81,6 +81,7 @@ const EncryptionPanel: React.FC<IProps> = (props: IProps) => {
const changeHandler = useCallback(() => { const changeHandler = useCallback(() => {
// handle transitions -> cancelled for mismatches which fire a modal instead of showing a card // handle transitions -> cancelled for mismatches which fire a modal instead of showing a card
if (request && request.cancelled && MISMATCHES.includes(request.cancellationCode)) { if (request && request.cancelled && MISMATCHES.includes(request.cancellationCode)) {
// FIXME: Using an import will result in test failures
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog("Verification failed", "insecure", ErrorDialog, { Modal.createTrackedDialog("Verification failed", "insecure", ErrorDialog, {
headerImage: require("../../../../res/img/e2e/warning.svg"), headerImage: require("../../../../res/img/e2e/warning.svg"),

View File

@ -17,7 +17,6 @@ limitations under the License.
import React from "react"; import React from "react";
import { MatrixClientPeg } from "../../../MatrixClientPeg"; import { MatrixClientPeg } from "../../../MatrixClientPeg";
import * as sdk from '../../../index';
import { verificationMethods } from 'matrix-js-sdk/src/crypto'; import { verificationMethods } from 'matrix-js-sdk/src/crypto';
import { SCAN_QR_CODE_METHOD } from "matrix-js-sdk/src/crypto/verification/QRCode"; import { SCAN_QR_CODE_METHOD } from "matrix-js-sdk/src/crypto/verification/QRCode";
import { VerificationRequest } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; 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"; } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
import Spinner from "../elements/Spinner"; import Spinner from "../elements/Spinner";
import { replaceableComponent } from "../../../utils/replaceableComponent"; import { replaceableComponent } from "../../../utils/replaceableComponent";
import AccessibleButton from "../elements/AccessibleButton";
import VerificationShowSas from "../verification/VerificationShowSas";
// XXX: Should be defined in matrix-js-sdk // XXX: Should be defined in matrix-js-sdk
enum VerificationPhase { enum VerificationPhase {
@ -81,7 +82,6 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
const { member, request } = this.props; const { member, request } = this.props;
const showSAS: boolean = request.otherPartySupportsMethod(verificationMethods.SAS); const showSAS: boolean = request.otherPartySupportsMethod(verificationMethods.SAS);
const showQR: boolean = request.otherPartySupportsMethod(SCAN_QR_CODE_METHOD); const showQR: boolean = request.otherPartySupportsMethod(SCAN_QR_CODE_METHOD);
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
const brand = SdkConfig.get().brand; const brand = SdkConfig.get().brand;
const noCommonMethodError: JSX.Element = !showSAS && !showQR ? const noCommonMethodError: JSX.Element = !showSAS && !showQR ?
@ -195,7 +195,6 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
private renderQRReciprocatePhase() { private renderQRReciprocatePhase() {
const { member, request } = this.props; const { member, request } = this.props;
const AccessibleButton = sdk.getComponent("elements.AccessibleButton");
const description = request.isSelfVerification ? const description = request.isSelfVerification ?
_t("Almost there! Is your other session showing the same shield?") : _t("Almost there! Is your other session showing the same shield?") :
_t("Almost there! Is %(displayName)s showing the same shield?", { _t("Almost there! Is %(displayName)s showing the same shield?", {
@ -265,7 +264,6 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
}); });
} }
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
return ( return (
<div className="mx_UserInfo_container mx_VerificationPanel_verified_section"> <div className="mx_UserInfo_container mx_VerificationPanel_verified_section">
<h3>{_t("Verified")}</h3> <h3>{_t("Verified")}</h3>
@ -282,8 +280,6 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
private renderCancelledPhase() { private renderCancelledPhase() {
const { member, request } = this.props; const { member, request } = this.props;
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
let startAgainInstruction: string; let startAgainInstruction: string;
if (request.isSelfVerification) { if (request.isSelfVerification) {
startAgainInstruction = _t("Start verification again from the notification."); startAgainInstruction = _t("Start verification again from the notification.");
@ -332,7 +328,6 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
case verificationMethods.RECIPROCATE_QR_CODE: case verificationMethods.RECIPROCATE_QR_CODE:
return this.renderQRReciprocatePhase(); return this.renderQRReciprocatePhase();
case verificationMethods.SAS: { case verificationMethods.SAS: {
const VerificationShowSas = sdk.getComponent('views.verification.VerificationShowSas');
const emojis = this.state.sasEvent ? const emojis = this.state.sasEvent ?
<VerificationShowSas <VerificationShowSas
displayName={displayName} displayName={displayName}

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