Merge branch 'develop' into matthew/fix_logging

This commit is contained in:
Matthew Hodgson 2019-11-26 10:19:48 +00:00 committed by GitHub
commit 9f455fae4b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
136 changed files with 1483 additions and 775 deletions

View file

@ -13,7 +13,6 @@
], ],
"transform-class-properties", "transform-class-properties",
"transform-object-rest-spread", "transform-object-rest-spread",
"transform-async-to-bluebird",
"transform-runtime", "transform-runtime",
"add-module-exports", "add-module-exports",
"syntax-dynamic-import" "syntax-dynamic-import"

View file

@ -1,3 +1,116 @@
Changes in [1.7.3](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.7.3) (2019-11-25)
===================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.7.3-rc.2...v1.7.3)
* No changes since rc.2
Changes in [1.7.3-rc.2](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.7.3-rc.2) (2019-11-22)
=============================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.7.3-rc.1...v1.7.3-rc.2)
* Fix double date separator for room upgrade tiles
[\#3663](https://github.com/matrix-org/matrix-react-sdk/pull/3663)
* Show m.room.create event before the ELS on room upgrade
[\#3660](https://github.com/matrix-org/matrix-react-sdk/pull/3660)
* Make addEventListener conditional
[\#3659](https://github.com/matrix-org/matrix-react-sdk/pull/3659)
* Fix e2e icons
[\#3658](https://github.com/matrix-org/matrix-react-sdk/pull/3658)
Changes in [1.7.3-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.7.3-rc.1) (2019-11-20)
=============================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.7.2...v1.7.3-rc.1)
* Fix positioning, size, and colour of the composer e2e icon
[\#3641](https://github.com/matrix-org/matrix-react-sdk/pull/3641)
* upgrade nunito from 3.500 to 3.504
[\#3639](https://github.com/matrix-org/matrix-react-sdk/pull/3639)
* Wire up the widget permission prompt to the cross-platform setting
[\#3630](https://github.com/matrix-org/matrix-react-sdk/pull/3630)
* Get theme automatically from system setting
[\#3637](https://github.com/matrix-org/matrix-react-sdk/pull/3637)
* Update code style for our 90 char life
[\#3636](https://github.com/matrix-org/matrix-react-sdk/pull/3636)
* use general warning icon instead of e2e one for room status
[\#3633](https://github.com/matrix-org/matrix-react-sdk/pull/3633)
* Add support for platform specific event indexing and search
[\#3550](https://github.com/matrix-org/matrix-react-sdk/pull/3550)
* Update from Weblate
[\#3635](https://github.com/matrix-org/matrix-react-sdk/pull/3635)
* Use a settings watcher to set the theme
[\#3634](https://github.com/matrix-org/matrix-react-sdk/pull/3634)
* Merge the `feature_user_info_panel` flag into `feature_dm_verification`
[\#3632](https://github.com/matrix-org/matrix-react-sdk/pull/3632)
* Fix some styling regressions in member panel
[\#3631](https://github.com/matrix-org/matrix-react-sdk/pull/3631)
* Add a bit more safety around breadcrumbs
[\#3629](https://github.com/matrix-org/matrix-react-sdk/pull/3629)
* Ensure widgets always have a sender associated with them
[\#3628](https://github.com/matrix-org/matrix-react-sdk/pull/3628)
* re-add missing case of codepath
[\#3627](https://github.com/matrix-org/matrix-react-sdk/pull/3627)
* Implement the bulk of the new widget permission prompt design
[\#3622](https://github.com/matrix-org/matrix-react-sdk/pull/3622)
* Relax identity server discovery error handling
[\#3588](https://github.com/matrix-org/matrix-react-sdk/pull/3588)
* Add cross-signing feature flag
[\#3626](https://github.com/matrix-org/matrix-react-sdk/pull/3626)
* Attempt number two at ripping out Bluebird from rageshake.js
[\#3624](https://github.com/matrix-org/matrix-react-sdk/pull/3624)
* Update from Weblate
[\#3625](https://github.com/matrix-org/matrix-react-sdk/pull/3625)
* Remove Bluebird: phase 2.1
[\#3618](https://github.com/matrix-org/matrix-react-sdk/pull/3618)
* Add better error handling to Synapse user deactivation
[\#3619](https://github.com/matrix-org/matrix-react-sdk/pull/3619)
* New design for member panel
[\#3620](https://github.com/matrix-org/matrix-react-sdk/pull/3620)
* Show server details on login for unreachable homeserver
[\#3617](https://github.com/matrix-org/matrix-react-sdk/pull/3617)
* Add a function to get the "base" theme for a theme
[\#3615](https://github.com/matrix-org/matrix-react-sdk/pull/3615)
* Remove Bluebird: phase 2
[\#3616](https://github.com/matrix-org/matrix-react-sdk/pull/3616)
* Remove Bluebird: phase 1
[\#3612](https://github.com/matrix-org/matrix-react-sdk/pull/3612)
* Move notification count to in front of the room name in the page title
[\#3613](https://github.com/matrix-org/matrix-react-sdk/pull/3613)
* Add some logging/recovery for lost rooms
[\#3614](https://github.com/matrix-org/matrix-react-sdk/pull/3614)
* Add Mjolnir ban list support
[\#3585](https://github.com/matrix-org/matrix-react-sdk/pull/3585)
* Improve room switching performance with alias cache
[\#3610](https://github.com/matrix-org/matrix-react-sdk/pull/3610)
* Fix draw order when hovering composer format buttons
[\#3609](https://github.com/matrix-org/matrix-react-sdk/pull/3609)
* Use a ternary operator instead of relying on AND semantics in
EditHistoryDialog
[\#3606](https://github.com/matrix-org/matrix-react-sdk/pull/3606)
* Update from Weblate
[\#3608](https://github.com/matrix-org/matrix-react-sdk/pull/3608)
* Fix HTML fallback in replies
[\#3607](https://github.com/matrix-org/matrix-react-sdk/pull/3607)
* Fix rounded corners for the formatting toolbar
[\#3605](https://github.com/matrix-org/matrix-react-sdk/pull/3605)
* Check for a message type before assuming it is a room message
[\#3604](https://github.com/matrix-org/matrix-react-sdk/pull/3604)
* Remove lint comments about no-descending-specificity
[\#3603](https://github.com/matrix-org/matrix-react-sdk/pull/3603)
* Show verification requests in the timeline
[\#3601](https://github.com/matrix-org/matrix-react-sdk/pull/3601)
* Match identity server registration to the IS r0.3.0 spec
[\#3602](https://github.com/matrix-org/matrix-react-sdk/pull/3602)
* Restore thumbs after variation selector removal
[\#3600](https://github.com/matrix-org/matrix-react-sdk/pull/3600)
* Fix breadcrumbs so the bar is a toolbar and the buttons are buttons.
[\#3599](https://github.com/matrix-org/matrix-react-sdk/pull/3599)
* Now that part of spacing is padding, make it smaller when collapsed
[\#3597](https://github.com/matrix-org/matrix-react-sdk/pull/3597)
* Remove variation selectors from quick reactions
[\#3598](https://github.com/matrix-org/matrix-react-sdk/pull/3598)
* Fix linkify imports
[\#3595](https://github.com/matrix-org/matrix-react-sdk/pull/3595)
Changes in [1.7.2](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.7.2) (2019-11-06) Changes in [1.7.2](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.7.2) (2019-11-06)
=================================================================================================== ===================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.7.1...v1.7.2) [Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.7.1...v1.7.2)

View file

@ -2,8 +2,7 @@
The CIDER editor is a custom editor written for Riot. The CIDER editor is a custom editor written for Riot.
Most of the code can be found in the `/editor/` directory of the `matrix-react-sdk` project. Most of the code can be found in the `/editor/` directory of the `matrix-react-sdk` project.
It is used to power the composer to edit messages, It is used to power the composer main composer (both to send and edit messages), and might be used for other usecases where autocomplete is desired (invite box, ...).
and will soon be used as the main composer to send messages as well.
## High-level overview. ## High-level overview.

View file

@ -1,6 +1,6 @@
{ {
"name": "matrix-react-sdk", "name": "matrix-react-sdk",
"version": "1.7.2", "version": "1.7.3",
"description": "SDK for matrix.org using React", "description": "SDK for matrix.org using React",
"author": "matrix.org", "author": "matrix.org",
"repository": { "repository": {
@ -60,7 +60,6 @@
"dependencies": { "dependencies": {
"babel-plugin-syntax-dynamic-import": "^6.18.0", "babel-plugin-syntax-dynamic-import": "^6.18.0",
"babel-runtime": "^6.26.0", "babel-runtime": "^6.26.0",
"bluebird": "^3.5.0",
"blueimp-canvas-to-blob": "^3.5.0", "blueimp-canvas-to-blob": "^3.5.0",
"browser-encrypt-attachment": "^0.3.0", "browser-encrypt-attachment": "^0.3.0",
"browser-request": "^0.3.3", "browser-request": "^0.3.3",
@ -89,7 +88,7 @@
"linkifyjs": "^2.1.6", "linkifyjs": "^2.1.6",
"lodash": "^4.17.14", "lodash": "^4.17.14",
"lolex": "4.2", "lolex": "4.2",
"matrix-js-sdk": "2.4.3", "matrix-js-sdk": "2.4.4",
"optimist": "^0.6.1", "optimist": "^0.6.1",
"pako": "^1.0.5", "pako": "^1.0.5",
"png-chunks-extract": "^1.0.0", "png-chunks-extract": "^1.0.0",
@ -120,7 +119,6 @@
"babel-eslint": "^10.0.1", "babel-eslint": "^10.0.1",
"babel-loader": "^7.1.5", "babel-loader": "^7.1.5",
"babel-plugin-add-module-exports": "^0.2.1", "babel-plugin-add-module-exports": "^0.2.1",
"babel-plugin-transform-async-to-bluebird": "^1.1.1",
"babel-plugin-transform-builtin-extend": "^1.1.2", "babel-plugin-transform-builtin-extend": "^1.1.2",
"babel-plugin-transform-class-properties": "^6.24.1", "babel-plugin-transform-class-properties": "^6.24.1",
"babel-plugin-transform-object-rest-spread": "^6.26.0", "babel-plugin-transform-object-rest-spread": "^6.26.0",
@ -135,6 +133,7 @@
"eslint": "^5.12.0", "eslint": "^5.12.0",
"eslint-config-google": "^0.7.1", "eslint-config-google": "^0.7.1",
"eslint-plugin-babel": "^5.2.1", "eslint-plugin-babel": "^5.2.1",
"eslint-plugin-jest": "^23.0.4",
"eslint-plugin-flowtype": "^2.30.0", "eslint-plugin-flowtype": "^2.30.0",
"eslint-plugin-react": "^7.7.0", "eslint-plugin-react": "^7.7.0",
"eslint-plugin-react-hooks": "^2.0.1", "eslint-plugin-react-hooks": "^2.0.1",

View file

@ -25,6 +25,7 @@
@import "./structures/_TabbedView.scss"; @import "./structures/_TabbedView.scss";
@import "./structures/_TagPanel.scss"; @import "./structures/_TagPanel.scss";
@import "./structures/_TagPanelButtons.scss"; @import "./structures/_TagPanelButtons.scss";
@import "./structures/_ToastContainer.scss";
@import "./structures/_TopLeftMenuButton.scss"; @import "./structures/_TopLeftMenuButton.scss";
@import "./structures/_UploadBar.scss"; @import "./structures/_UploadBar.scss";
@import "./structures/_ViewSource.scss"; @import "./structures/_ViewSource.scss";
@ -48,6 +49,7 @@
@import "./views/context_menus/_StatusMessageContextMenu.scss"; @import "./views/context_menus/_StatusMessageContextMenu.scss";
@import "./views/context_menus/_TagTileContextMenu.scss"; @import "./views/context_menus/_TagTileContextMenu.scss";
@import "./views/context_menus/_TopLeftMenu.scss"; @import "./views/context_menus/_TopLeftMenu.scss";
@import "./views/context_menus/_WidgetContextMenu.scss";
@import "./views/dialogs/_AddressPickerDialog.scss"; @import "./views/dialogs/_AddressPickerDialog.scss";
@import "./views/dialogs/_Analytics.scss"; @import "./views/dialogs/_Analytics.scss";
@import "./views/dialogs/_ChangelogDialog.scss"; @import "./views/dialogs/_ChangelogDialog.scss";
@ -90,6 +92,7 @@
@import "./views/elements/_ErrorBoundary.scss"; @import "./views/elements/_ErrorBoundary.scss";
@import "./views/elements/_EventListSummary.scss"; @import "./views/elements/_EventListSummary.scss";
@import "./views/elements/_Field.scss"; @import "./views/elements/_Field.scss";
@import "./views/elements/_FormButton.scss";
@import "./views/elements/_IconButton.scss"; @import "./views/elements/_IconButton.scss";
@import "./views/elements/_ImageView.scss"; @import "./views/elements/_ImageView.scss";
@import "./views/elements/_InlineSpinner.scss"; @import "./views/elements/_InlineSpinner.scss";
@ -172,7 +175,7 @@
@import "./views/rooms/_WhoIsTypingTile.scss"; @import "./views/rooms/_WhoIsTypingTile.scss";
@import "./views/settings/_DevicesPanel.scss"; @import "./views/settings/_DevicesPanel.scss";
@import "./views/settings/_EmailAddresses.scss"; @import "./views/settings/_EmailAddresses.scss";
@import "./views/settings/_IntegrationsManager.scss"; @import "./views/settings/_IntegrationManager.scss";
@import "./views/settings/_KeyBackupPanel.scss"; @import "./views/settings/_KeyBackupPanel.scss";
@import "./views/settings/_Notifications.scss"; @import "./views/settings/_Notifications.scss";
@import "./views/settings/_PhoneNumbers.scss"; @import "./views/settings/_PhoneNumbers.scss";

View file

@ -0,0 +1,98 @@
/*
Copyright 2019 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_ToastContainer {
position: absolute;
top: 0;
left: 70px;
z-index: 101;
padding: 4px;
display: grid;
grid-template-rows: 1fr 14px 6px;
&.mx_ToastContainer_stacked::before {
content: "";
margin: 0 4px;
grid-row: 2 / 4;
grid-column: 1;
background-color: white;
box-shadow: 0px 4px 12px $menu-box-shadow-color;
border-radius: 8px;
}
.mx_Toast_toast {
grid-row: 1 / 3;
grid-column: 1;
color: $primary-fg-color;
background-color: $primary-bg-color;
box-shadow: 0px 4px 12px $menu-box-shadow-color;
border-radius: 8px;
overflow: hidden;
display: grid;
grid-template-columns: 20px 1fr;
column-gap: 10px;
row-gap: 4px;
padding: 8px;
padding-right: 16px;
&.mx_Toast_hasIcon {
&::after {
content: "";
width: 20px;
height: 20px;
grid-column: 1;
grid-row: 1;
mask-size: 100%;
mask-repeat: no-repeat;
}
&.mx_Toast_icon_verification::after {
mask-image: url("$(res)/img/e2e/normal.svg");
background-color: $primary-fg-color;
}
h2, .mx_Toast_body {
grid-column: 2;
}
}
h2 {
grid-column: 1 / 3;
grid-row: 1;
margin: 0;
font-size: 15px;
font-weight: 600;
}
.mx_Toast_body {
grid-column: 1 / 3;
grid-row: 2;
}
.mx_Toast_buttons {
display: flex;
}
.mx_Toast_description {
max-width: 400px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
margin: 4px 0 11px 0;
font-size: 12px;
}
}
}

View file

@ -0,0 +1,36 @@
/*
Copyright 2019 The Matrix.org Foundaction 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_WidgetContextMenu {
padding: 6px;
.mx_WidgetContextMenu_option {
padding: 3px 6px 3px 6px;
cursor: pointer;
white-space: nowrap;
}
.mx_WidgetContextMenu_separator {
margin-top: 0;
margin-bottom: 0;
border-bottom-style: none;
border-left-style: none;
border-right-style: none;
border-top-style: solid;
border-top-width: 1px;
border-color: $menu-border-color;
}
}

View file

@ -16,10 +16,10 @@ limitations under the License.
/* /*
* To avoid visual glitching of two modals stacking briefly, we customise the * To avoid visual glitching of two modals stacking briefly, we customise the
* terms dialog sizing when it will appear for the integrations manager so that * terms dialog sizing when it will appear for the integration manager so that
* it gets the same basic size as the IM's own modal. * it gets the same basic size as the IM's own modal.
*/ */
.mx_TermsDialog_forIntegrationsManager .mx_Dialog { .mx_TermsDialog_forIntegrationManager .mx_Dialog {
width: 60%; width: 60%;
height: 70%; height: 70%;
box-sizing: border-box; box-sizing: border-box;

View file

@ -0,0 +1,36 @@
/*
Copyright 2019 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_FormButton {
line-height: 16px;
padding: 5px 15px;
font-size: 12px;
height: min-content;
&:not(:last-child) {
margin-right: 8px;
}
&.mx_AccessibleButton_kind_primary {
color: $accent-color;
background-color: $accent-bg-color;
}
&.mx_AccessibleButton_kind_danger {
color: $notice-primary-color;
background-color: $notice-primary-bg-color;
}
}

View file

@ -65,23 +65,6 @@ limitations under the License.
.mx_KeyVerification_buttons { .mx_KeyVerification_buttons {
align-items: center; align-items: center;
display: flex; display: flex;
.mx_AccessibleButton_kind_decline {
color: $notice-primary-color;
background-color: $notice-primary-bg-color;
}
.mx_AccessibleButton_kind_accept {
color: $accent-color;
background-color: $accent-bg-color;
}
[role=button] {
margin: 10px;
padding: 7px 15px;
border-radius: 5px;
height: min-content;
}
} }
.mx_KeyVerification_state { .mx_KeyVerification_state {

View file

@ -153,40 +153,12 @@ $AppsDrawerBodyHeight: 273px;
background-color: $accent-color; background-color: $accent-color;
} }
.mx_AppTileMenuBar_iconButton.mx_AppTileMenuBar_iconButton_reload {
mask-image: url('$(res)/img/feather-customised/widget/refresh.svg');
mask-size: 100%;
}
.mx_AppTileMenuBar_iconButton.mx_AppTileMenuBar_iconButton_popout { .mx_AppTileMenuBar_iconButton.mx_AppTileMenuBar_iconButton_popout {
mask-image: url('$(res)/img/feather-customised/widget/external-link.svg'); mask-image: url('$(res)/img/feather-customised/widget/external-link.svg');
} }
.mx_AppTileMenuBar_iconButton.mx_AppTileMenuBar_iconButton_snapshot { .mx_AppTileMenuBar_iconButton.mx_AppTileMenuBar_iconButton_menu {
mask-image: url('$(res)/img/feather-customised/widget/camera.svg'); mask-image: url('$(res)/img/icon_context.svg');
}
.mx_AppTileMenuBar_iconButton.mx_AppTileMenuBar_iconButton_edit {
mask-image: url('$(res)/img/feather-customised/widget/edit.svg');
}
.mx_AppTileMenuBar_iconButton.mx_AppTileMenuBar_iconButton_delete {
mask-image: url('$(res)/img/feather-customised/widget/bin.svg');
background-color: $warning-color;
}
.mx_AppTileMenuBar_iconButton.mx_AppTileMenuBar_iconButton_cancel {
mask-image: url('$(res)/img/feather-customised/widget/x-circle.svg');
}
/* delete ? */
.mx_AppTileMenuBarWidget {
cursor: pointer;
width: 10px;
height: 10px;
padding: 1px;
transition-duration: 500ms;
border: 1px solid transparent;
} }
.mx_AppTileMenuBarWidgetDelete { .mx_AppTileMenuBarWidgetDelete {

View file

@ -15,8 +15,8 @@ limitations under the License.
*/ */
.mx_E2EIcon { .mx_E2EIcon {
width: 25px; width: 16px;
height: 25px; height: 16px;
margin: 0 9px; margin: 0 9px;
position: relative; position: relative;
display: block; display: block;
@ -30,16 +30,14 @@ limitations under the License.
bottom: 0; bottom: 0;
left: 0; left: 0;
right: 0; right: 0;
mask-repeat: no-repeat; background-repeat: no-repeat;
mask-size: contain; background-size: contain;
} }
.mx_E2EIcon_verified::after { .mx_E2EIcon_verified::after {
mask-image: url('$(res)/img/e2e/verified.svg'); background-image: url('$(res)/img/e2e/verified.svg');
background-color: $accent-color;
} }
.mx_E2EIcon_warning::after { .mx_E2EIcon_warning::after {
mask-image: url('$(res)/img/e2e/warning.svg'); background-image: url('$(res)/img/e2e/warning.svg');
background-color: $warning-color;
} }

View file

@ -23,10 +23,6 @@ limitations under the License.
padding-left: 84px; padding-left: 84px;
} }
.mx_MessageComposer_wrapper.mx_MessageComposer_hasE2EIcon {
padding-left: 109px;
}
.mx_MessageComposer_replaced_wrapper { .mx_MessageComposer_replaced_wrapper {
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
@ -78,10 +74,8 @@ limitations under the License.
.mx_MessageComposer_e2eIcon.mx_E2EIcon { .mx_MessageComposer_e2eIcon.mx_E2EIcon {
position: absolute; position: absolute;
left: 60px; left: 60px;
margin-right: 0; // Counteract the E2EIcon class
&::after { margin-left: 3px; // Counteract the E2EIcon class
background-color: $composer-e2e-icon-color;
}
} }
.mx_MessageComposer_noperm_error { .mx_MessageComposer_noperm_error {

View file

@ -17,6 +17,10 @@ limitations under the License.
.mx_RoomHeader { .mx_RoomHeader {
flex: 0 0 52px; flex: 0 0 52px;
border-bottom: 1px solid $primary-hairline-color; border-bottom: 1px solid $primary-hairline-color;
.mx_E2EIcon {
margin: 0 5px;
}
} }
.mx_RoomHeader_wrapper { .mx_RoomHeader_wrapper {

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
.mx_IntegrationsManager .mx_Dialog { .mx_IntegrationManager .mx_Dialog {
width: 60%; width: 60%;
height: 70%; height: 70%;
overflow: hidden; overflow: hidden;
@ -23,22 +23,22 @@ limitations under the License.
max-height: initial; max-height: initial;
} }
.mx_IntegrationsManager iframe { .mx_IntegrationManager iframe {
background-color: #fff; background-color: #fff;
border: 0px; border: 0px;
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
.mx_IntegrationsManager_loading h3 { .mx_IntegrationManager_loading h3 {
text-align: center; text-align: center;
} }
.mx_IntegrationsManager_error { .mx_IntegrationManager_error {
text-align: center; text-align: center;
padding-top: 20px; padding-top: 20px;
} }
.mx_IntegrationsManager_error h3 { .mx_IntegrationManager_error h3 {
color: $warning-color; color: $warning-color;
} }

View file

@ -14,10 +14,6 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
.mx_SetIntegrationManager .mx_Field_input {
@mixin mx_Settings_fullWidthField;
}
.mx_SetIntegrationManager { .mx_SetIntegrationManager {
margin-top: 10px; margin-top: 10px;
margin-bottom: 10px; margin-bottom: 10px;
@ -32,6 +28,10 @@ limitations under the License.
padding-left: 5px; padding-left: 5px;
} }
.mx_SetIntegrationManager_tooltip { .mx_SetIntegrationManager .mx_ToggleSwitch {
@mixin mx_Settings_tooltip; display: inline-block;
float: right;
top: 9px;
@mixin mx_Settings_fullWidthField;
} }

View file

@ -1,12 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<svg <path fill-rule="evenodd" clip-rule="evenodd" d="M12 21C12 21 21 17.2 21 11.5V4.85L12 2L3 4.85V11.5C3 17.2 12 21 12 21Z" fill="#03B381" stroke="white" stroke-linecap="round" stroke-linejoin="round"/>
xmlns="http://www.w3.org/2000/svg" <path fill-rule="evenodd" clip-rule="evenodd" d="M17.2268 7.80652C17.6053 8.17175 17.6053 8.7639 17.2268 9.12913L11.4013 14.7502C11.0228 15.1154 10.4091 15.1154 10.0306 14.7502L10.0145 14.7342C10.0084 14.7286 10.0023 14.7229 9.99635 14.7171L7.32348 12.1381C6.92604 11.7546 6.92604 11.1328 7.32348 10.7493C7.72091 10.3658 8.36528 10.3658 8.76272 10.7493L10.7838 12.6995L15.8561 7.80652C16.2346 7.44129 16.8483 7.44129 17.2268 7.80652Z" fill="white"/>
width="24"
height="24"
viewBox="0 0 24 24"
fill="none">
<path
style="stroke:none;fill:#03b381;fill-opacity:1"
d="M 12 2 L 3 4.8496094 L 3 11.5 C 3 17.2 12 21 12 21 C 12 21 21 17.2 21 11.5 L 21 4.8496094 L 12 2 z M 16.541016 7.5332031 C 16.789066 7.5332031 17.037312 7.6240256 17.226562 7.8066406 C 17.605062 8.1718706 17.605063 8.7636762 17.226562 9.1289062 L 11.400391 14.75 C 11.021891 15.1152 10.40975 15.1152 10.03125 14.75 L 10.013672 14.734375 C 10.007572 14.728775 10.002044 14.722597 9.9960938 14.716797 L 7.3242188 12.138672 C 6.9267788 11.755172 6.9267788 11.1335 7.3242188 10.75 C 7.7216487 10.3665 8.3662319 10.3665 8.7636719 10.75 L 10.783203 12.699219 L 15.855469 7.8066406 C 16.044719 7.6240256 16.292966 7.5332031 16.541016 7.5332031 z "
id="path2" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 902 B

After

Width:  |  Height:  |  Size: 753 B

View file

@ -1,12 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<svg <path fill-rule="evenodd" clip-rule="evenodd" d="M12 21C12 21 21 17.2 21 11.5V4.85L12 2L3 4.85V11.5C3 17.2 12 21 12 21Z" fill="#FF4B55" stroke="white" stroke-linecap="round" stroke-linejoin="round"/>
xmlns="http://www.w3.org/2000/svg" <rect x="10.8" y="5.5" width="2.5" height="8" rx="1.25" fill="white"/>
width="24" <rect x="10.8" y="15" width="2.5" height="2.5" rx="1.25" fill="white"/>
height="24"
fill="none"
viewBox="0 0 24 24">
<path
style="fill-opacity:1;fill:#ff4b55;stroke:none"
d="M 12 2 L 3 4.8496094 L 3 11.5 C 3 17.2 12 21 12 21 C 12 21 21 17.2 21 11.5 L 21 4.8496094 L 12 2 z M 12.050781 5.5 C 12.743281 5.5 13.300781 6.0575 13.300781 6.75 L 13.300781 12.25 C 13.300781 12.9425 12.743281 13.5 12.050781 13.5 C 11.358281 13.5 10.800781 12.9425 10.800781 12.25 L 10.800781 6.75 C 10.800781 6.0575 11.358281 5.5 12.050781 5.5 z M 12.050781 15 C 12.743281 15 13.300781 15.5575 13.300781 16.25 C 13.300781 16.9425 12.743281 17.5 12.050781 17.5 C 11.358281 17.5 10.800781 16.9425 10.800781 16.25 C 10.800781 15.5575 11.358281 15 12.050781 15 z "
id="path2" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 824 B

After

Width:  |  Height:  |  Size: 446 B

View file

@ -1,65 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="11.014242"
height="12"
viewBox="0 0 11.014242 12"
version="1.1"
id="svg6"
sodipodi:docname="bin.svg"
inkscape:version="0.92.3 (2405546, 2018-03-11)">
<metadata
id="metadata12">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs10" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="750"
inkscape:window-height="480"
id="namedview8"
showgrid="false"
fit-margin-top="0.5"
fit-margin-left="0.5"
fit-margin-bottom="0.5"
fit-margin-right="0.5"
inkscape:zoom="19.666667"
inkscape:cx="5.5071212"
inkscape:cy="6"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="0"
inkscape:current-layer="svg6" />
<g
id="g4"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
transform="translate(1.0071212)">
<path
d="M 0,3 H 9 M 8,3 v 7 A 1,1 0 0 1 7,11 H 2 A 1,1 0 0 1 1,10 V 3 M 2.5,3 V 2 a 1,1 0 0 1 1,-1 h 2 a 1,1 0 0 1 1,1 v 1 m -3,2.5 v 3 m 2,-3 v 3"
id="path2"
style="stroke:#000000;stroke-opacity:1"
inkscape:connector-curvature="0" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2 KiB

View file

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="11" viewBox="0 0 13 11">
<g fill="none" fill-rule="evenodd" stroke="#212121" stroke-linecap="round" stroke-linejoin="round" transform="translate(1 1)">
<path d="M11 8a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1V2.5a1 1 0 0 1 1-1h2L4 0h3l1 1.5h2a1 1 0 0 1 1 1V8z"/>
<circle cx="5.5" cy="5" r="2"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 378 B

View file

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="11" viewBox="0 0 12 11">
<g fill="none" fill-rule="evenodd" stroke="#212121" stroke-linecap="round" stroke-linejoin="round">
<path d="M10 6.33V9a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h2.67"/>
<path d="M9 0l2 2-5 5H4V5z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 324 B

View file

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="11" viewBox="0 0 13 11">
<g fill="none" fill-rule="evenodd" stroke="#212121" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 1.5v3H9M1 9.5v-3h3"/>
<path d="M2.255 4A4.5 4.5 0 0 1 9.68 2.32L12 4.5m-11 2l2.32 2.18A4.5 4.5 0 0 0 10.745 7"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 346 B

View file

@ -1,6 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12">
<g fill="none" fill-rule="evenodd" stroke="#212121" stroke-linecap="round" stroke-linejoin="round" transform="translate(1 1)">
<circle cx="5" cy="5" r="5"/>
<path d="M6.5 3.5l-3 3M3.5 3.5l3 3"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 315 B

View file

@ -12,9 +12,9 @@ $monospace-font-family: Inconsolata, Twemoji, 'Apple Color Emoji', 'Segoe UI Emo
// unified palette // unified palette
// try to use these colors when possible // try to use these colors when possible
$accent-color: #03b381; $accent-color: #03b381;
$accent-bg-color: rgba(115, 247, 91, 0.08); $accent-bg-color: rgba(3, 179, 129, 0.16);
$notice-primary-color: #ff4b55; $notice-primary-color: #ff4b55;
$notice-primary-bg-color: rgba(255, 75, 85, 0.08); $notice-primary-bg-color: rgba(255, 75, 85, 0.16);
$notice-secondary-color: #61708b; $notice-secondary-color: #61708b;
$header-panel-bg-color: #f3f8fd; $header-panel-bg-color: #f3f8fd;

View file

@ -80,13 +80,26 @@ function play(audioId) {
// which listens? // which listens?
const audio = document.getElementById(audioId); const audio = document.getElementById(audioId);
if (audio) { if (audio) {
const playAudio = async () => {
try {
// This still causes the chrome debugger to break on promise rejection if
// the promise is rejected, even though we're catching the exception.
await audio.play();
} catch (e) {
// This is usually because the user hasn't interacted with the document,
// or chrome doesn't think so and is denying the request. Not sure what
// we can really do here...
// https://github.com/vector-im/riot-web/issues/7657
console.log("Unable to play audio clip", e);
}
};
if (audioPromises[audioId]) { if (audioPromises[audioId]) {
audioPromises[audioId] = audioPromises[audioId].then(()=>{ audioPromises[audioId] = audioPromises[audioId].then(()=>{
audio.load(); audio.load();
return audio.play(); return playAudio();
}); });
} else { } else {
audioPromises[audioId] = audio.play(); audioPromises[audioId] = playAudio();
} }
} }
} }
@ -382,7 +395,7 @@ function _onAction(payload) {
} }
async function _startCallApp(roomId, type) { async function _startCallApp(roomId, type) {
// check for a working integrations manager. Technically we could put // check for a working integration manager. Technically we could put
// the state event in anyway, but the resulting widget would then not // the state event in anyway, but the resulting widget would then not
// work for us. Better that the user knows before everyone else in the // work for us. Better that the user knows before everyone else in the
// room sees it. // room sees it.
@ -495,6 +508,17 @@ async function _startCallApp(roomId, type) {
// with the dispatcher once // with the dispatcher once
if (!global.mxCallHandler) { if (!global.mxCallHandler) {
dis.register(_onAction); dis.register(_onAction);
// add empty handlers for media actions, otherwise the media keys
// end up causing the audio elements with our ring/ringback etc
// audio clips in to play.
if (navigator.mediaSession) {
navigator.mediaSession.setActionHandler('play', function() {});
navigator.mediaSession.setActionHandler('pause', function() {});
navigator.mediaSession.setActionHandler('seekbackward', function() {});
navigator.mediaSession.setActionHandler('seekforward', function() {});
navigator.mediaSession.setActionHandler('previoustrack', function() {});
navigator.mediaSession.setActionHandler('nexttrack', function() {});
}
} }
const callHandler = { const callHandler = {

View file

@ -17,7 +17,6 @@ limitations under the License.
'use strict'; 'use strict';
import Promise from 'bluebird';
import extend from './extend'; import extend from './extend';
import dis from './dispatcher'; import dis from './dispatcher';
import MatrixClientPeg from './MatrixClientPeg'; import MatrixClientPeg from './MatrixClientPeg';

View file

@ -16,7 +16,6 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import Promise from 'bluebird';
import Matrix from 'matrix-js-sdk'; import Matrix from 'matrix-js-sdk';
import MatrixClientPeg from './MatrixClientPeg'; import MatrixClientPeg from './MatrixClientPeg';
@ -526,7 +525,7 @@ export function logout() {
console.log("Failed to call logout API: token will not be invalidated"); console.log("Failed to call logout API: token will not be invalidated");
onLoggedOut(); onLoggedOut();
}, },
).done(); );
} }
export function softLogout() { export function softLogout() {

View file

@ -23,7 +23,6 @@ import Analytics from './Analytics';
import sdk from './index'; import sdk from './index';
import dis from './dispatcher'; import dis from './dispatcher';
import { _t } from './languageHandler'; import { _t } from './languageHandler';
import Promise from "bluebird";
import {defer} from "./utils/promise"; import {defer} from "./utils/promise";
const DIALOG_CONTAINER_ID = "mx_Dialog_Container"; const DIALOG_CONTAINER_ID = "mx_Dialog_Container";

View file

@ -146,7 +146,7 @@ const Notifier = {
} }
document.body.appendChild(audioElement); document.body.appendChild(audioElement);
} }
audioElement.play(); await audioElement.play();
} catch (ex) { } catch (ex) {
console.warn("Caught error when trying to fetch room notification sound:", ex); console.warn("Caught error when trying to fetch room notification sound:", ex);
} }
@ -198,7 +198,7 @@ const Notifier = {
if (enable) { if (enable) {
// Attempt to get permission from user // Attempt to get permission from user
plaf.requestNotificationPermission().done((result) => { plaf.requestNotificationPermission().then((result) => {
if (result !== 'granted') { if (result !== 'granted') {
// The permission request was dismissed or denied // The permission request was dismissed or denied
// TODO: Support alternative branding in messaging // TODO: Support alternative branding in messaging

View file

@ -35,7 +35,7 @@ module.exports = {
}, },
resend: function(event) { resend: function(event) {
const room = MatrixClientPeg.get().getRoom(event.getRoomId()); const room = MatrixClientPeg.get().getRoom(event.getRoomId());
MatrixClientPeg.get().resendEvent(event, room).done(function(res) { MatrixClientPeg.get().resendEvent(event, room).then(function(res) {
dis.dispatch({ dis.dispatch({
action: 'message_sent', action: 'message_sent',
event: event, event: event,

View file

@ -17,7 +17,6 @@ limitations under the License.
import MatrixClientPeg from './MatrixClientPeg'; import MatrixClientPeg from './MatrixClientPeg';
import PushProcessor from 'matrix-js-sdk/lib/pushprocessor'; import PushProcessor from 'matrix-js-sdk/lib/pushprocessor';
import Promise from 'bluebird';
export const ALL_MESSAGES_LOUD = 'all_messages_loud'; export const ALL_MESSAGES_LOUD = 'all_messages_loud';
export const ALL_MESSAGES = 'all_messages'; export const ALL_MESSAGES = 'all_messages';

View file

@ -15,7 +15,6 @@ limitations under the License.
*/ */
import MatrixClientPeg from './MatrixClientPeg'; import MatrixClientPeg from './MatrixClientPeg';
import Promise from 'bluebird';
/** /**
* Given a room object, return the alias we should use for it, * Given a room object, return the alias we should use for it,

View file

@ -16,7 +16,6 @@ limitations under the License.
*/ */
import url from 'url'; import url from 'url';
import Promise from 'bluebird';
import SettingsStore from "./settings/SettingsStore"; import SettingsStore from "./settings/SettingsStore";
import { Service, startTermsFlow, TermsNotSignedError } from './Terms'; import { Service, startTermsFlow, TermsNotSignedError } from './Terms';
const request = require('browser-request'); const request = require('browser-request');

View file

@ -279,7 +279,7 @@ function inviteUser(event, roomId, userId) {
} }
} }
client.invite(roomId, userId).done(function() { client.invite(roomId, userId).then(function() {
sendResponse(event, { sendResponse(event, {
success: true, success: true,
}); });
@ -398,7 +398,7 @@ function setPlumbingState(event, roomId, status) {
sendError(event, _t('You need to be logged in.')); sendError(event, _t('You need to be logged in.'));
return; return;
} }
client.sendStateEvent(roomId, "m.room.plumbing", { status: status }).done(() => { client.sendStateEvent(roomId, "m.room.plumbing", { status: status }).then(() => {
sendResponse(event, { sendResponse(event, {
success: true, success: true,
}); });
@ -414,7 +414,7 @@ function setBotOptions(event, roomId, userId) {
sendError(event, _t('You need to be logged in.')); sendError(event, _t('You need to be logged in.'));
return; return;
} }
client.sendStateEvent(roomId, "m.room.bot.options", event.data.content, "_" + userId).done(() => { client.sendStateEvent(roomId, "m.room.bot.options", event.data.content, "_" + userId).then(() => {
sendResponse(event, { sendResponse(event, {
success: true, success: true,
}); });
@ -444,7 +444,7 @@ function setBotPower(event, roomId, userId, level) {
}, },
); );
client.setPowerLevel(roomId, userId, level, powerEvent).done(() => { client.setPowerLevel(roomId, userId, level, powerEvent).then(() => {
sendResponse(event, { sendResponse(event, {
success: true, success: true,
}); });

View file

@ -28,7 +28,6 @@ import { linkifyAndSanitizeHtml } from './HtmlUtils';
import QuestionDialog from "./components/views/dialogs/QuestionDialog"; import QuestionDialog from "./components/views/dialogs/QuestionDialog";
import WidgetUtils from "./utils/WidgetUtils"; import WidgetUtils from "./utils/WidgetUtils";
import {textToHtmlRainbow} from "./utils/colour"; import {textToHtmlRainbow} from "./utils/colour";
import Promise from "bluebird";
import { getAddressType } from './UserAddress'; import { getAddressType } from './UserAddress';
import { abbreviateUrl } from './utils/UrlUtils'; import { abbreviateUrl } from './utils/UrlUtils';
import { getDefaultIdentityServerUrl, useDefaultIdentityServer } from './utils/IdentityServerUtils'; import { getDefaultIdentityServerUrl, useDefaultIdentityServer } from './utils/IdentityServerUtils';

View file

@ -14,7 +14,6 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import Promise from 'bluebird';
import classNames from 'classnames'; import classNames from 'classnames';
import MatrixClientPeg from './MatrixClientPeg'; import MatrixClientPeg from './MatrixClientPeg';

View file

@ -14,8 +14,6 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import Promise from "bluebird";
// const OUTBOUND_API_NAME = 'toWidget'; // const OUTBOUND_API_NAME = 'toWidget';
// Initiate requests using the "toWidget" postMessage API and handle responses // Initiate requests using the "toWidget" postMessage API and handle responses

View file

@ -14,7 +14,6 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import Promise from 'bluebird';
import {createNewMatrixCall, Room} from "matrix-js-sdk"; import {createNewMatrixCall, Room} from "matrix-js-sdk";
import CallHandler from './CallHandler'; import CallHandler from './CallHandler';
import MatrixClientPeg from "./MatrixClientPeg"; import MatrixClientPeg from "./MatrixClientPeg";

View file

@ -26,7 +26,6 @@ import RoomProvider from './RoomProvider';
import UserProvider from './UserProvider'; import UserProvider from './UserProvider';
import EmojiProvider from './EmojiProvider'; import EmojiProvider from './EmojiProvider';
import NotifProvider from './NotifProvider'; import NotifProvider from './NotifProvider';
import Promise from 'bluebird';
import {timeout} from "../utils/promise"; import {timeout} from "../utils/promise";
export type SelectionRange = { export type SelectionRange = {

View file

@ -19,7 +19,6 @@ limitations under the License.
import React from 'react'; import React from 'react';
import createReactClass from 'create-react-class'; import createReactClass from 'create-react-class';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Promise from 'bluebird';
import MatrixClientPeg from '../../MatrixClientPeg'; import MatrixClientPeg from '../../MatrixClientPeg';
import sdk from '../../index'; import sdk from '../../index';
import dis from '../../dispatcher'; import dis from '../../dispatcher';
@ -637,7 +636,7 @@ export default createReactClass({
title: _t('Error'), title: _t('Error'),
description: _t('Failed to upload image'), description: _t('Failed to upload image'),
}); });
}).done(); });
}, },
_onJoinableChange: function(ev) { _onJoinableChange: function(ev) {
@ -676,7 +675,7 @@ export default createReactClass({
this.setState({ this.setState({
avatarChanged: false, avatarChanged: false,
}); });
}).done(); });
}, },
_saveGroup: async function() { _saveGroup: async function() {

View file

@ -121,7 +121,7 @@ export default createReactClass({
this.setState({ this.setState({
errorText: msg, errorText: msg,
}); });
}).done(); });
this._intervalId = null; this._intervalId = null;
if (this.props.poll) { if (this.props.poll) {

View file

@ -525,6 +525,7 @@ const LoggedInView = createReactClass({
const EmbeddedPage = sdk.getComponent('structures.EmbeddedPage'); const EmbeddedPage = sdk.getComponent('structures.EmbeddedPage');
const GroupView = sdk.getComponent('structures.GroupView'); const GroupView = sdk.getComponent('structures.GroupView');
const MyGroups = sdk.getComponent('structures.MyGroups'); const MyGroups = sdk.getComponent('structures.MyGroups');
const ToastContainer = sdk.getComponent('structures.ToastContainer');
const MatrixToolbar = sdk.getComponent('globals.MatrixToolbar'); const MatrixToolbar = sdk.getComponent('globals.MatrixToolbar');
const CookieBar = sdk.getComponent('globals.CookieBar'); const CookieBar = sdk.getComponent('globals.CookieBar');
const NewVersionBar = sdk.getComponent('globals.NewVersionBar'); const NewVersionBar = sdk.getComponent('globals.NewVersionBar');
@ -628,6 +629,7 @@ const LoggedInView = createReactClass({
return ( return (
<div onPaste={this._onPaste} onKeyDown={this._onReactKeyDown} className='mx_MatrixChat_wrapper' aria-hidden={this.props.hideToSRUsers} onMouseDown={this._onMouseDown} onMouseUp={this._onMouseUp}> <div onPaste={this._onPaste} onKeyDown={this._onReactKeyDown} className='mx_MatrixChat_wrapper' aria-hidden={this.props.hideToSRUsers} onMouseDown={this._onMouseDown} onMouseUp={this._onMouseUp}>
{ topBar } { topBar }
<ToastContainer />
<DragDropContext onDragEnd={this._onDragEnd}> <DragDropContext onDragEnd={this._onDragEnd}>
<div ref={this._setResizeContainerRef} className={bodyClasses}> <div ref={this._setResizeContainerRef} className={bodyClasses}>
<LeftPanel <LeftPanel

View file

@ -17,8 +17,6 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import Promise from 'bluebird';
import React from 'react'; import React from 'react';
import createReactClass from 'create-react-class'; import createReactClass from 'create-react-class';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
@ -62,10 +60,7 @@ import { countRoomsWithNotif } from '../../RoomNotifs';
import { ThemeWatcher } from "../../theme"; import { ThemeWatcher } from "../../theme";
import { storeRoomAliasInCache } from '../../RoomAliasCache'; import { storeRoomAliasInCache } from '../../RoomAliasCache';
import { defer } from "../../utils/promise"; import { defer } from "../../utils/promise";
import KeyVerificationStateObserver from '../../utils/KeyVerificationStateObserver';
// Disable warnings for now: we use deprecated bluebird functions
// and need to migrate, but they spam the console with warnings.
Promise.config({warnings: false});
/** constants for MatrixChat.state.view */ /** constants for MatrixChat.state.view */
const VIEWS = { const VIEWS = {
@ -545,7 +540,7 @@ export default createReactClass({
const Loader = sdk.getComponent("elements.Spinner"); const Loader = sdk.getComponent("elements.Spinner");
const modal = Modal.createDialog(Loader, null, 'mx_Dialog_spinner'); const modal = Modal.createDialog(Loader, null, 'mx_Dialog_spinner');
MatrixClientPeg.get().leave(payload.room_id).done(() => { MatrixClientPeg.get().leave(payload.room_id).then(() => {
modal.close(); modal.close();
if (this.state.currentRoomId === payload.room_id) { if (this.state.currentRoomId === payload.room_id) {
dis.dispatch({action: 'view_next_room'}); dis.dispatch({action: 'view_next_room'});
@ -863,7 +858,7 @@ export default createReactClass({
waitFor = this.firstSyncPromise.promise; waitFor = this.firstSyncPromise.promise;
} }
waitFor.done(() => { waitFor.then(() => {
let presentedId = roomInfo.room_alias || roomInfo.room_id; let presentedId = roomInfo.room_alias || roomInfo.room_id;
const room = MatrixClientPeg.get().getRoom(roomInfo.room_id); const room = MatrixClientPeg.get().getRoom(roomInfo.room_id);
if (room) { if (room) {
@ -980,7 +975,7 @@ export default createReactClass({
const [shouldCreate, createOpts] = await modal.finished; const [shouldCreate, createOpts] = await modal.finished;
if (shouldCreate) { if (shouldCreate) {
createRoom({createOpts}).done(); createRoom({createOpts});
} }
}, },
@ -1270,7 +1265,6 @@ export default createReactClass({
this.firstSyncComplete = false; this.firstSyncComplete = false;
this.firstSyncPromise = defer(); this.firstSyncPromise = defer();
const cli = MatrixClientPeg.get(); const cli = MatrixClientPeg.get();
const IncomingSasDialog = sdk.getComponent('views.dialogs.IncomingSasDialog');
// Allow the JS SDK to reap timeline events. This reduces the amount of // Allow the JS SDK to reap timeline events. This reduces the amount of
// memory consumed as the JS SDK stores multiple distinct copies of room // memory consumed as the JS SDK stores multiple distinct copies of room
@ -1469,12 +1463,35 @@ export default createReactClass({
} }
}); });
cli.on("crypto.verification.start", (verifier) => { if (SettingsStore.isFeatureEnabled("feature_dm_verification")) {
Modal.createTrackedDialog('Incoming Verification', '', IncomingSasDialog, { cli.on("crypto.verification.request", request => {
verifier, let requestObserver;
}); if (request.event.getRoomId()) {
}); requestObserver = new KeyVerificationStateObserver(
request.event, MatrixClientPeg.get());
}
if (!requestObserver || requestObserver.pending) {
dis.dispatch({
action: "show_toast",
toast: {
key: request.event.getId(),
title: _t("Verification Request"),
icon: "verification",
props: {request, requestObserver},
component: sdk.getComponent("toasts.VerificationRequestToast"),
},
});
}
});
} else {
cli.on("crypto.verification.start", (verifier) => {
const IncomingSasDialog = sdk.getComponent("views.dialogs.IncomingSasDialog");
Modal.createTrackedDialog('Incoming Verification', '', IncomingSasDialog, {
verifier,
});
});
}
// Fire the tinter right on startup to ensure the default theme is applied // Fire the tinter right on startup to ensure the default theme is applied
// A later sync can/will correct the tint to be the right value for the user // A later sync can/will correct the tint to be the right value for the user
const colorScheme = SettingsStore.getValue("roomColor"); const colorScheme = SettingsStore.getValue("roomColor");
@ -1745,7 +1762,7 @@ export default createReactClass({
return; return;
} }
cli.sendEvent(roomId, event.getType(), event.getContent()).done(() => { cli.sendEvent(roomId, event.getType(), event.getContent()).then(() => {
dis.dispatch({action: 'message_sent'}); dis.dispatch({action: 'message_sent'});
}, (err) => { }, (err) => {
dis.dispatch({action: 'message_send_failed'}); dis.dispatch({action: 'message_send_failed'});

View file

@ -1,6 +1,7 @@
/* /*
Copyright 2016 OpenMarket Ltd Copyright 2016 OpenMarket Ltd
Copyright 2018 New Vector Ltd Copyright 2018 New Vector Ltd
Copyright 2019 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.
@ -18,7 +19,6 @@ limitations under the License.
/* global Velocity */ /* global Velocity */
import React from 'react'; import React from 'react';
import createReactClass from 'create-react-class';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import classNames from 'classnames'; import classNames from 'classnames';
@ -37,10 +37,8 @@ const isMembershipChange = (e) => e.getType() === 'm.room.member' || e.getType()
/* (almost) stateless UI component which builds the event tiles in the room timeline. /* (almost) stateless UI component which builds the event tiles in the room timeline.
*/ */
module.exports = createReactClass({ export default class MessagePanel extends React.Component {
displayName: 'MessagePanel', static propTypes = {
propTypes: {
// true to give the component a 'display: none' style. // true to give the component a 'display: none' style.
hidden: PropTypes.bool, hidden: PropTypes.bool,
@ -109,9 +107,10 @@ module.exports = createReactClass({
// whether to show reactions for an event // whether to show reactions for an event
showReactions: PropTypes.bool, showReactions: PropTypes.bool,
}, };
componentWillMount: function() { constructor() {
super();
// the event after which we put a visible unread marker on the last // the event after which we put a visible unread marker on the last
// render cycle; null if readMarkerVisible was false or the RM was // render cycle; null if readMarkerVisible was false or the RM was
// suppressed (eg because it was at the end of the timeline) // suppressed (eg because it was at the end of the timeline)
@ -167,38 +166,42 @@ module.exports = createReactClass({
this._showHiddenEventsInTimeline = this._showHiddenEventsInTimeline =
SettingsStore.getValue("showHiddenEventsInTimeline"); SettingsStore.getValue("showHiddenEventsInTimeline");
this._isMounted = true;
},
componentWillUnmount: function() {
this._isMounted = false; this._isMounted = false;
}, }
componentDidMount() {
this._isMounted = true;
}
componentWillUnmount() {
this._isMounted = false;
}
/* get the DOM node representing the given event */ /* get the DOM node representing the given event */
getNodeForEventId: function(eventId) { getNodeForEventId(eventId) {
if (!this.eventNodes) { if (!this.eventNodes) {
return undefined; return undefined;
} }
return this.eventNodes[eventId]; return this.eventNodes[eventId];
}, }
/* return true if the content is fully scrolled down right now; else false. /* return true if the content is fully scrolled down right now; else false.
*/ */
isAtBottom: function() { isAtBottom() {
return this.refs.scrollPanel return this.refs.scrollPanel
&& this.refs.scrollPanel.isAtBottom(); && this.refs.scrollPanel.isAtBottom();
}, }
/* get the current scroll state. See ScrollPanel.getScrollState for /* get the current scroll state. See ScrollPanel.getScrollState for
* details. * details.
* *
* returns null if we are not mounted. * returns null if we are not mounted.
*/ */
getScrollState: function() { getScrollState() {
if (!this.refs.scrollPanel) { return null; } if (!this.refs.scrollPanel) { return null; }
return this.refs.scrollPanel.getScrollState(); return this.refs.scrollPanel.getScrollState();
}, }
// returns one of: // returns one of:
// //
@ -206,7 +209,7 @@ module.exports = createReactClass({
// -1: read marker is above the window // -1: read marker is above the window
// 0: read marker is within the window // 0: read marker is within the window
// +1: read marker is below the window // +1: read marker is below the window
getReadMarkerPosition: function() { getReadMarkerPosition() {
const readMarker = this.refs.readMarkerNode; const readMarker = this.refs.readMarkerNode;
const messageWrapper = this.refs.scrollPanel; const messageWrapper = this.refs.scrollPanel;
@ -226,45 +229,45 @@ module.exports = createReactClass({
} else { } else {
return 1; return 1;
} }
}, }
/* jump to the top of the content. /* jump to the top of the content.
*/ */
scrollToTop: function() { scrollToTop() {
if (this.refs.scrollPanel) { if (this.refs.scrollPanel) {
this.refs.scrollPanel.scrollToTop(); this.refs.scrollPanel.scrollToTop();
} }
}, }
/* jump to the bottom of the content. /* jump to the bottom of the content.
*/ */
scrollToBottom: function() { scrollToBottom() {
if (this.refs.scrollPanel) { if (this.refs.scrollPanel) {
this.refs.scrollPanel.scrollToBottom(); this.refs.scrollPanel.scrollToBottom();
} }
}, }
/** /**
* Page up/down. * Page up/down.
* *
* @param {number} mult: -1 to page up, +1 to page down * @param {number} mult: -1 to page up, +1 to page down
*/ */
scrollRelative: function(mult) { scrollRelative(mult) {
if (this.refs.scrollPanel) { if (this.refs.scrollPanel) {
this.refs.scrollPanel.scrollRelative(mult); this.refs.scrollPanel.scrollRelative(mult);
} }
}, }
/** /**
* Scroll up/down in response to a scroll key * Scroll up/down in response to a scroll key
* *
* @param {KeyboardEvent} ev: the keyboard event to handle * @param {KeyboardEvent} ev: the keyboard event to handle
*/ */
handleScrollKey: function(ev) { handleScrollKey(ev) {
if (this.refs.scrollPanel) { if (this.refs.scrollPanel) {
this.refs.scrollPanel.handleScrollKey(ev); this.refs.scrollPanel.handleScrollKey(ev);
} }
}, }
/* jump to the given event id. /* jump to the given event id.
* *
@ -276,33 +279,33 @@ module.exports = createReactClass({
* node (specifically, the bottom of it) will be positioned. If omitted, it * node (specifically, the bottom of it) will be positioned. If omitted, it
* defaults to 0. * defaults to 0.
*/ */
scrollToEvent: function(eventId, pixelOffset, offsetBase) { scrollToEvent(eventId, pixelOffset, offsetBase) {
if (this.refs.scrollPanel) { if (this.refs.scrollPanel) {
this.refs.scrollPanel.scrollToToken(eventId, pixelOffset, offsetBase); this.refs.scrollPanel.scrollToToken(eventId, pixelOffset, offsetBase);
} }
}, }
scrollToEventIfNeeded: function(eventId) { scrollToEventIfNeeded(eventId) {
const node = this.eventNodes[eventId]; const node = this.eventNodes[eventId];
if (node) { if (node) {
node.scrollIntoView({block: "nearest", behavior: "instant"}); node.scrollIntoView({block: "nearest", behavior: "instant"});
} }
}, }
/* check the scroll state and send out pagination requests if necessary. /* check the scroll state and send out pagination requests if necessary.
*/ */
checkFillState: function() { checkFillState() {
if (this.refs.scrollPanel) { if (this.refs.scrollPanel) {
this.refs.scrollPanel.checkFillState(); this.refs.scrollPanel.checkFillState();
} }
}, }
_isUnmounting: function() { _isUnmounting() {
return !this._isMounted; return !this._isMounted;
}, }
// TODO: Implement granular (per-room) hide options // TODO: Implement granular (per-room) hide options
_shouldShowEvent: function(mxEv) { _shouldShowEvent(mxEv) {
if (mxEv.sender && MatrixClientPeg.get().isUserIgnored(mxEv.sender.userId)) { if (mxEv.sender && MatrixClientPeg.get().isUserIgnored(mxEv.sender.userId)) {
return false; // ignored = no show (only happens if the ignore happens after an event was received) return false; // ignored = no show (only happens if the ignore happens after an event was received)
} }
@ -320,9 +323,9 @@ module.exports = createReactClass({
if (this.props.highlightedEventId === mxEv.getId()) return true; if (this.props.highlightedEventId === mxEv.getId()) return true;
return !shouldHideEvent(mxEv); return !shouldHideEvent(mxEv);
}, }
_getEventTiles: function() { _getEventTiles() {
const DateSeparator = sdk.getComponent('messages.DateSeparator'); const DateSeparator = sdk.getComponent('messages.DateSeparator');
const EventListSummary = sdk.getComponent('views.elements.EventListSummary'); const EventListSummary = sdk.getComponent('views.elements.EventListSummary');
const MemberEventListSummary = sdk.getComponent('views.elements.MemberEventListSummary'); const MemberEventListSummary = sdk.getComponent('views.elements.MemberEventListSummary');
@ -411,6 +414,12 @@ module.exports = createReactClass({
readMarkerInSummary = true; readMarkerInSummary = true;
} }
// If this m.room.create event should be shown (room upgrade) then show it before the summary
if (this._shouldShowEvent(mxEv)) {
// pass in the mxEv as prevEvent as well so no extra DateSeparator is rendered
ret.push(...this._getTilesForEvent(mxEv, mxEv, false));
}
const summarisedEvents = []; // Don't add m.room.create here as we don't want it inside the summary const summarisedEvents = []; // Don't add m.room.create here as we don't want it inside the summary
for (;i + 1 < this.props.events.length; i++) { for (;i + 1 < this.props.events.length; i++) {
const collapsedMxEv = this.props.events[i + 1]; const collapsedMxEv = this.props.events[i + 1];
@ -596,9 +605,9 @@ module.exports = createReactClass({
this.currentReadMarkerEventId = readMarkerVisible ? this.props.readMarkerEventId : null; this.currentReadMarkerEventId = readMarkerVisible ? this.props.readMarkerEventId : null;
return ret; return ret;
}, }
_getTilesForEvent: function(prevEvent, mxEv, last) { _getTilesForEvent(prevEvent, mxEv, last) {
const EventTile = sdk.getComponent('rooms.EventTile'); const EventTile = sdk.getComponent('rooms.EventTile');
const DateSeparator = sdk.getComponent('messages.DateSeparator'); const DateSeparator = sdk.getComponent('messages.DateSeparator');
const ret = []; const ret = [];
@ -691,20 +700,20 @@ module.exports = createReactClass({
); );
return ret; return ret;
}, }
_wantsDateSeparator: function(prevEvent, nextEventDate) { _wantsDateSeparator(prevEvent, nextEventDate) {
if (prevEvent == null) { if (prevEvent == null) {
// first event in the panel: depends if we could back-paginate from // first event in the panel: depends if we could back-paginate from
// here. // here.
return !this.props.suppressFirstDateSeparator; return !this.props.suppressFirstDateSeparator;
} }
return wantsDateSeparator(prevEvent.getDate(), nextEventDate); return wantsDateSeparator(prevEvent.getDate(), nextEventDate);
}, }
// Get a list of read receipts that should be shown next to this event // Get a list of read receipts that should be shown next to this event
// Receipts are objects which have a 'userId', 'roomMember' and 'ts'. // Receipts are objects which have a 'userId', 'roomMember' and 'ts'.
_getReadReceiptsForEvent: function(event) { _getReadReceiptsForEvent(event) {
const myUserId = MatrixClientPeg.get().credentials.userId; const myUserId = MatrixClientPeg.get().credentials.userId;
// get list of read receipts, sorted most recent first // get list of read receipts, sorted most recent first
@ -728,12 +737,12 @@ module.exports = createReactClass({
}); });
}); });
return receipts; return receipts;
}, }
// Get an object that maps from event ID to a list of read receipts that // Get an object that maps from event ID to a list of read receipts that
// should be shown next to that event. If a hidden event has read receipts, // should be shown next to that event. If a hidden event has read receipts,
// they are folded into the receipts of the last shown event. // they are folded into the receipts of the last shown event.
_getReadReceiptsByShownEvent: function() { _getReadReceiptsByShownEvent() {
const receiptsByEvent = {}; const receiptsByEvent = {};
const receiptsByUserId = {}; const receiptsByUserId = {};
@ -786,9 +795,9 @@ module.exports = createReactClass({
} }
return receiptsByEvent; return receiptsByEvent;
}, }
_getReadMarkerTile: function(visible) { _getReadMarkerTile(visible) {
let hr; let hr;
if (visible) { if (visible) {
hr = <hr className="mx_RoomView_myReadMarker" hr = <hr className="mx_RoomView_myReadMarker"
@ -802,9 +811,9 @@ module.exports = createReactClass({
{ hr } { hr }
</li> </li>
); );
}, }
_startAnimation: function(ghostNode) { _startAnimation = (ghostNode) => {
if (this._readMarkerGhostNode) { if (this._readMarkerGhostNode) {
Velocity.Utilities.removeData(this._readMarkerGhostNode); Velocity.Utilities.removeData(this._readMarkerGhostNode);
} }
@ -816,9 +825,9 @@ module.exports = createReactClass({
{duration: 400, easing: 'easeInSine', {duration: 400, easing: 'easeInSine',
delay: 1000}); delay: 1000});
} }
}, };
_getReadMarkerGhostTile: function() { _getReadMarkerGhostTile() {
const hr = <hr className="mx_RoomView_myReadMarker" const hr = <hr className="mx_RoomView_myReadMarker"
style={{opacity: 1, width: '99%'}} style={{opacity: 1, width: '99%'}}
ref={this._startAnimation} ref={this._startAnimation}
@ -833,31 +842,31 @@ module.exports = createReactClass({
{ hr } { hr }
</li> </li>
); );
}, }
_collectEventNode: function(eventId, node) { _collectEventNode = (eventId, node) => {
this.eventNodes[eventId] = node; this.eventNodes[eventId] = node;
}, }
// once dynamic content in the events load, make the scrollPanel check the // once dynamic content in the events load, make the scrollPanel check the
// scroll offsets. // scroll offsets.
_onHeightChanged: function() { _onHeightChanged = () => {
const scrollPanel = this.refs.scrollPanel; const scrollPanel = this.refs.scrollPanel;
if (scrollPanel) { if (scrollPanel) {
scrollPanel.checkScroll(); scrollPanel.checkScroll();
} }
}, };
_onTypingShown: function() { _onTypingShown = () => {
const scrollPanel = this.refs.scrollPanel; const scrollPanel = this.refs.scrollPanel;
// this will make the timeline grow, so checkScroll // this will make the timeline grow, so checkScroll
scrollPanel.checkScroll(); scrollPanel.checkScroll();
if (scrollPanel && scrollPanel.getScrollState().stuckAtBottom) { if (scrollPanel && scrollPanel.getScrollState().stuckAtBottom) {
scrollPanel.preventShrinking(); scrollPanel.preventShrinking();
} }
}, };
_onTypingHidden: function() { _onTypingHidden = () => {
const scrollPanel = this.refs.scrollPanel; const scrollPanel = this.refs.scrollPanel;
if (scrollPanel) { if (scrollPanel) {
// as hiding the typing notifications doesn't // as hiding the typing notifications doesn't
@ -868,9 +877,9 @@ module.exports = createReactClass({
// reveal added padding to balance the notifs disappearing. // reveal added padding to balance the notifs disappearing.
scrollPanel.checkScroll(); scrollPanel.checkScroll();
} }
}, };
updateTimelineMinHeight: function() { updateTimelineMinHeight() {
const scrollPanel = this.refs.scrollPanel; const scrollPanel = this.refs.scrollPanel;
if (scrollPanel) { if (scrollPanel) {
@ -885,16 +894,16 @@ module.exports = createReactClass({
scrollPanel.preventShrinking(); scrollPanel.preventShrinking();
} }
} }
}, }
onTimelineReset: function() { onTimelineReset() {
const scrollPanel = this.refs.scrollPanel; const scrollPanel = this.refs.scrollPanel;
if (scrollPanel) { if (scrollPanel) {
scrollPanel.clearPreventShrinking(); scrollPanel.clearPreventShrinking();
} }
}, }
render: function() { render() {
const ScrollPanel = sdk.getComponent("structures.ScrollPanel"); const ScrollPanel = sdk.getComponent("structures.ScrollPanel");
const WhoIsTypingTile = sdk.getComponent("rooms.WhoIsTypingTile"); const WhoIsTypingTile = sdk.getComponent("rooms.WhoIsTypingTile");
const Spinner = sdk.getComponent("elements.Spinner"); const Spinner = sdk.getComponent("elements.Spinner");
@ -941,5 +950,5 @@ module.exports = createReactClass({
{ bottomSpinner } { bottomSpinner }
</ScrollPanel> </ScrollPanel>
); );
}, }
}); }

View file

@ -47,7 +47,7 @@ export default createReactClass({
}, },
_fetch: function() { _fetch: function() {
this.context.matrixClient.getJoinedGroups().done((result) => { this.context.matrixClient.getJoinedGroups().then((result) => {
this.setState({groups: result.groups, error: null}); this.setState({groups: result.groups, error: null});
}, (err) => { }, (err) => {
if (err.errcode === 'M_GUEST_ACCESS_FORBIDDEN') { if (err.errcode === 'M_GUEST_ACCESS_FORBIDDEN') {

View file

@ -27,7 +27,6 @@ const dis = require('../../dispatcher');
import { linkifyAndSanitizeHtml } from '../../HtmlUtils'; import { linkifyAndSanitizeHtml } from '../../HtmlUtils';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Promise from 'bluebird';
import { _t } from '../../languageHandler'; import { _t } from '../../languageHandler';
import { instanceForInstanceId, protocolNameForInstanceId } from '../../utils/DirectoryUtils'; import { instanceForInstanceId, protocolNameForInstanceId } from '../../utils/DirectoryUtils';
import Analytics from '../../Analytics'; import Analytics from '../../Analytics';
@ -89,7 +88,7 @@ module.exports = createReactClass({
this.setState({protocolsLoading: false}); this.setState({protocolsLoading: false});
return; return;
} }
MatrixClientPeg.get().getThirdpartyProtocols().done((response) => { MatrixClientPeg.get().getThirdpartyProtocols().then((response) => {
this.protocols = response; this.protocols = response;
this.setState({protocolsLoading: false}); this.setState({protocolsLoading: false});
}, (err) => { }, (err) => {
@ -135,7 +134,7 @@ module.exports = createReactClass({
publicRooms: [], publicRooms: [],
loading: true, loading: true,
}); });
this.getMoreRooms().done(); this.getMoreRooms();
}, },
getMoreRooms: function() { getMoreRooms: function() {
@ -246,7 +245,7 @@ module.exports = createReactClass({
if (!alias) return; if (!alias) return;
step = _t('delete the alias.'); step = _t('delete the alias.');
return MatrixClientPeg.get().deleteAlias(alias); return MatrixClientPeg.get().deleteAlias(alias);
}).done(() => { }).then(() => {
modal.close(); modal.close();
this.refreshRoomList(); this.refreshRoomList();
}, (err) => { }, (err) => {
@ -348,7 +347,7 @@ module.exports = createReactClass({
}); });
return; return;
} }
MatrixClientPeg.get().getThirdpartyLocation(protocolName, fields).done((resp) => { MatrixClientPeg.get().getThirdpartyLocation(protocolName, fields).then((resp) => {
if (resp.length > 0 && resp[0].alias) { if (resp.length > 0 && resp[0].alias) {
this.showRoomAlias(resp[0].alias, true); this.showRoomAlias(resp[0].alias, true);
} else { } else {

View file

@ -27,7 +27,6 @@ import React from 'react';
import createReactClass from 'create-react-class'; import createReactClass from 'create-react-class';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Promise from 'bluebird';
import classNames from 'classnames'; import classNames from 'classnames';
import {Room} from "matrix-js-sdk"; import {Room} from "matrix-js-sdk";
import { _t } from '../../languageHandler'; import { _t } from '../../languageHandler';
@ -1102,7 +1101,7 @@ module.exports = createReactClass({
} }
ContentMessages.sharedInstance().sendStickerContentToRoom(url, this.state.room.roomId, info, text, MatrixClientPeg.get()) ContentMessages.sharedInstance().sendStickerContentToRoom(url, this.state.room.roomId, info, text, MatrixClientPeg.get())
.done(undefined, (error) => { .then(undefined, (error) => {
if (error.name === "UnknownDeviceError") { if (error.name === "UnknownDeviceError") {
// Let the staus bar handle this // Let the staus bar handle this
return; return;
@ -1135,7 +1134,7 @@ module.exports = createReactClass({
debuglog("sending search request"); debuglog("sending search request");
const searchPromise = eventSearch(term, roomId); const searchPromise = eventSearch(term, roomId);
this._handleSearchResult(searchPromise).done(); this._handleSearchResult(searchPromise);
}, },
_handleSearchResult: function(searchPromise) { _handleSearchResult: function(searchPromise) {
@ -1306,7 +1305,7 @@ module.exports = createReactClass({
}, },
onForgetClick: function() { onForgetClick: function() {
MatrixClientPeg.get().forget(this.state.room.roomId).done(function() { MatrixClientPeg.get().forget(this.state.room.roomId).then(function() {
dis.dispatch({ action: 'view_next_room' }); dis.dispatch({ action: 'view_next_room' });
}, function(err) { }, function(err) {
const errCode = err.errcode || _t("unknown error code"); const errCode = err.errcode || _t("unknown error code");
@ -1323,7 +1322,7 @@ module.exports = createReactClass({
this.setState({ this.setState({
rejecting: true, rejecting: true,
}); });
MatrixClientPeg.get().leave(this.state.roomId).done(function() { MatrixClientPeg.get().leave(this.state.roomId).then(function() {
dis.dispatch({ action: 'view_next_room' }); dis.dispatch({ action: 'view_next_room' });
self.setState({ self.setState({
rejecting: false, rejecting: false,

View file

@ -17,7 +17,6 @@ limitations under the License.
import React from "react"; import React from "react";
import createReactClass from 'create-react-class'; import createReactClass from 'create-react-class';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Promise from 'bluebird';
import { KeyCode } from '../../Keyboard'; import { KeyCode } from '../../Keyboard';
import Timer from '../../utils/Timer'; import Timer from '../../utils/Timer';
import AutoHideScrollbar from "./AutoHideScrollbar"; import AutoHideScrollbar from "./AutoHideScrollbar";

View file

@ -23,7 +23,6 @@ import React from 'react';
import createReactClass from 'create-react-class'; import createReactClass from 'create-react-class';
import ReactDOM from "react-dom"; import ReactDOM from "react-dom";
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Promise from 'bluebird';
const Matrix = require("matrix-js-sdk"); const Matrix = require("matrix-js-sdk");
const EventTimeline = Matrix.EventTimeline; const EventTimeline = Matrix.EventTimeline;
@ -462,7 +461,7 @@ const TimelinePanel = createReactClass({
// timeline window. // timeline window.
// //
// see https://github.com/vector-im/vector-web/issues/1035 // see https://github.com/vector-im/vector-web/issues/1035
this._timelineWindow.paginate(EventTimeline.FORWARDS, 1, false).done(() => { this._timelineWindow.paginate(EventTimeline.FORWARDS, 1, false).then(() => {
if (this.unmounted) { return; } if (this.unmounted) { return; }
const { events, liveEvents } = this._getEvents(); const { events, liveEvents } = this._getEvents();

View file

@ -0,0 +1,84 @@
/*
Copyright 2019 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 * as React from "react";
import dis from "../../dispatcher";
import { _t } from '../../languageHandler';
import classNames from "classnames";
export default class ToastContainer extends React.Component {
constructor() {
super();
this.state = {toasts: []};
}
componentDidMount() {
this._dispatcherRef = dis.register(this.onAction);
}
componentWillUnmount() {
dis.unregister(this._dispatcherRef);
}
onAction = (payload) => {
if (payload.action === "show_toast") {
this._addToast(payload.toast);
}
};
_addToast(toast) {
this.setState({toasts: this.state.toasts.concat(toast)});
}
dismissTopToast = () => {
const [, ...remaining] = this.state.toasts;
this.setState({toasts: remaining});
};
render() {
const totalCount = this.state.toasts.length;
const isStacked = totalCount > 1;
let toast;
if (totalCount !== 0) {
const topToast = this.state.toasts[0];
const {title, icon, key, component, props} = topToast;
const toastClasses = classNames("mx_Toast_toast", {
"mx_Toast_hasIcon": icon,
[`mx_Toast_icon_${icon}`]: icon,
});
const countIndicator = isStacked ? _t(" (1/%(totalCount)s)", {totalCount}) : null;
const toastProps = Object.assign({}, props, {
dismiss: this.dismissTopToast,
key,
});
toast = (<div className={toastClasses}>
<h2>{title}{countIndicator}</h2>
<div className="mx_Toast_body">{React.createElement(component, toastProps)}</div>
</div>);
}
const containerClasses = classNames("mx_ToastContainer", {
"mx_ToastContainer_stacked": isStacked,
});
return (
<div className={containerClasses} role="alert">
{toast}
</div>
);
}
}

View file

@ -105,7 +105,7 @@ module.exports = createReactClass({
phase: PHASE_SENDING_EMAIL, phase: PHASE_SENDING_EMAIL,
}); });
this.reset = new PasswordReset(this.props.serverConfig.hsUrl, this.props.serverConfig.isUrl); this.reset = new PasswordReset(this.props.serverConfig.hsUrl, this.props.serverConfig.isUrl);
this.reset.resetPassword(email, password).done(() => { this.reset.resetPassword(email, password).then(() => {
this.setState({ this.setState({
phase: PHASE_EMAIL_SENT, phase: PHASE_EMAIL_SENT,
}); });

View file

@ -253,7 +253,7 @@ module.exports = createReactClass({
this.setState({ this.setState({
busy: false, busy: false,
}); });
}).done(); });
}, },
onUsernameChanged: function(username) { onUsernameChanged: function(username) {
@ -439,7 +439,7 @@ module.exports = createReactClass({
this.setState({ this.setState({
busy: false, busy: false,
}); });
}).done(); });
}, },
_isSupportedFlow: function(flow) { _isSupportedFlow: function(flow) {

View file

@ -43,7 +43,7 @@ module.exports = createReactClass({
const cli = MatrixClientPeg.get(); const cli = MatrixClientPeg.get();
this.setState({busy: true}); this.setState({busy: true});
const self = this; const self = this;
cli.getProfileInfo(cli.credentials.userId).done(function(result) { cli.getProfileInfo(cli.credentials.userId).then(function(result) {
self.setState({ self.setState({
avatarUrl: MatrixClientPeg.get().mxcUrlToHttp(result.avatar_url), avatarUrl: MatrixClientPeg.get().mxcUrlToHttp(result.avatar_url),
busy: false, busy: false,

View file

@ -18,7 +18,6 @@ limitations under the License.
*/ */
import Matrix from 'matrix-js-sdk'; import Matrix from 'matrix-js-sdk';
import Promise from 'bluebird';
import React from 'react'; import React from 'react';
import createReactClass from 'create-react-class'; import createReactClass from 'create-react-class';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
@ -371,7 +370,7 @@ module.exports = createReactClass({
if (pushers[i].kind === 'email') { if (pushers[i].kind === 'email') {
const emailPusher = pushers[i]; const emailPusher = pushers[i];
emailPusher.data = { brand: this.props.brand }; emailPusher.data = { brand: this.props.brand };
matrixClient.setPusher(emailPusher).done(() => { matrixClient.setPusher(emailPusher).then(() => {
console.log("Set email branding to " + this.props.brand); console.log("Set email branding to " + this.props.brand);
}, (error) => { }, (error) => {
console.error("Couldn't set email branding: " + error); console.error("Couldn't set email branding: " + error);

View file

@ -441,7 +441,7 @@ export const MsisdnAuthEntry = createReactClass({
this.props.fail(e); this.props.fail(e);
}).finally(() => { }).finally(() => {
this.setState({requestingToken: false}); this.setState({requestingToken: false});
}).done(); });
}, },
/* /*

View file

@ -160,7 +160,7 @@ module.exports = createReactClass({
_onClickForget: function() { _onClickForget: function() {
// FIXME: duplicated with RoomSettings (and dead code in RoomView) // FIXME: duplicated with RoomSettings (and dead code in RoomView)
MatrixClientPeg.get().forget(this.props.room.roomId).done(() => { MatrixClientPeg.get().forget(this.props.room.roomId).then(() => {
// Switch to another room view if we're currently viewing the // Switch to another room view if we're currently viewing the
// historical room // historical room
if (RoomViewStore.getRoomId() === this.props.room.roomId) { if (RoomViewStore.getRoomId() === this.props.room.roomId) {
@ -190,7 +190,7 @@ module.exports = createReactClass({
this.setState({ this.setState({
roomNotifState: newState, roomNotifState: newState,
}); });
RoomNotifs.setRoomNotifsState(roomId, newState).done(() => { RoomNotifs.setRoomNotifsState(roomId, newState).then(() => {
// delay slightly so that the user can see their state change // delay slightly so that the user can see their state change
// before closing the menu // before closing the menu
return sleep(500).then(() => { return sleep(500).then(() => {

View file

@ -0,0 +1,134 @@
/*
Copyright 2019 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 PropTypes from 'prop-types';
import sdk from '../../../index';
import {_t} from '../../../languageHandler';
export default class WidgetContextMenu extends React.Component {
static propTypes = {
onFinished: PropTypes.func,
// Callback for when the revoke button is clicked. Required.
onRevokeClicked: PropTypes.func.isRequired,
// Callback for when the snapshot button is clicked. Button not shown
// without a callback.
onSnapshotClicked: PropTypes.func,
// Callback for when the reload button is clicked. Button not shown
// without a callback.
onReloadClicked: PropTypes.func,
// Callback for when the edit button is clicked. Button not shown
// without a callback.
onEditClicked: PropTypes.func,
// Callback for when the delete button is clicked. Button not shown
// without a callback.
onDeleteClicked: PropTypes.func,
};
proxyClick(fn) {
fn();
if (this.props.onFinished) this.props.onFinished();
}
// XXX: It's annoying that our context menus require us to hit onFinished() to close :(
onEditClicked = () => {
this.proxyClick(this.props.onEditClicked);
};
onReloadClicked = () => {
this.proxyClick(this.props.onReloadClicked);
};
onSnapshotClicked = () => {
this.proxyClick(this.props.onSnapshotClicked);
};
onDeleteClicked = () => {
this.proxyClick(this.props.onDeleteClicked);
};
onRevokeClicked = () => {
this.proxyClick(this.props.onRevokeClicked);
};
render() {
const AccessibleButton = sdk.getComponent("views.elements.AccessibleButton");
const options = [];
if (this.props.onEditClicked) {
options.push(
<AccessibleButton className='mx_WidgetContextMenu_option' onClick={this.onEditClicked} key='edit'>
{_t("Edit")}
</AccessibleButton>,
);
}
if (this.props.onReloadClicked) {
options.push(
<AccessibleButton className='mx_WidgetContextMenu_option' onClick={this.onReloadClicked}
key='reload'>
{_t("Reload")}
</AccessibleButton>,
);
}
if (this.props.onSnapshotClicked) {
options.push(
<AccessibleButton className='mx_WidgetContextMenu_option' onClick={this.onSnapshotClicked}
key='snap'>
{_t("Take picture")}
</AccessibleButton>,
);
}
if (this.props.onDeleteClicked) {
options.push(
<AccessibleButton className='mx_WidgetContextMenu_option' onClick={this.onDeleteClicked}
key='delete'>
{_t("Remove for everyone")}
</AccessibleButton>,
);
}
// Push this last so it appears last. It's always present.
options.push(
<AccessibleButton className='mx_WidgetContextMenu_option' onClick={this.onRevokeClicked} key='revoke'>
{_t("Remove for me")}
</AccessibleButton>,
);
// Put separators between the options
if (options.length > 1) {
const length = options.length;
for (let i = 0; i < length - 1; i++) {
const sep = <hr key={i} className="mx_WidgetContextMenu_separator" />;
// Insert backwards so the insertions don't affect our math on where to place them.
// We also use our cached length to avoid worrying about options.length changing
options.splice(length - 1 - i, 0, sep);
}
}
return <div className="mx_WidgetContextMenu">{options}</div>;
}
}

View file

@ -266,7 +266,7 @@ module.exports = createReactClass({
this.setState({ this.setState({
searchError: err.errcode ? err.message : _t('Something went wrong!'), searchError: err.errcode ? err.message : _t('Something went wrong!'),
}); });
}).done(() => { }).then(() => {
this.setState({ this.setState({
busy: false, busy: false,
}); });
@ -379,7 +379,7 @@ module.exports = createReactClass({
// Do a local search immediately // Do a local search immediately
this._doLocalSearch(query); this._doLocalSearch(query);
} }
}).done(() => { }).then(() => {
this.setState({ this.setState({
busy: false, busy: false,
}); });

View file

@ -93,7 +93,7 @@ export default createReactClass({
this.setState({createError: e}); this.setState({createError: e});
}).finally(() => { }).finally(() => {
this.setState({creating: false}); this.setState({creating: false});
}).done(); });
}, },
_onCancel: function() { _onCancel: function() {

View file

@ -0,0 +1,57 @@
/*
Copyright 2019 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 PropTypes from 'prop-types';
import {_t} from "../../../languageHandler";
import sdk from "../../../index";
import dis from '../../../dispatcher';
export default class IntegrationsDisabledDialog extends React.Component {
static propTypes = {
onFinished: PropTypes.func.isRequired,
};
_onAcknowledgeClick = () => {
this.props.onFinished();
};
_onOpenSettingsClick = () => {
this.props.onFinished();
dis.dispatch({action: "view_user_settings"});
};
render() {
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
return (
<BaseDialog className='mx_IntegrationsDisabledDialog' hasCancel={true}
onFinished={this.props.onFinished}
title={_t("Integrations are disabled")}>
<div className='mx_IntegrationsDisabledDialog_content'>
<p>{_t("Enable 'Manage Integrations' in Settings to do this.")}</p>
</div>
<DialogButtons
primaryButton={_t("Settings")}
onPrimaryButtonClick={this._onOpenSettingsClick}
cancelButton={_t("OK")}
onCancel={this._onAcknowledgeClick}
/>
</BaseDialog>
);
}
}

View file

@ -0,0 +1,55 @@
/*
Copyright 2019 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 PropTypes from 'prop-types';
import {_t} from "../../../languageHandler";
import sdk from "../../../index";
export default class IntegrationsImpossibleDialog extends React.Component {
static propTypes = {
onFinished: PropTypes.func.isRequired,
};
_onAcknowledgeClick = () => {
this.props.onFinished();
};
render() {
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
return (
<BaseDialog className='mx_IntegrationsImpossibleDialog' hasCancel={false}
onFinished={this.props.onFinished}
title={_t("Integrations not allowed")}>
<div className='mx_IntegrationsImpossibleDialog_content'>
<p>
{_t(
"Your Riot doesn't allow you to use an Integration Manager to do this. " +
"Please contact an admin.",
)}
</p>
</div>
<DialogButtons
primaryButton={_t("OK")}
onPrimaryButtonClick={this._onAcknowledgeClick}
hasCancel={false}
/>
</BaseDialog>
);
}
}

View file

@ -78,7 +78,7 @@ export default createReactClass({
true, true,
); );
} }
}).done(); });
}, },
componentWillUnmount: function() { componentWillUnmount: function() {

View file

@ -62,7 +62,7 @@ export default createReactClass({
return; return;
} }
this._addThreepid = new AddThreepid(); this._addThreepid = new AddThreepid();
this._addThreepid.addEmailAddress(emailAddress).done(() => { this._addThreepid.addEmailAddress(emailAddress).then(() => {
Modal.createTrackedDialog('Verification Pending', '', QuestionDialog, { Modal.createTrackedDialog('Verification Pending', '', QuestionDialog, {
title: _t("Verification Pending"), title: _t("Verification Pending"),
description: _t( description: _t(
@ -96,7 +96,7 @@ export default createReactClass({
}, },
verifyEmailAddress: function() { verifyEmailAddress: function() {
this._addThreepid.checkEmailLinkClicked().done(() => { this._addThreepid.checkEmailLinkClicked().then(() => {
this.props.onFinished(true); this.props.onFinished(true);
}, (err) => { }, (err) => {
this.setState({emailBusy: false}); this.setState({emailBusy: false});

View file

@ -15,7 +15,6 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import Promise from 'bluebird';
import React from 'react'; import React from 'react';
import createReactClass from 'create-react-class'; import createReactClass from 'create-react-class';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';

View file

@ -82,10 +82,10 @@ export default class TabbedIntegrationManagerDialog extends React.Component {
client.setTermsInteractionCallback((policyInfo, agreedUrls) => { client.setTermsInteractionCallback((policyInfo, agreedUrls) => {
// To avoid visual glitching of two modals stacking briefly, we customise the // To avoid visual glitching of two modals stacking briefly, we customise the
// terms dialog sizing when it will appear for the integrations manager so that // terms dialog sizing when it will appear for the integration manager so that
// it gets the same basic size as the IM's own modal. // it gets the same basic size as the IM's own modal.
return dialogTermsInteractionCallback( return dialogTermsInteractionCallback(
policyInfo, agreedUrls, 'mx_TermsDialog_forIntegrationsManager', policyInfo, agreedUrls, 'mx_TermsDialog_forIntegrationManager',
); );
}); });
@ -139,7 +139,7 @@ export default class TabbedIntegrationManagerDialog extends React.Component {
} }
_renderTab() { _renderTab() {
const IntegrationsManager = sdk.getComponent("views.settings.IntegrationsManager"); const IntegrationManager = sdk.getComponent("views.settings.IntegrationManager");
let uiUrl = null; let uiUrl = null;
if (this.state.currentScalarClient) { if (this.state.currentScalarClient) {
uiUrl = this.state.currentScalarClient.getScalarInterfaceUrlForRoom( uiUrl = this.state.currentScalarClient.getScalarInterfaceUrlForRoom(
@ -148,7 +148,7 @@ export default class TabbedIntegrationManagerDialog extends React.Component {
this.props.integrationId, this.props.integrationId,
); );
} }
return <IntegrationsManager return <IntegrationManager
configured={true} configured={true}
loading={this.state.currentLoading} loading={this.state.currentLoading}
connected={this.state.currentConnected} connected={this.state.currentConnected}

View file

@ -86,7 +86,7 @@ export default class TermsDialog extends React.PureComponent {
case Matrix.SERVICE_TYPES.IS: case Matrix.SERVICE_TYPES.IS:
return <div>{_t("Identity Server")}<br />({host})</div>; return <div>{_t("Identity Server")}<br />({host})</div>;
case Matrix.SERVICE_TYPES.IM: case Matrix.SERVICE_TYPES.IM:
return <div>{_t("Integrations Manager")}<br />({host})</div>; return <div>{_t("Integration Manager")}<br />({host})</div>;
} }
} }

View file

@ -30,6 +30,7 @@ export default class AppPermission extends React.Component {
creatorUserId: PropTypes.string.isRequired, creatorUserId: PropTypes.string.isRequired,
roomId: PropTypes.string.isRequired, roomId: PropTypes.string.isRequired,
onPermissionGranted: PropTypes.func.isRequired, onPermissionGranted: PropTypes.func.isRequired,
isRoomEncrypted: PropTypes.bool,
}; };
static defaultProps = { static defaultProps = {
@ -114,6 +115,8 @@ export default class AppPermission extends React.Component {
: _t("Using this widget may share data <helpIcon /> with %(widgetDomain)s.", : _t("Using this widget may share data <helpIcon /> with %(widgetDomain)s.",
{widgetDomain: this.state.widgetDomain}, {helpIcon: () => warningTooltip}); {widgetDomain: this.state.widgetDomain}, {helpIcon: () => warningTooltip});
const encryptionWarning = this.props.isRoomEncrypted ? _t("Widgets do not use message encryption.") : null;
return ( return (
<div className='mx_AppPermissionWarning'> <div className='mx_AppPermissionWarning'>
<div className='mx_AppPermissionWarning_row mx_AppPermissionWarning_bolder mx_AppPermissionWarning_smallText'> <div className='mx_AppPermissionWarning_row mx_AppPermissionWarning_bolder mx_AppPermissionWarning_smallText'>
@ -128,7 +131,7 @@ export default class AppPermission extends React.Component {
{warning} {warning}
</div> </div>
<div className='mx_AppPermissionWarning_row mx_AppPermissionWarning_smallText'> <div className='mx_AppPermissionWarning_row mx_AppPermissionWarning_smallText'>
{_t("This widget may use cookies.")} {_t("This widget may use cookies.")}&nbsp;{encryptionWarning}
</div> </div>
<div className='mx_AppPermissionWarning_row'> <div className='mx_AppPermissionWarning_row'>
<AccessibleButton kind='primary_sm' onClick={this.props.onPermissionGranted}> <AccessibleButton kind='primary_sm' onClick={this.props.onPermissionGranted}>

View file

@ -35,6 +35,7 @@ import ActiveWidgetStore from '../../../stores/ActiveWidgetStore';
import classNames from 'classnames'; import classNames from 'classnames';
import {IntegrationManagers} from "../../../integrations/IntegrationManagers"; import {IntegrationManagers} from "../../../integrations/IntegrationManagers";
import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore"; import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore";
import {createMenu} from "../../structures/ContextualMenu";
const ALLOWED_APP_URL_SCHEMES = ['https:', 'http:']; const ALLOWED_APP_URL_SCHEMES = ['https:', 'http:'];
const ENABLE_REACT_PERF = false; const ENABLE_REACT_PERF = false;
@ -52,7 +53,7 @@ export default class AppTile extends React.Component {
this._onLoaded = this._onLoaded.bind(this); this._onLoaded = this._onLoaded.bind(this);
this._onEditClick = this._onEditClick.bind(this); this._onEditClick = this._onEditClick.bind(this);
this._onDeleteClick = this._onDeleteClick.bind(this); this._onDeleteClick = this._onDeleteClick.bind(this);
this._onCancelClick = this._onCancelClick.bind(this); this._onRevokeClicked = this._onRevokeClicked.bind(this);
this._onSnapshotClick = this._onSnapshotClick.bind(this); this._onSnapshotClick = this._onSnapshotClick.bind(this);
this.onClickMenuBar = this.onClickMenuBar.bind(this); this.onClickMenuBar = this.onClickMenuBar.bind(this);
this._onMinimiseClick = this._onMinimiseClick.bind(this); this._onMinimiseClick = this._onMinimiseClick.bind(this);
@ -207,7 +208,7 @@ export default class AppTile extends React.Component {
if (!this._scalarClient) { if (!this._scalarClient) {
this._scalarClient = defaultManager.getScalarClient(); this._scalarClient = defaultManager.getScalarClient();
} }
this._scalarClient.getScalarToken().done((token) => { this._scalarClient.getScalarToken().then((token) => {
// Append scalar_token as a query param if not already present // Append scalar_token as a query param if not already present
this._scalarClient.scalarToken = token; this._scalarClient.scalarToken = token;
const u = url.parse(this._addWurlParams(this.props.url)); const u = url.parse(this._addWurlParams(this.props.url));
@ -271,7 +272,7 @@ export default class AppTile extends React.Component {
return WidgetUtils.canUserModifyWidgets(this.props.room.roomId); return WidgetUtils.canUserModifyWidgets(this.props.room.roomId);
} }
_onEditClick(e) { _onEditClick() {
console.log("Edit widget ID ", this.props.id); console.log("Edit widget ID ", this.props.id);
if (this.props.onEditClick) { if (this.props.onEditClick) {
this.props.onEditClick(); this.props.onEditClick();
@ -293,7 +294,7 @@ export default class AppTile extends React.Component {
} }
} }
_onSnapshotClick(e) { _onSnapshotClick() {
console.warn("Requesting widget snapshot"); console.warn("Requesting widget snapshot");
ActiveWidgetStore.getWidgetMessaging(this.props.id).getScreenshot() ActiveWidgetStore.getWidgetMessaging(this.props.id).getScreenshot()
.catch((err) => { .catch((err) => {
@ -360,13 +361,9 @@ export default class AppTile extends React.Component {
} }
} }
_onCancelClick() { _onRevokeClicked() {
if (this.props.onDeleteClick) { console.info("Revoke widget permissions - %s", this.props.id);
this.props.onDeleteClick(); this._revokeWidgetPermission();
} else {
console.info("Revoke widget permissions - %s", this.props.id);
this._revokeWidgetPermission();
}
} }
/** /**
@ -544,18 +541,59 @@ export default class AppTile extends React.Component {
} }
} }
_onPopoutWidgetClick(e) { _onPopoutWidgetClick() {
// Using Object.assign workaround as the following opens in a new window instead of a new tab. // Using Object.assign workaround as the following opens in a new window instead of a new tab.
// window.open(this._getSafeUrl(), '_blank', 'noopener=yes'); // window.open(this._getSafeUrl(), '_blank', 'noopener=yes');
Object.assign(document.createElement('a'), Object.assign(document.createElement('a'),
{ target: '_blank', href: this._getSafeUrl(), rel: 'noopener'}).click(); { target: '_blank', href: this._getSafeUrl(), rel: 'noopener'}).click();
} }
_onReloadWidgetClick(e) { _onReloadWidgetClick() {
// Reload iframe in this way to avoid cross-origin restrictions // Reload iframe in this way to avoid cross-origin restrictions
this.refs.appFrame.src = this.refs.appFrame.src; this.refs.appFrame.src = this.refs.appFrame.src;
} }
_getMenuOptions(ev) {
// TODO: This block of code gets copy/pasted a lot. We should make that happen less.
const menuOptions = {};
const buttonRect = ev.target.getBoundingClientRect();
// The window X and Y offsets are to adjust position when zoomed in to page
const buttonLeft = buttonRect.left + window.pageXOffset;
const buttonTop = buttonRect.top + window.pageYOffset;
// Align the right edge of the menu to the left edge of the button
menuOptions.right = window.innerWidth - buttonLeft;
// Align the menu vertically on whichever side of the button has more
// space available.
if (buttonTop < window.innerHeight / 2) {
menuOptions.top = buttonTop;
} else {
menuOptions.bottom = window.innerHeight - buttonTop;
}
return menuOptions;
}
_onContextMenuClick = (ev) => {
const WidgetContextMenu = sdk.getComponent('views.context_menus.WidgetContextMenu');
const menuOptions = {
...this._getMenuOptions(ev),
// A revoke handler is always required
onRevokeClicked: this._onRevokeClicked,
};
const canUserModify = this._canUserModify();
const showEditButton = Boolean(this._scalarClient && canUserModify);
const showDeleteButton = (this.props.showDelete === undefined || this.props.showDelete) && canUserModify;
const showPictureSnapshotButton = this._hasCapability('m.capability.screenshot') && this.props.show;
if (showEditButton) menuOptions.onEditClicked = this._onEditClick;
if (showDeleteButton) menuOptions.onDeleteClicked = this._onDeleteClick;
if (showPictureSnapshotButton) menuOptions.onSnapshotClicked = this._onSnapshotClick;
if (this.props.showReload) menuOptions.onReloadClicked = this._onReloadWidgetClick;
createMenu(WidgetContextMenu, menuOptions);
};
render() { render() {
let appTileBody; let appTileBody;
@ -565,7 +603,7 @@ export default class AppTile extends React.Component {
} }
// Note that there is advice saying allow-scripts shouldn't be used with allow-same-origin // Note that there is advice saying allow-scripts shouldn't be used with allow-same-origin
// because that would allow the iframe to prgramatically remove the sandbox attribute, but // because that would allow the iframe to programmatically remove the sandbox attribute, but
// this would only be for content hosted on the same origin as the riot client: anything // this would only be for content hosted on the same origin as the riot client: anything
// hosted on the same origin as the client will get the same access as if you clicked // hosted on the same origin as the client will get the same access as if you clicked
// a link to it. // a link to it.
@ -585,12 +623,14 @@ export default class AppTile extends React.Component {
</div> </div>
); );
if (!this.state.hasPermissionToLoad) { if (!this.state.hasPermissionToLoad) {
const isEncrypted = MatrixClientPeg.get().isRoomEncrypted(this.props.room.roomId);
appTileBody = ( appTileBody = (
<div className={appTileBodyClass}> <div className={appTileBodyClass}>
<AppPermission <AppPermission
roomId={this.props.room.roomId} roomId={this.props.room.roomId}
creatorUserId={this.props.creatorUserId} creatorUserId={this.props.creatorUserId}
url={this.state.widgetUrl} url={this.state.widgetUrl}
isRoomEncrypted={isEncrypted}
onPermissionGranted={this._grantWidgetPermission} onPermissionGranted={this._grantWidgetPermission}
/> />
</div> </div>
@ -643,13 +683,6 @@ export default class AppTile extends React.Component {
} }
} }
// editing is done in scalar
const canUserModify = this._canUserModify();
const showEditButton = Boolean(this._scalarClient && canUserModify);
const showDeleteButton = (this.props.showDelete === undefined || this.props.showDelete) && canUserModify;
const showCancelButton = (this.props.showCancel === undefined || this.props.showCancel) && !showDeleteButton;
// Picture snapshot - only show button when apps are maximised.
const showPictureSnapshotButton = this._hasCapability('m.capability.screenshot') && this.props.show;
const showMinimiseButton = this.props.showMinimise && this.props.show; const showMinimiseButton = this.props.showMinimise && this.props.show;
const showMaximiseButton = this.props.showMinimise && !this.props.show; const showMaximiseButton = this.props.showMinimise && !this.props.show;
@ -688,41 +721,17 @@ export default class AppTile extends React.Component {
{ this.props.showTitle && this._getTileTitle() } { this.props.showTitle && this._getTileTitle() }
</span> </span>
<span className="mx_AppTileMenuBarWidgets"> <span className="mx_AppTileMenuBarWidgets">
{ /* Reload widget */ }
{ this.props.showReload && <AccessibleButton
className="mx_AppTileMenuBar_iconButton mx_AppTileMenuBar_iconButton_reload"
title={_t('Reload widget')}
onClick={this._onReloadWidgetClick}
/> }
{ /* Popout widget */ } { /* Popout widget */ }
{ this.props.showPopout && <AccessibleButton { this.props.showPopout && <AccessibleButton
className="mx_AppTileMenuBar_iconButton mx_AppTileMenuBar_iconButton_popout" className="mx_AppTileMenuBar_iconButton mx_AppTileMenuBar_iconButton_popout"
title={_t('Popout widget')} title={_t('Popout widget')}
onClick={this._onPopoutWidgetClick} onClick={this._onPopoutWidgetClick}
/> } /> }
{ /* Snapshot widget */ } { /* Context menu */ }
{ showPictureSnapshotButton && <AccessibleButton { <AccessibleButton
className="mx_AppTileMenuBar_iconButton mx_AppTileMenuBar_iconButton_snapshot" className="mx_AppTileMenuBar_iconButton mx_AppTileMenuBar_iconButton_menu"
title={_t('Picture')} title={_t('More options')}
onClick={this._onSnapshotClick} onClick={this._onContextMenuClick}
/> }
{ /* Edit widget */ }
{ showEditButton && <AccessibleButton
className="mx_AppTileMenuBar_iconButton mx_AppTileMenuBar_iconButton_edit"
title={_t('Edit')}
onClick={this._onEditClick}
/> }
{ /* Delete widget */ }
{ showDeleteButton && <AccessibleButton
className="mx_AppTileMenuBar_iconButton mx_AppTileMenuBar_iconButton_delete"
title={_t('Delete widget')}
onClick={this._onDeleteClick}
/> }
{ /* Cancel widget */ }
{ showCancelButton && <AccessibleButton
className="mx_AppTileMenuBar_iconButton mx_AppTileMenuBar_iconButton_cancel"
title={_t('Revoke widget access')}
onClick={this._onCancelClick}
/> } /> }
</span> </span>
</div> } </div> }

View file

@ -17,7 +17,6 @@ limitations under the License.
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import sdk from '../../../index'; import sdk from '../../../index';
import Promise from 'bluebird';
/** /**
* A component which wraps an EditableText, with a spinner while updates take * A component which wraps an EditableText, with a spinner while updates take
@ -51,7 +50,7 @@ export default class EditableTextContainer extends React.Component {
this.setState({busy: true}); this.setState({busy: true});
this.props.getInitialValue().done( this.props.getInitialValue().then(
(result) => { (result) => {
if (this._unmounted) { return; } if (this._unmounted) { return; }
this.setState({ this.setState({
@ -83,7 +82,7 @@ export default class EditableTextContainer extends React.Component {
errorString: null, errorString: null,
}); });
this.props.onSubmit(value).done( this.props.onSubmit(value).then(
() => { () => {
if (this._unmounted) { return; } if (this._unmounted) { return; }
this.setState({ this.setState({

View file

@ -54,7 +54,7 @@ export default class ErrorBoundary extends React.PureComponent {
if (!PlatformPeg.get()) return; if (!PlatformPeg.get()) return;
MatrixClientPeg.get().stopClient(); MatrixClientPeg.get().stopClient();
MatrixClientPeg.get().store.deleteAllData().done(() => { MatrixClientPeg.get().store.deleteAllData().then(() => {
PlatformPeg.get().reload(); PlatformPeg.get().reload();
}); });
}; };

View file

@ -0,0 +1,28 @@
/*
Copyright 2019 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 AccessibleButton from "./AccessibleButton";
export default function FormButton(props) {
const {className, label, kind, ...restProps} = props;
const newClassName = (className || "") + " mx_FormButton";
const allProps = Object.assign({}, restProps,
{className: newClassName, kind: kind || "primary", children: [label]});
return React.createElement(AccessibleButton, allProps);
}
FormButton.propTypes = AccessibleButton.propTypes;

View file

@ -84,7 +84,7 @@ export default class ImageView extends React.Component {
title: _t('Error'), title: _t('Error'),
description: _t('You cannot delete this image. (%(code)s)', {code: code}), description: _t('You cannot delete this image. (%(code)s)', {code: code}),
}); });
}).done(); });
}, },
}); });
}; };

View file

@ -49,7 +49,7 @@ export default class LanguageDropdown extends React.Component {
this.setState({langs}); this.setState({langs});
}).catch(() => { }).catch(() => {
this.setState({langs: ['en']}); this.setState({langs: ['en']});
}).done(); });
if (!this.props.value) { if (!this.props.value) {
// If no value is given, we start with the first // If no value is given, we start with the first

View file

@ -100,7 +100,9 @@ module.exports = createReactClass({
const parent = ReactDOM.findDOMNode(this).parentNode; const parent = ReactDOM.findDOMNode(this).parentNode;
let style = {}; let style = {};
style = this._updatePosition(style); style = this._updatePosition(style);
style.display = "block"; // Hide the entire container when not visible. This prevents flashing of the tooltip
// if it is not meant to be visible on first mount.
style.display = this.props.visible ? "block" : "none";
const tooltipClasses = classNames("mx_Tooltip", this.props.tooltipClassName, { const tooltipClasses = classNames("mx_Tooltip", this.props.tooltipClassName, {
"mx_Tooltip_visible": this.props.visible, "mx_Tooltip_visible": this.props.visible,

View file

@ -36,7 +36,7 @@ export default createReactClass({
}, },
componentWillMount: function() { componentWillMount: function() {
this.context.matrixClient.getJoinedGroups().done((result) => { this.context.matrixClient.getJoinedGroups().then((result) => {
this.setState({groups: result.groups || [], error: null}); this.setState({groups: result.groups || [], error: null});
}, (err) => { }, (err) => {
console.error(err); console.error(err);

View file

@ -55,7 +55,7 @@ export default class MAudioBody extends React.Component {
decryptFile(content.file).then(function(blob) { decryptFile(content.file).then(function(blob) {
decryptedBlob = blob; decryptedBlob = blob;
return URL.createObjectURL(decryptedBlob); return URL.createObjectURL(decryptedBlob);
}).done((url) => { }).then((url) => {
this.setState({ this.setState({
decryptedUrl: url, decryptedUrl: url,
decryptedBlob: decryptedBlob, decryptedBlob: decryptedBlob,

View file

@ -24,7 +24,6 @@ import MFileBody from './MFileBody';
import Modal from '../../../Modal'; import Modal from '../../../Modal';
import sdk from '../../../index'; import sdk from '../../../index';
import { decryptFile } from '../../../utils/DecryptFile'; import { decryptFile } from '../../../utils/DecryptFile';
import Promise from 'bluebird';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
import SettingsStore from "../../../settings/SettingsStore"; import SettingsStore from "../../../settings/SettingsStore";
@ -289,7 +288,7 @@ export default class MImageBody extends React.Component {
this.setState({ this.setState({
error: err, error: err,
}); });
}).done(); });
} }
// Remember that the user wanted to show this particular image // Remember that the user wanted to show this particular image

View file

@ -111,10 +111,10 @@ export default class MKeyVerificationRequest extends React.Component {
userLabelForEventRoom(fromUserId, mxEvent)}</div>); userLabelForEventRoom(fromUserId, mxEvent)}</div>);
const isResolved = !(this.state.accepted || this.state.cancelled || this.state.done); const isResolved = !(this.state.accepted || this.state.cancelled || this.state.done);
if (isResolved) { if (isResolved) {
const AccessibleButton = sdk.getComponent("elements.AccessibleButton"); const FormButton = sdk.getComponent("elements.FormButton");
stateNode = (<div className="mx_KeyVerification_buttons"> stateNode = (<div className="mx_KeyVerification_buttons">
<AccessibleButton kind="decline" onClick={this._onRejectClicked}>{_t("Decline")}</AccessibleButton> <FormButton kind="danger" onClick={this._onRejectClicked} label={_t("Decline")} />
<AccessibleButton kind="accept" onClick={this._onAcceptClicked}>{_t("Accept")}</AccessibleButton> <FormButton onClick={this._onAcceptClicked} label={_t("Accept")} />
</div>); </div>);
} }
} else if (isOwn) { // request sent by us } else if (isOwn) { // request sent by us

View file

@ -20,7 +20,6 @@ import createReactClass from 'create-react-class';
import MFileBody from './MFileBody'; import MFileBody from './MFileBody';
import MatrixClientPeg from '../../../MatrixClientPeg'; import MatrixClientPeg from '../../../MatrixClientPeg';
import { decryptFile } from '../../../utils/DecryptFile'; import { decryptFile } from '../../../utils/DecryptFile';
import Promise from 'bluebird';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
import SettingsStore from "../../../settings/SettingsStore"; import SettingsStore from "../../../settings/SettingsStore";
@ -89,7 +88,7 @@ module.exports = createReactClass({
const content = this.props.mxEvent.getContent(); const content = this.props.mxEvent.getContent();
if (content.file !== undefined && this.state.decryptedUrl === null) { if (content.file !== undefined && this.state.decryptedUrl === null) {
let thumbnailPromise = Promise.resolve(null); let thumbnailPromise = Promise.resolve(null);
if (content.info.thumbnail_file) { if (content.info && content.info.thumbnail_file) {
thumbnailPromise = decryptFile( thumbnailPromise = decryptFile(
content.info.thumbnail_file, content.info.thumbnail_file,
).then(function(blob) { ).then(function(blob) {
@ -115,7 +114,7 @@ module.exports = createReactClass({
this.setState({ this.setState({
error: err, error: err,
}); });
}).done(); });
} }
}, },

View file

@ -43,7 +43,8 @@ export default class ReactionsRowButtonTooltip extends React.PureComponent {
if (room) { if (room) {
const senders = []; const senders = [];
for (const reactionEvent of reactionEvents) { for (const reactionEvent of reactionEvents) {
const { name } = room.getMember(reactionEvent.getSender()); const member = room.getMember(reactionEvent.getSender());
const name = member ? member.name : reactionEvent.getSender();
senders.push(name); senders.push(name);
} }
const shortName = unicodeToShortcode(content); const shortName = unicodeToShortcode(content);

View file

@ -14,7 +14,6 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import Promise from 'bluebird';
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import createReactClass from 'create-react-class'; import createReactClass from 'create-react-class';

View file

@ -21,7 +21,6 @@ import PropTypes from 'prop-types';
import classNames from 'classnames'; import classNames from 'classnames';
import flatMap from 'lodash/flatMap'; import flatMap from 'lodash/flatMap';
import type {Completion} from '../../../autocomplete/Autocompleter'; import type {Completion} from '../../../autocomplete/Autocompleter';
import Promise from 'bluebird';
import { Room } from 'matrix-js-sdk'; import { Room } from 'matrix-js-sdk';
import SettingsStore from "../../../settings/SettingsStore"; import SettingsStore from "../../../settings/SettingsStore";

View file

@ -53,7 +53,7 @@ module.exports = createReactClass({
); );
}, (error)=>{ }, (error)=>{
console.error("Failed to get URL preview: " + error); console.error("Failed to get URL preview: " + error);
}).done(); });
}, },
componentDidMount: function() { componentDidMount: function() {

View file

@ -248,7 +248,7 @@ module.exports = createReactClass({
return client.getStoredDevicesForUser(member.userId); return client.getStoredDevicesForUser(member.userId);
}).finally(function() { }).finally(function() {
self._cancelDeviceList = null; self._cancelDeviceList = null;
}).done(function(devices) { }).then(function(devices) {
if (cancelled) { if (cancelled) {
// we got cancelled - presumably a different user now // we got cancelled - presumably a different user now
return; return;
@ -581,7 +581,7 @@ module.exports = createReactClass({
}, },
).finally(()=>{ ).finally(()=>{
this.setState({ updating: this.state.updating - 1 }); this.setState({ updating: this.state.updating - 1 });
}).done(); });
}, },
onPowerChange: async function(powerLevel) { onPowerChange: async function(powerLevel) {
@ -638,7 +638,7 @@ module.exports = createReactClass({
this.setState({ updating: this.state.updating + 1 }); this.setState({ updating: this.state.updating + 1 });
createRoom({dmUserId: this.props.member.userId}).finally(() => { createRoom({dmUserId: this.props.member.userId}).finally(() => {
this.setState({ updating: this.state.updating - 1 }); this.setState({ updating: this.state.updating - 1 });
}).done(); });
}, },
onLeaveClick: function() { onLeaveClick: function() {

View file

@ -25,7 +25,6 @@ import RoomViewStore from '../../../stores/RoomViewStore';
import Stickerpicker from './Stickerpicker'; import Stickerpicker from './Stickerpicker';
import { makeRoomPermalink } from '../../../utils/permalinks/Permalinks'; import { makeRoomPermalink } from '../../../utils/permalinks/Permalinks';
import ContentMessages from '../../../ContentMessages'; import ContentMessages from '../../../ContentMessages';
import classNames from 'classnames';
import E2EIcon from './E2EIcon'; import E2EIcon from './E2EIcon';
function ComposerAvatar(props) { function ComposerAvatar(props) {
@ -353,13 +352,9 @@ export default class MessageComposer extends React.Component {
); );
} }
const wrapperClasses = classNames({
mx_MessageComposer_wrapper: true,
mx_MessageComposer_hasE2EIcon: !!this.props.e2eStatus,
});
return ( return (
<div className="mx_MessageComposer"> <div className="mx_MessageComposer">
<div className={wrapperClasses}> <div className="mx_MessageComposer_wrapper">
<div className="mx_MessageComposer_row"> <div className="mx_MessageComposer_row">
{ controls } { controls }
</div> </div>

View file

@ -460,13 +460,9 @@ export default class SlateMessageComposer extends React.Component {
const showFormatBar = this.state.showFormatting && this.state.inputState.isRichTextEnabled; const showFormatBar = this.state.showFormatting && this.state.inputState.isRichTextEnabled;
const wrapperClasses = classNames({
mx_MessageComposer_wrapper: true,
mx_MessageComposer_hasE2EIcon: !!this.props.e2eStatus,
});
return ( return (
<div className="mx_MessageComposer"> <div className="mx_MessageComposer">
<div className={wrapperClasses}> <div className="mx_MessageComposer_wrapper">
<div className="mx_MessageComposer_row"> <div className="mx_MessageComposer_row">
{ controls } { controls }
</div> </div>

View file

@ -74,10 +74,10 @@ export default class Stickerpicker extends React.Component {
this.forceUpdate(); this.forceUpdate();
return this.scalarClient; return this.scalarClient;
}).catch((e) => { }).catch((e) => {
this._imError(_td("Failed to connect to integrations server"), e); this._imError(_td("Failed to connect to integration manager"), e);
}); });
} else { } else {
this._imError(_td("No integrations server is configured to manage stickers with")); IntegrationManagers.sharedInstance().openNoManagerDialog();
} }
} }
@ -287,12 +287,17 @@ export default class Stickerpicker extends React.Component {
return stickersContent; return stickersContent;
} }
/** // Dev note: this isn't jsdoc because it's angry.
/*
* Show the sticker picker overlay * Show the sticker picker overlay
* If no stickerpacks have been added, show a link to the integration manager add sticker packs page. * If no stickerpacks have been added, show a link to the integration manager add sticker packs page.
* @param {Event} e Event that triggered the function
*/ */
_onShowStickersClick(e) { _onShowStickersClick(e) {
if (!SettingsStore.getValue("integrationProvisioning")) {
// Intercept this case and spawn a warning.
return IntegrationManagers.sharedInstance().showDisabledDialog();
}
// XXX: Simplify by using a context menu that is positioned relative to the sticker picker button // XXX: Simplify by using a context menu that is positioned relative to the sticker picker button
const buttonRect = e.target.getBoundingClientRect(); const buttonRect = e.target.getBoundingClientRect();
@ -346,7 +351,7 @@ export default class Stickerpicker extends React.Component {
} }
/** /**
* Launch the integrations manager on the stickers integration page * Launch the integration manager on the stickers integration page
*/ */
_launchManageIntegrations() { _launchManageIntegrations() {
// TODO: Open the right integration manager for the widget // TODO: Open the right integration manager for the widget

View file

@ -112,7 +112,7 @@ module.exports = createReactClass({
} }
}); });
httpPromise.done(function() { httpPromise.then(function() {
self.setState({ self.setState({
phase: self.Phases.Display, phase: self.Phases.Display,
avatarUrl: MatrixClientPeg.get().mxcUrlToHttp(newUrl), avatarUrl: MatrixClientPeg.get().mxcUrlToHttp(newUrl),

View file

@ -25,7 +25,6 @@ const Modal = require("../../../Modal");
const sdk = require("../../../index"); const sdk = require("../../../index");
import dis from "../../../dispatcher"; import dis from "../../../dispatcher";
import Promise from 'bluebird';
import AccessibleButton from '../elements/AccessibleButton'; import AccessibleButton from '../elements/AccessibleButton';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
@ -174,7 +173,7 @@ module.exports = createReactClass({
newPassword: "", newPassword: "",
newPasswordConfirm: "", newPasswordConfirm: "",
}); });
}).done(); });
}, },
_optionallySetEmail: function() { _optionallySetEmail: function() {

View file

@ -52,7 +52,7 @@ export default class DevicesPanel extends React.Component {
} }
_loadDevices() { _loadDevices() {
MatrixClientPeg.get().getDevices().done( MatrixClientPeg.get().getDevices().then(
(resp) => { (resp) => {
if (this._unmounted) { return; } if (this._unmounted) { return; }
this.setState({devices: resp.devices || []}); this.setState({devices: resp.devices || []});

View file

@ -21,12 +21,9 @@ import sdk from '../../../index';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
import dis from '../../../dispatcher'; import dis from '../../../dispatcher';
export default class IntegrationsManager extends React.Component { export default class IntegrationManager extends React.Component {
static propTypes = { static propTypes = {
// false to display an error saying that there is no integrations manager configured // false to display an error saying that we couldn't connect to the integration manager
configured: PropTypes.bool.isRequired,
// false to display an error saying that we couldn't connect to the integrations manager
connected: PropTypes.bool.isRequired, connected: PropTypes.bool.isRequired,
// true to display a loading spinner // true to display a loading spinner
@ -40,7 +37,6 @@ export default class IntegrationsManager extends React.Component {
}; };
static defaultProps = { static defaultProps = {
configured: true,
connected: true, connected: true,
loading: false, loading: false,
}; };
@ -70,20 +66,11 @@ export default class IntegrationsManager extends React.Component {
}; };
render() { render() {
if (!this.props.configured) {
return (
<div className='mx_IntegrationsManager_error'>
<h3>{_t("No integrations server configured")}</h3>
<p>{_t("This Riot instance does not have an integrations server configured.")}</p>
</div>
);
}
if (this.props.loading) { if (this.props.loading) {
const Spinner = sdk.getComponent("elements.Spinner"); const Spinner = sdk.getComponent("elements.Spinner");
return ( return (
<div className='mx_IntegrationsManager_loading'> <div className='mx_IntegrationManager_loading'>
<h3>{_t("Connecting to integrations server...")}</h3> <h3>{_t("Connecting to integration manager...")}</h3>
<Spinner /> <Spinner />
</div> </div>
); );
@ -91,9 +78,9 @@ export default class IntegrationsManager extends React.Component {
if (!this.props.connected) { if (!this.props.connected) {
return ( return (
<div className='mx_IntegrationsManager_error'> <div className='mx_IntegrationManager_error'>
<h3>{_t("Cannot connect to integrations server")}</h3> <h3>{_t("Cannot connect to integration manager")}</h3>
<p>{_t("The integrations server is offline or it cannot reach your homeserver.")}</p> <p>{_t("The integration manager is offline or it cannot reach your homeserver.")}</p>
</div> </div>
); );
} }

View file

@ -16,7 +16,6 @@ limitations under the License.
import React from 'react'; import React from 'react';
import createReactClass from 'create-react-class'; import createReactClass from 'create-react-class';
import Promise from 'bluebird';
import sdk from '../../../index'; import sdk from '../../../index';
import { _t } from '../../../languageHandler'; import { _t } from '../../../languageHandler';
import MatrixClientPeg from '../../../MatrixClientPeg'; import MatrixClientPeg from '../../../MatrixClientPeg';
@ -97,7 +96,7 @@ module.exports = createReactClass({
phase: this.phases.LOADING, phase: this.phases.LOADING,
}); });
MatrixClientPeg.get().setPushRuleEnabled('global', self.state.masterPushRule.kind, self.state.masterPushRule.rule_id, !checked).done(function() { MatrixClientPeg.get().setPushRuleEnabled('global', self.state.masterPushRule.kind, self.state.masterPushRule.rule_id, !checked).then(function() {
self._refreshFromServer(); self._refreshFromServer();
}); });
}, },
@ -170,7 +169,7 @@ module.exports = createReactClass({
emailPusher.kind = null; emailPusher.kind = null;
emailPusherPromise = MatrixClientPeg.get().setPusher(emailPusher); emailPusherPromise = MatrixClientPeg.get().setPusher(emailPusher);
} }
emailPusherPromise.done(() => { emailPusherPromise.then(() => {
this._refreshFromServer(); this._refreshFromServer();
}, (error) => { }, (error) => {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
@ -274,7 +273,7 @@ module.exports = createReactClass({
} }
} }
Promise.all(deferreds).done(function() { Promise.all(deferreds).then(function() {
self._refreshFromServer(); self._refreshFromServer();
}, function(error) { }, function(error) {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
@ -343,7 +342,7 @@ module.exports = createReactClass({
} }
} }
Promise.all(deferreds).done(function(resps) { Promise.all(deferreds).then(function(resps) {
self._refreshFromServer(); self._refreshFromServer();
}, function(error) { }, function(error) {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
@ -398,7 +397,7 @@ module.exports = createReactClass({
}; };
// Then, add the new ones // Then, add the new ones
Promise.all(removeDeferreds).done(function(resps) { Promise.all(removeDeferreds).then(function(resps) {
const deferreds = []; const deferreds = [];
let pushRuleVectorStateKind = self.state.vectorContentRules.vectorState; let pushRuleVectorStateKind = self.state.vectorContentRules.vectorState;
@ -434,7 +433,7 @@ module.exports = createReactClass({
} }
} }
Promise.all(deferreds).done(function(resps) { Promise.all(deferreds).then(function(resps) {
self._refreshFromServer(); self._refreshFromServer();
}, onError); }, onError);
}, onError); }, onError);
@ -650,7 +649,7 @@ module.exports = createReactClass({
externalContentRules: self.state.externalContentRules, externalContentRules: self.state.externalContentRules,
externalPushRules: self.state.externalPushRules, externalPushRules: self.state.externalPushRules,
}); });
}).done(); });
MatrixClientPeg.get().getThreePids().then((r) => this.setState({threepids: r.threepids})); MatrixClientPeg.get().getThreePids().then((r) => this.setState({threepids: r.threepids}));
}, },

View file

@ -16,13 +16,9 @@ limitations under the License.
import React from 'react'; import React from 'react';
import {_t} from "../../../languageHandler"; import {_t} from "../../../languageHandler";
import sdk from '../../../index';
import Field from "../elements/Field";
import {IntegrationManagers} from "../../../integrations/IntegrationManagers"; import {IntegrationManagers} from "../../../integrations/IntegrationManagers";
import MatrixClientPeg from "../../../MatrixClientPeg"; import sdk from '../../../index';
import {SERVICE_TYPES} from "matrix-js-sdk"; import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore";
import {IntegrationManagerInstance} from "../../../integrations/IntegrationManagerInstance";
import Modal from "../../../Modal";
export default class SetIntegrationManager extends React.Component { export default class SetIntegrationManager extends React.Component {
constructor() { constructor() {
@ -32,135 +28,23 @@ export default class SetIntegrationManager extends React.Component {
this.state = { this.state = {
currentManager, currentManager,
url: "", // user-entered text provisioningEnabled: SettingsStore.getValue("integrationProvisioning"),
error: null,
busy: false,
checking: false,
}; };
} }
_onUrlChanged = (ev) => { onProvisioningToggled = () => {
const u = ev.target.value; const current = this.state.provisioningEnabled;
this.setState({url: u}); SettingsStore.setValue("integrationProvisioning", null, SettingLevel.ACCOUNT, !current).catch(err => {
}; console.error("Error changing integration manager provisioning");
console.error(err);
_getTooltip = () => { this.setState({provisioningEnabled: current});
if (this.state.checking) { });
const InlineSpinner = sdk.getComponent('views.elements.InlineSpinner'); this.setState({provisioningEnabled: !current});
return <div>
<InlineSpinner />
{ _t("Checking server") }
</div>;
} else if (this.state.error) {
return <span className="warning">{this.state.error}</span>;
} else {
return null;
}
};
_canChange = () => {
return !!this.state.url && !this.state.busy;
};
_continueTerms = async (manager) => {
try {
await IntegrationManagers.sharedInstance().overwriteManagerOnAccount(manager);
this.setState({
busy: false,
error: null,
currentManager: IntegrationManagers.sharedInstance().getPrimaryManager(),
url: "", // clear input
});
} catch (e) {
console.error(e);
this.setState({
busy: false,
error: _t("Failed to update integration manager"),
});
}
};
_setManager = async (ev) => {
// Don't reload the page when the user hits enter in the form.
ev.preventDefault();
ev.stopPropagation();
this.setState({busy: true, checking: true, error: null});
let offline = false;
let manager: IntegrationManagerInstance;
try {
manager = await IntegrationManagers.sharedInstance().tryDiscoverManager(this.state.url);
offline = !manager; // no manager implies offline
} catch (e) {
console.error(e);
offline = true; // probably a connection error
}
if (offline) {
this.setState({
busy: false,
checking: false,
error: _t("Integration manager offline or not accessible."),
});
return;
}
// Test the manager (causes terms of service prompt if agreement is needed)
// We also cancel the tooltip at this point so it doesn't collide with the dialog.
this.setState({checking: false});
try {
const client = manager.getScalarClient();
await client.connect();
} catch (e) {
console.error(e);
this.setState({
busy: false,
error: _t("Terms of service not accepted or the integration manager is invalid."),
});
return;
}
// Specifically request the terms of service to see if there are any.
// The above won't trigger a terms of service check if there are no terms to
// sign, so when there's no terms at all we need to ensure we tell the user.
let hasTerms = true;
try {
const terms = await MatrixClientPeg.get().getTerms(SERVICE_TYPES.IM, manager.trimmedApiUrl);
hasTerms = terms && terms['policies'] && Object.keys(terms['policies']).length > 0;
} catch (e) {
// Assume errors mean there are no terms. This could be a 404, 500, etc
console.error(e);
hasTerms = false;
}
if (!hasTerms) {
this.setState({busy: false});
const QuestionDialog = sdk.getComponent("views.dialogs.QuestionDialog");
Modal.createTrackedDialog('No Terms Warning', '', QuestionDialog, {
title: _t("Integration manager has no terms of service"),
description: (
<div>
<span className="warning">
{_t("The integration manager you have chosen does not have any terms of service.")}
</span>
<span>
&nbsp;{_t("Only continue if you trust the owner of the server.")}
</span>
</div>
),
button: _t("Continue"),
onFinished: async (confirmed) => {
if (!confirmed) return;
this._continueTerms(manager);
},
});
return;
}
this._continueTerms(manager);
}; };
render() { render() {
const AccessibleButton = sdk.getComponent('views.elements.AccessibleButton'); const ToggleSwitch = sdk.getComponent("views.elements.ToggleSwitch");
const currentManager = this.state.currentManager; const currentManager = this.state.currentManager;
let managerName; let managerName;
@ -168,45 +52,32 @@ export default class SetIntegrationManager extends React.Component {
if (currentManager) { if (currentManager) {
managerName = `(${currentManager.name})`; managerName = `(${currentManager.name})`;
bodyText = _t( bodyText = _t(
"You are currently using <b>%(serverName)s</b> to manage your bots, widgets, " + "Use an Integration Manager <b>(%(serverName)s)</b> to manage bots, widgets, " +
"and sticker packs.", "and sticker packs.",
{serverName: currentManager.name}, {serverName: currentManager.name},
{ b: sub => <b>{sub}</b> }, { b: sub => <b>{sub}</b> },
); );
} else { } else {
bodyText = _t( bodyText = _t("Use an Integration Manager to manage bots, widgets, and sticker packs.");
"Add which integration manager you want to manage your bots, widgets, " +
"and sticker packs.",
);
} }
return ( return (
<form className="mx_SettingsTab_section mx_SetIntegrationManager" onSubmit={this._setManager}> <div className='mx_SetIntegrationManager'>
<div className="mx_SettingsTab_heading"> <div className="mx_SettingsTab_heading">
<span>{_t("Integration Manager")}</span> <span>{_t("Manage integrations")}</span>
<span className="mx_SettingsTab_subheading">{managerName}</span> <span className="mx_SettingsTab_subheading">{managerName}</span>
<ToggleSwitch checked={this.state.provisioningEnabled} onChange={this.onProvisioningToggled} />
</div> </div>
<span className="mx_SettingsTab_subsectionText"> <span className="mx_SettingsTab_subsectionText">
{bodyText} {bodyText}
<br />
<br />
{_t(
"Integration Managers receive configuration data, and can modify widgets, " +
"send room invites, and set power levels on your behalf.",
)}
</span> </span>
<Field </div>
label={_t("Enter a new integration manager")}
id="mx_SetIntegrationManager_newUrl"
type="text" value={this.state.url}
autoComplete="off"
onChange={this._onUrlChanged}
tooltipContent={this._getTooltip()}
tooltipClassName="mx_SetIntegrationManager_tooltip"
disabled={this.state.busy}
flagInvalid={!!this.state.error}
/>
<AccessibleButton
kind="primary_sm"
type="submit"
disabled={!this._canChange()}
onClick={this._setManager}
>{_t("Change")}</AccessibleButton>
</form>
); );
} }
} }

View file

@ -75,7 +75,7 @@ export default class HelpUserSettingsTab extends React.Component {
// stopping in the middle of the logs. // stopping in the middle of the logs.
console.log("Clear cache & reload clicked"); console.log("Clear cache & reload clicked");
MatrixClientPeg.get().stopClient(); MatrixClientPeg.get().stopClient();
MatrixClientPeg.get().store.deleteAllData().done(() => { MatrixClientPeg.get().store.deleteAllData().then(() => {
PlatformPeg.get().reload(); PlatformPeg.get().reload();
}); });
}; };

View file

@ -0,0 +1,123 @@
/*
Copyright 2019 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 PropTypes from 'prop-types';
import sdk from "../../../index";
import { _t } from '../../../languageHandler';
import Modal from "../../../Modal";
import MatrixClientPeg from '../../../MatrixClientPeg';
import {verificationMethods} from 'matrix-js-sdk/lib/crypto';
import KeyVerificationStateObserver, {userLabelForEventRoom} from "../../../utils/KeyVerificationStateObserver";
import dis from "../../../dispatcher";
export default class VerificationRequestToast extends React.PureComponent {
constructor(props) {
super(props);
const {event, timeout} = props.request;
// to_device requests don't have a timestamp, so consider them age=0
const age = event.getTs() ? event.getLocalAge() : 0;
const remaining = Math.max(0, timeout - age);
const counter = Math.ceil(remaining / 1000);
this.state = {counter};
if (this.props.requestObserver) {
this.props.requestObserver.setCallback(this._checkRequestIsPending);
}
}
componentDidMount() {
if (this.props.requestObserver) {
this.props.requestObserver.attach();
this._checkRequestIsPending();
}
this._intervalHandle = setInterval(() => {
let {counter} = this.state;
counter -= 1;
if (counter <= 0) {
this.cancel();
} else {
this.setState({counter});
}
}, 1000);
}
componentWillUnmount() {
clearInterval(this._intervalHandle);
if (this.props.requestObserver) {
this.props.requestObserver.detach();
}
}
_checkRequestIsPending = () => {
if (!this.props.requestObserver.pending) {
this.props.dismiss();
}
}
cancel = () => {
this.props.dismiss();
try {
this.props.request.cancel();
} catch (err) {
console.error("Error while cancelling verification request", err);
}
}
accept = () => {
this.props.dismiss();
const {event} = this.props.request;
// no room id for to_device requests
if (event.getRoomId()) {
dis.dispatch({
action: 'view_room',
room_id: event.getRoomId(),
should_peek: false,
});
}
const verifier = this.props.request.beginKeyVerification(verificationMethods.SAS);
const IncomingSasDialog = sdk.getComponent('views.dialogs.IncomingSasDialog');
Modal.createTrackedDialog('Incoming Verification', '', IncomingSasDialog, {verifier});
};
render() {
const FormButton = sdk.getComponent("elements.FormButton");
const {event} = this.props.request;
const userId = event.getSender();
let nameLabel = event.getRoomId() ? userLabelForEventRoom(userId, event) : userId;
// for legacy to_device verification requests
if (nameLabel === userId) {
const client = MatrixClientPeg.get();
const user = client.getUser(event.getSender());
if (user && user.displayName) {
nameLabel = _t("%(name)s (%(userId)s)", {name: user.displayName, userId});
}
}
return (<div>
<div className="mx_Toast_description">{nameLabel}</div>
<div className="mx_Toast_buttons" aria-live="off">
<FormButton label={_t("Decline (%(counter)s)", {counter: this.state.counter})} kind="danger" onClick={this.cancel} />
<FormButton label={_t("Accept")} onClick={this.accept} />
</div>
</div>);
}
}
VerificationRequestToast.propTypes = {
dismiss: PropTypes.func.isRequired,
request: PropTypes.object.isRequired,
requestObserver: PropTypes.instanceOf(KeyVerificationStateObserver),
};

View file

@ -90,6 +90,13 @@ module.exports = createReactClass({
} }
} else { } else {
call = CallHandler.getAnyActiveCall(); call = CallHandler.getAnyActiveCall();
// Ignore calls if we can't get the room associated with them.
// I think the underlying problem is that the js-sdk sends events
// for calls before it has made the rooms available in the store,
// although this isn't confirmed.
if (MatrixClientPeg.get().getRoom(call.roomId) === null) {
call = null;
}
this.setState({ call: call }); this.setState({ call: call });
} }

View file

@ -21,7 +21,6 @@ import { _t } from './languageHandler';
import dis from "./dispatcher"; import dis from "./dispatcher";
import * as Rooms from "./Rooms"; import * as Rooms from "./Rooms";
import Promise from 'bluebird';
import {getAddressType} from "./UserAddress"; import {getAddressType} from "./UserAddress";
/** /**

View file

@ -2260,5 +2260,7 @@
"You cancelled": "Отказахте потвърждаването", "You cancelled": "Отказахте потвърждаването",
"%(name)s cancelled": "%(name)s отказа", "%(name)s cancelled": "%(name)s отказа",
"%(name)s wants to verify": "%(name)s иска да извърши потвърждение", "%(name)s wants to verify": "%(name)s иска да извърши потвърждение",
"You sent a verification request": "Изпратихте заявка за потвърждение" "You sent a verification request": "Изпратихте заявка за потвърждение",
"Custom (%(level)s)": "Собствен (%(level)s)",
"Try out new ways to ignore people (experimental)": "Опитайте нови начини да игнорирате хора (експериментално)"
} }

View file

@ -1269,7 +1269,7 @@
"Security & Privacy": "Bezpečnost & Soukromí", "Security & Privacy": "Bezpečnost & Soukromí",
"Encryption": "Šifrování", "Encryption": "Šifrování",
"Once enabled, encryption cannot be disabled.": "Když se šifrování zapne, už nepůjde vypnout.", "Once enabled, encryption cannot be disabled.": "Když se šifrování zapne, už nepůjde vypnout.",
"Encrypted": "Šifrování je zapnuté", "Encrypted": "Šifrování",
"General": "Obecné", "General": "Obecné",
"General failure": "Nějaká chyba", "General failure": "Nějaká chyba",
"This homeserver does not support login using email address.": "Tento homeserver neumožňuje přihlášní pomocí emailu.", "This homeserver does not support login using email address.": "Tento homeserver neumožňuje přihlášní pomocí emailu.",

View file

@ -342,7 +342,7 @@
"Multiple integration managers": "Multiple integration managers", "Multiple integration managers": "Multiple integration managers",
"Try out new ways to ignore people (experimental)": "Try out new ways to ignore people (experimental)", "Try out new ways to ignore people (experimental)": "Try out new ways to ignore people (experimental)",
"Send verification requests in direct message, including a new verification UX in the member panel.": "Send verification requests in direct message, including a new verification UX in the member panel.", "Send verification requests in direct message, including a new verification UX in the member panel.": "Send verification requests in direct message, including a new verification UX in the member panel.",
"Enable cross-signing to verify per-user instead of per-device": "Enable cross-signing to verify per-user instead of per-device", "Enable cross-signing to verify per-user instead of per-device (in development)": "Enable cross-signing to verify per-user instead of per-device (in development)",
"Enable local event indexing and E2EE search (requires restart)": "Enable local event indexing and E2EE search (requires restart)", "Enable local event indexing and E2EE search (requires restart)": "Enable local event indexing and E2EE search (requires restart)",
"Use the new, faster, composer for writing messages": "Use the new, faster, composer for writing messages", "Use the new, faster, composer for writing messages": "Use the new, faster, composer for writing messages",
"Enable Emoji suggestions while typing": "Enable Emoji suggestions while typing", "Enable Emoji suggestions while typing": "Enable Emoji suggestions while typing",
@ -481,6 +481,7 @@
"Headphones": "Headphones", "Headphones": "Headphones",
"Folder": "Folder", "Folder": "Folder",
"Pin": "Pin", "Pin": "Pin",
"Decline (%(counter)s)": "Decline (%(counter)s)",
"Accept <policyLink /> to continue:": "Accept <policyLink /> to continue:", "Accept <policyLink /> to continue:": "Accept <policyLink /> to continue:",
"Failed to upload profile picture!": "Failed to upload profile picture!", "Failed to upload profile picture!": "Failed to upload profile picture!",
"Upload new:": "Upload new:", "Upload new:": "Upload new:",
@ -507,11 +508,9 @@
"Failed to set display name": "Failed to set display name", "Failed to set display name": "Failed to set display name",
"Disable Notifications": "Disable Notifications", "Disable Notifications": "Disable Notifications",
"Enable Notifications": "Enable Notifications", "Enable Notifications": "Enable Notifications",
"No integrations server configured": "No integrations server configured", "Connecting to integration manager...": "Connecting to integration manager...",
"This Riot instance does not have an integrations server configured.": "This Riot instance does not have an integrations server configured.", "Cannot connect to integration manager": "Cannot connect to integration manager",
"Connecting to integrations server...": "Connecting to integrations server...", "The integration manager is offline or it cannot reach your homeserver.": "The integration manager is offline or it cannot reach your homeserver.",
"Cannot connect to integrations server": "Cannot connect to integrations server",
"The integrations server is offline or it cannot reach your homeserver.": "The integrations server is offline or it cannot reach your homeserver.",
"Delete Backup": "Delete Backup", "Delete Backup": "Delete Backup",
"Are you sure? You will lose your encrypted messages if your keys are not backed up properly.": "Are you sure? You will lose your encrypted messages if your keys are not backed up properly.", "Are you sure? You will lose your encrypted messages if your keys are not backed up properly.": "Are you sure? You will lose your encrypted messages if your keys are not backed up properly.",
"Encrypted messages are secured with end-to-end encryption. Only you and the recipient(s) have the keys to read these messages.": "Encrypted messages are secured with end-to-end encryption. Only you and the recipient(s) have the keys to read these messages.", "Encrypted messages are secured with end-to-end encryption. Only you and the recipient(s) have the keys to read these messages.": "Encrypted messages are secured with end-to-end encryption. Only you and the recipient(s) have the keys to read these messages.",
@ -598,15 +597,10 @@
"Do not use an identity server": "Do not use an identity server", "Do not use an identity server": "Do not use an identity server",
"Enter a new identity server": "Enter a new identity server", "Enter a new identity server": "Enter a new identity server",
"Change": "Change", "Change": "Change",
"Failed to update integration manager": "Failed to update integration manager", "Use an Integration Manager <b>(%(serverName)s)</b> to manage bots, widgets, and sticker packs.": "Use an Integration Manager <b>(%(serverName)s)</b> to manage bots, widgets, and sticker packs.",
"Integration manager offline or not accessible.": "Integration manager offline or not accessible.", "Use an Integration Manager to manage bots, widgets, and sticker packs.": "Use an Integration Manager to manage bots, widgets, and sticker packs.",
"Terms of service not accepted or the integration manager is invalid.": "Terms of service not accepted or the integration manager is invalid.", "Manage integrations": "Manage integrations",
"Integration manager has no terms of service": "Integration manager has no terms of service", "Integration Managers receive configuration data, and can modify widgets, send room invites, and set power levels on your behalf.": "Integration Managers receive configuration data, and can modify widgets, send room invites, and set power levels on your behalf.",
"The integration manager you have chosen does not have any terms of service.": "The integration manager you have chosen does not have any terms of service.",
"You are currently using <b>%(serverName)s</b> to manage your bots, widgets, and sticker packs.": "You are currently using <b>%(serverName)s</b> to manage your bots, widgets, and sticker packs.",
"Add which integration manager you want to manage your bots, widgets, and sticker packs.": "Add which integration manager you want to manage your bots, widgets, and sticker packs.",
"Integration Manager": "Integration Manager",
"Enter a new integration manager": "Enter a new integration manager",
"Flair": "Flair", "Flair": "Flair",
"Failed to change password. Is your password correct?": "Failed to change password. Is your password correct?", "Failed to change password. Is your password correct?": "Failed to change password. Is your password correct?",
"Success": "Success", "Success": "Success",
@ -1024,8 +1018,7 @@
"numbered-list": "numbered-list", "numbered-list": "numbered-list",
"Show Text Formatting Toolbar": "Show Text Formatting Toolbar", "Show Text Formatting Toolbar": "Show Text Formatting Toolbar",
"Hide Text Formatting Toolbar": "Hide Text Formatting Toolbar", "Hide Text Formatting Toolbar": "Hide Text Formatting Toolbar",
"Failed to connect to integrations server": "Failed to connect to integrations server", "Failed to connect to integration manager": "Failed to connect to integration manager",
"No integrations server is configured to manage stickers with": "No integrations server is configured to manage stickers with",
"You don't currently have any stickerpacks enabled": "You don't currently have any stickerpacks enabled", "You don't currently have any stickerpacks enabled": "You don't currently have any stickerpacks enabled",
"Add some now": "Add some now", "Add some now": "Add some now",
"Stickerpack": "Stickerpack", "Stickerpack": "Stickerpack",
@ -1195,6 +1188,7 @@
"Widget ID": "Widget ID", "Widget ID": "Widget ID",
"Using this widget may share data <helpIcon /> with %(widgetDomain)s & your Integration Manager.": "Using this widget may share data <helpIcon /> with %(widgetDomain)s & your Integration Manager.", "Using this widget may share data <helpIcon /> with %(widgetDomain)s & your Integration Manager.": "Using this widget may share data <helpIcon /> with %(widgetDomain)s & your Integration Manager.",
"Using this widget may share data <helpIcon /> with %(widgetDomain)s.": "Using this widget may share data <helpIcon /> with %(widgetDomain)s.", "Using this widget may share data <helpIcon /> with %(widgetDomain)s.": "Using this widget may share data <helpIcon /> with %(widgetDomain)s.",
"Widgets do not use message encryption.": "Widgets do not use message encryption.",
"Widget added by": "Widget added by", "Widget added by": "Widget added by",
"This widget may use cookies.": "This widget may use cookies.", "This widget may use cookies.": "This widget may use cookies.",
"Delete Widget": "Delete Widget", "Delete Widget": "Delete Widget",
@ -1204,10 +1198,8 @@
"An error ocurred whilst trying to remove the widget from the room": "An error ocurred whilst trying to remove the widget from the room", "An error ocurred whilst trying to remove the widget from the room": "An error ocurred whilst trying to remove the widget from the room",
"Minimize apps": "Minimize apps", "Minimize apps": "Minimize apps",
"Maximize apps": "Maximize apps", "Maximize apps": "Maximize apps",
"Reload widget": "Reload widget",
"Popout widget": "Popout widget", "Popout widget": "Popout widget",
"Picture": "Picture", "More options": "More options",
"Revoke widget access": "Revoke widget access",
"Create new room": "Create new room", "Create new room": "Create new room",
"Unblacklist": "Unblacklist", "Unblacklist": "Unblacklist",
"Blacklist": "Blacklist", "Blacklist": "Blacklist",
@ -1398,6 +1390,10 @@
"Verifying this user will mark their device as trusted, and also mark your device as trusted to them.": "Verifying this user will mark their device as trusted, and also mark your device as trusted to them.", "Verifying this user will mark their device as trusted, and also mark your device as trusted to them.": "Verifying this user will mark their device as trusted, and also mark your device as trusted to them.",
"Waiting for partner to confirm...": "Waiting for partner to confirm...", "Waiting for partner to confirm...": "Waiting for partner to confirm...",
"Incoming Verification Request": "Incoming Verification Request", "Incoming Verification Request": "Incoming Verification Request",
"Integrations are disabled": "Integrations are disabled",
"Enable 'Manage Integrations' in Settings to do this.": "Enable 'Manage Integrations' in Settings to do this.",
"Integrations not allowed": "Integrations not allowed",
"Your Riot doesn't allow you to use an Integration Manager to do this. Please contact an admin.": "Your Riot doesn't allow you to use an Integration Manager to do this. Please contact an admin.",
"You added a new device '%(displayName)s', which is requesting encryption keys.": "You added a new device '%(displayName)s', which is requesting encryption keys.", "You added a new device '%(displayName)s', which is requesting encryption keys.": "You added a new device '%(displayName)s', which is requesting encryption keys.",
"Your unverified device '%(displayName)s' is requesting encryption keys.": "Your unverified device '%(displayName)s' is requesting encryption keys.", "Your unverified device '%(displayName)s' is requesting encryption keys.": "Your unverified device '%(displayName)s' is requesting encryption keys.",
"Start verification": "Start verification", "Start verification": "Start verification",
@ -1475,7 +1471,7 @@
"Missing session data": "Missing session data", "Missing session data": "Missing session data",
"Some session data, including encrypted message keys, is missing. Sign out and sign in to fix this, restoring keys from backup.": "Some session data, including encrypted message keys, is missing. Sign out and sign in to fix this, restoring keys from backup.", "Some session data, including encrypted message keys, is missing. Sign out and sign in to fix this, restoring keys from backup.": "Some session data, including encrypted message keys, is missing. Sign out and sign in to fix this, restoring keys from backup.",
"Your browser likely removed this data when running low on disk space.": "Your browser likely removed this data when running low on disk space.", "Your browser likely removed this data when running low on disk space.": "Your browser likely removed this data when running low on disk space.",
"Integrations Manager": "Integrations Manager", "Integration Manager": "Integration Manager",
"Find others by phone or email": "Find others by phone or email", "Find others by phone or email": "Find others by phone or email",
"Be found by phone or email": "Be found by phone or email", "Be found by phone or email": "Be found by phone or email",
"Use bots, bridges, widgets and sticker packs": "Use bots, bridges, widgets and sticker packs", "Use bots, bridges, widgets and sticker packs": "Use bots, bridges, widgets and sticker packs",
@ -1564,6 +1560,10 @@
"Hide": "Hide", "Hide": "Hide",
"Home": "Home", "Home": "Home",
"Sign in": "Sign in", "Sign in": "Sign in",
"Reload": "Reload",
"Take picture": "Take picture",
"Remove for everyone": "Remove for everyone",
"Remove for me": "Remove for me",
"powered by Matrix": "powered by Matrix", "powered by Matrix": "powered by Matrix",
"This homeserver would like to make sure you are not a robot.": "This homeserver would like to make sure you are not a robot.", "This homeserver would like to make sure you are not a robot.": "This homeserver would like to make sure you are not a robot.",
"Custom Server Options": "Custom Server Options", "Custom Server Options": "Custom Server Options",
@ -1694,6 +1694,7 @@
"Review terms and conditions": "Review terms and conditions", "Review terms and conditions": "Review terms and conditions",
"Old cryptography data detected": "Old cryptography data detected", "Old cryptography data detected": "Old cryptography data detected",
"Data from an older version of Riot has been detected. This will have caused end-to-end cryptography to malfunction in the older version. End-to-end encrypted messages exchanged recently whilst using the older version may not be decryptable in this version. This may also cause messages exchanged with this version to fail. If you experience problems, log out and back in again. To retain message history, export and re-import your keys.": "Data from an older version of Riot has been detected. This will have caused end-to-end cryptography to malfunction in the older version. End-to-end encrypted messages exchanged recently whilst using the older version may not be decryptable in this version. This may also cause messages exchanged with this version to fail. If you experience problems, log out and back in again. To retain message history, export and re-import your keys.", "Data from an older version of Riot has been detected. This will have caused end-to-end cryptography to malfunction in the older version. End-to-end encrypted messages exchanged recently whilst using the older version may not be decryptable in this version. This may also cause messages exchanged with this version to fail. If you experience problems, log out and back in again. To retain message history, export and re-import your keys.": "Data from an older version of Riot has been detected. This will have caused end-to-end cryptography to malfunction in the older version. End-to-end encrypted messages exchanged recently whilst using the older version may not be decryptable in this version. This may also cause messages exchanged with this version to fail. If you experience problems, log out and back in again. To retain message history, export and re-import your keys.",
"Verification Request": "Verification Request",
"Logout": "Logout", "Logout": "Logout",
"%(creator)s created and configured the room.": "%(creator)s created and configured the room.", "%(creator)s created and configured the room.": "%(creator)s created and configured the room.",
"Your Communities": "Your Communities", "Your Communities": "Your Communities",
@ -1759,6 +1760,7 @@
"Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.": "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.", "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.": "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.",
"Tried to load a specific point in this room's timeline, but was unable to find it.": "Tried to load a specific point in this room's timeline, but was unable to find it.", "Tried to load a specific point in this room's timeline, but was unable to find it.": "Tried to load a specific point in this room's timeline, but was unable to find it.",
"Failed to load timeline position": "Failed to load timeline position", "Failed to load timeline position": "Failed to load timeline position",
" (1/%(totalCount)s)": " (1/%(totalCount)s)",
"Guest": "Guest", "Guest": "Guest",
"Your profile": "Your profile", "Your profile": "Your profile",
"Uploading %(filename)s and %(count)s others|other": "Uploading %(filename)s and %(count)s others", "Uploading %(filename)s and %(count)s others|other": "Uploading %(filename)s and %(count)s others",

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