diff --git a/.eslintignore.errorfiles b/.eslintignore.errorfiles index 2ccf83489cc..4830f8235a5 100644 --- a/.eslintignore.errorfiles +++ b/.eslintignore.errorfiles @@ -44,13 +44,11 @@ src/components/views/rooms/MessageComposer.js src/components/views/rooms/PinnedEventTile.js src/components/views/rooms/RoomList.js src/components/views/rooms/RoomPreviewBar.js -src/components/views/rooms/RoomSettings.js src/components/views/rooms/SearchableEntityList.js src/components/views/rooms/SearchBar.js src/components/views/rooms/SearchResultTile.js src/components/views/rooms/TopUnreadMessagesBar.js src/components/views/rooms/UserTile.js -src/components/views/settings/AddPhoneNumber.js src/components/views/settings/ChangeAvatar.js src/components/views/settings/ChangePassword.js src/components/views/settings/DevicesPanel.js diff --git a/res/css/_common.scss b/res/css/_common.scss index 56af1ab5192..aaefb859e4b 100644 --- a/res/css/_common.scss +++ b/res/css/_common.scss @@ -270,6 +270,20 @@ textarea { opacity: 0.7; } +// TODO: Review mx_GeneralButton usage to see if it can use a different class +// These classes were brought in from the old UserSettings and are included here to avoid +// breaking the app. +// Ref: https://github.com/vector-im/riot-web/issues/8420 +.mx_GeneralButton { + @mixin mx_DialogButton; + display: inline; + margin: auto; +} + +.mx_GeneralButton:hover { + @mixin mx_DialogButton_hover; +} + .mx_linkButton { cursor: pointer; color: $accent-color; diff --git a/res/css/_components.scss b/res/css/_components.scss index ee55c000ff5..dc2091b3894 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -21,7 +21,6 @@ @import "./structures/_TagPanel.scss"; @import "./structures/_TopLeftMenuButton.scss"; @import "./structures/_UploadBar.scss"; -@import "./structures/_UserSettings.scss"; @import "./structures/_ViewSource.scss"; @import "./structures/auth/_Login.scss"; @import "./views/auth/_AuthBody.scss"; @@ -42,6 +41,7 @@ @import "./views/context_menus/_StatusMessageContextMenu.scss"; @import "./views/context_menus/_TagTileContextMenu.scss"; @import "./views/context_menus/_TopLeftMenu.scss"; +@import "./views/dialogs/_Analytics.scss"; @import "./views/dialogs/_BugReportDialog.scss"; @import "./views/dialogs/_ChangelogDialog.scss"; @import "./views/dialogs/_ChatCreateOrReuseChatDialog.scss"; @@ -50,6 +50,7 @@ @import "./views/dialogs/_CreateGroupDialog.scss"; @import "./views/dialogs/_CreateRoomDialog.scss"; @import "./views/dialogs/_DeactivateAccountDialog.scss"; +@import "./views/dialogs/_DeviceVerifyDialog.scss"; @import "./views/dialogs/_DevtoolsDialog.scss"; @import "./views/dialogs/_EncryptedEventDialog.scss"; @import "./views/dialogs/_GroupAddressPicker.scss"; @@ -77,6 +78,7 @@ @import "./views/elements/_HexVerify.scss"; @import "./views/elements/_ImageView.scss"; @import "./views/elements/_InlineSpinner.scss"; +@import "./views/elements/_ManageIntegsButton.scss"; @import "./views/elements/_MemberEventListSummary.scss"; @import "./views/elements/_ProgressBar.scss"; @import "./views/elements/_ReplyThread.scss"; @@ -104,6 +106,8 @@ @import "./views/messages/_SenderProfile.scss"; @import "./views/messages/_TextualEvent.scss"; @import "./views/messages/_UnknownBody.scss"; +@import "./views/room_settings/_AliasSettings.scss"; +@import "./views/room_settings/_ColorSettings.scss"; @import "./views/rooms/_AppsDrawer.scss"; @import "./views/rooms/_Autocomplete.scss"; @import "./views/rooms/_AuxPanel.scss"; @@ -125,7 +129,6 @@ @import "./views/rooms/_RoomList.scss"; @import "./views/rooms/_RoomPreviewBar.scss"; @import "./views/rooms/_RoomRecoveryReminder.scss"; -@import "./views/rooms/_RoomSettings.scss"; @import "./views/rooms/_RoomTile.scss"; @import "./views/rooms/_RoomTooltip.scss"; @import "./views/rooms/_RoomUpgradeWarningBar.scss"; diff --git a/res/css/structures/_TabbedView.scss b/res/css/structures/_TabbedView.scss index c87b4d053f1..cbd2870cba7 100644 --- a/res/css/structures/_TabbedView.scss +++ b/res/css/structures/_TabbedView.scss @@ -51,11 +51,6 @@ limitations under the License. color: $tab-label-active-fg-color; } -// TODO: Remove temporary hack alongside "visit old settings" tab -.mx_TabbedView_tabLabel_TEMP_HACK { - background-color: orange; -} - .mx_TabbedView_maskedIcon {; margin-left: 6px; margin-right: 9px; diff --git a/res/css/structures/_UserSettings.scss b/res/css/structures/_UserSettings.scss deleted file mode 100644 index b078a4e242f..00000000000 --- a/res/css/structures/_UserSettings.scss +++ /dev/null @@ -1,269 +0,0 @@ -/* -Copyright 2015, 2016 OpenMarket Ltd -Copyright 2017 Vector Creations Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -.mx_UserSettings { - max-width: 960px; - width: 100%; - margin-left: auto; - margin-right: auto; - - display: flex; - flex-direction: column; -} - -.mx_UserSettings .mx_RoomHeader { - order: 1; - - flex: 0 0 70px; -} - -.mx_UserSettings_body { - order: 2; - - flex: 1 1 0; - - margin-top: -20px; - overflow-y: auto; -} - -.mx_UserSettings h3 { - clear: both; - margin-left: 63px; - text-transform: uppercase; - color: $h3-color; - font-weight: 600; - font-size: 13px; - margin-top: 26px; - margin-bottom: 10px; -} - -.mx_UserSettings_section h3 { - margin-left: 0px; -} - -.mx_UserSettings_spinner { - display: inline-block; - vertical-align: middle; - margin-right: 12px; - width: 32px; - height: 32px; -} - -.mx_UserSettings_button { - @mixin mx_DialogButton; - display: inline; - margin: auto; -} - -.mx_UserSettings_button:hover { - @mixin mx_DialogButton_hover; -} - -.mx_UserSettings_button.danger { - background-color: $warning-color; -} - -.mx_UserSettings_section { - margin-left: 63px; - margin-top: 28px; - margin-bottom: 28px; -} - -.mx_UserSettings_cryptoSection ul { - display: table; -} -.mx_UserSettings_cryptoSection li { - display: table-row; -} -.mx_UserSettings_cryptoSection label, -.mx_UserSettings_cryptoSection span { - display: table-cell; - padding-right: 1em; -} - -.mx_UserSettings_passwordWarning { - /* To move the "Sign out" button out of the way */ - clear: both; - color: $warning-color; - margin-bottom: 5px; -} - -.mx_UserSettings_importExportButtons { - padding-top: 10px; - padding-left: 40px; -} - -.mx_UserSettings_importExportButtons .mx_UserSettings_button { - margin-right: 1em; -} - -.mx_UserSettings_toggle input { - width: 16px; - margin-right: 8px; - margin-bottom: 8px; -} - -.mx_UserSettings_toggle label { - padding-bottom: 21px; -} - -.mx_UserSettings_accountTable -.mx_UserSettings_notifTable -{ - display: table; -} - -.mx_UserSettings_notifTable .mx_Spinner { - position: absolute; -} - -.mx_UserSettings_language { - width: 200px; -} - -.mx_UserSettings_webRtcDevices_dropdown { - width: 50%; -} - -.mx_UserSettings_profileTable -{ - display: table; - float: left; -} - -.mx_UserSettings_profileTableRow -{ - display: table-row; -} - -.mx_UserSettings_profileLabelCell -{ - padding-bottom: 21px; - display: table-cell; - font-weight: bold; - padding-right: 24px; -} - -.mx_UserSettings_profileInputCell { - display: table-cell; - padding-bottom: 21px; - width: 240px; -} - -.mx_UserSettings_profileInputCell input, -.mx_UserSettings_profileInputCell .mx_EditableText -{ - display: inline-block; - border: 0px; - border-bottom: 1px solid $input-underline-color; - padding: 0px; - width: 240px; - color: $input-fg-color; - font-family: $font-family; - font-size: 16px; -} - -.mx_UserSettings_threepidButton { - display: table-cell; - padding-left: 0.5em; - position: relative; - cursor: pointer; -} - -.mx_UserSettings_phoneSection { - display:table; -} - -.mx_UserSettings_phoneCountry { - width: 70px; - display: table-cell; -} - -input.mx_UserSettings_phoneNumberField { - margin-left: 3px; - width: 172px; - border: 1px solid transparent; -} - -.mx_UserSettings_changePasswordButton { - float: right; - margin-right: 32px; - margin-left: 32px; -} - -.mx_UserSettings_logout { - float: right; - margin-right: 32px; - margin-left: 32px; -} - -.mx_UserSettings_avatarPicker { - margin-left: 32px; - margin-right: 32px; - float: right; - cursor: pointer; -} - -.mx_UserSettings_avatarPicker_img .mx_BaseAvatar_image { - object-fit: cover; -} - -.mx_UserSettings_avatarPicker_edit { - text-align: center; - margin-top: 10px; -} - -.mx_UserSettings_avatarPicker_edit img { - cursor: pointer; -} - -.mx_UserSettings_avatarPicker_edit > input { - display: none; -} - -.mx_UserSettings_avatarPicker_imgContainer { - display: inline-block; -} - -.mx_UserSettings_avatarPicker_remove { - display: inline-block; - float: right; - margin-right: -15px; -} - -.mx_UserSettings_advanced_spoiler, -.mx_UserSettings_link { - cursor: pointer; - color: $accent-color; - word-break: break-all; -} - -.mx_UserSettings_analyticsModal table { - margin: 10px 0px; -} - - -// Temp styles to keep the layout moderately usable. Not perfect, but better -// than 30 options being impossible to understand. -.mx_UserSettings .mx_SettingsFlag { - height: 30px; -} - -.mx_UserSettings .mx_SettingsFlag .mx_ToggleSwitch { - float: left; - margin-right: 5px; -} \ No newline at end of file diff --git a/res/css/views/dialogs/_Analytics.scss b/res/css/views/dialogs/_Analytics.scss new file mode 100644 index 00000000000..e403d3b207a --- /dev/null +++ b/res/css/views/dialogs/_Analytics.scss @@ -0,0 +1,19 @@ +/* +Copyright 2019 New Vector Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_AnalyticsModal table { + margin: 10px 0px; +} diff --git a/res/css/views/dialogs/_DeviceVerifyDialog.scss b/res/css/views/dialogs/_DeviceVerifyDialog.scss new file mode 100644 index 00000000000..1997e0c21dd --- /dev/null +++ b/res/css/views/dialogs/_DeviceVerifyDialog.scss @@ -0,0 +1,29 @@ +/* +Copyright 2019 New Vector Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_DeviceVerifyDialog_cryptoSection ul { + display: table; +} + +.mx_DeviceVerifyDialog_cryptoSection li { + display: table-row; +} + +.mx_DeviceVerifyDialog_cryptoSection label, +.mx_DeviceVerifyDialog_cryptoSection span { + display: table-cell; + padding-right: 1em; +} diff --git a/res/css/views/elements/_ManageIntegsButton.scss b/res/css/views/elements/_ManageIntegsButton.scss new file mode 100644 index 00000000000..7c91b9dbde5 --- /dev/null +++ b/res/css/views/elements/_ManageIntegsButton.scss @@ -0,0 +1,53 @@ +/* +Copyright 2019 New Vector Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_ManageIntegsButton_error { + position: relative; + cursor: not-allowed; +} + +.mx_ManageIntegsButton_error img { + position: absolute; + right: -5px; + top: -5px; +} + +.mx_ManageIntegsButton_error { + float: right; +} + +.mx_ManageIntegsButton_error .mx_ManageIntegsButton_errorPopup { + display: none; +} + +.mx_ManageIntegsButton_error:hover .mx_ManageIntegsButton_errorPopup { + display: inline; +} + +.mx_ManageIntegsButton_errorPopup { + position: absolute; + top: 110%; + left: -275%; + width: 550%; + padding: 30%; + font-size: 10pt; + line-height: 1.5em; + border-radius: 5px; + background-color: $accent-color; + color: $accent-fg-color; + text-align: center; + z-index: 1000; +} diff --git a/res/css/views/room_settings/_AliasSettings.scss b/res/css/views/room_settings/_AliasSettings.scss new file mode 100644 index 00000000000..d4ae58e5b00 --- /dev/null +++ b/res/css/views/room_settings/_AliasSettings.scss @@ -0,0 +1,28 @@ +/* +Copyright 2019 New Vector Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_AliasSettings_editable { + border: 0px; + border-bottom: 1px solid $strong-input-border-color; + padding: 0px; + min-width: 240px; +} + +.mx_AliasSettings_editable:focus { + border-bottom: 1px solid $accent-color; + outline: none; + box-shadow: none; +} diff --git a/res/css/views/room_settings/_ColorSettings.scss b/res/css/views/room_settings/_ColorSettings.scss new file mode 100644 index 00000000000..39b087653d1 --- /dev/null +++ b/res/css/views/room_settings/_ColorSettings.scss @@ -0,0 +1,39 @@ +/* +Copyright 2019 New Vector Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_ColorSettings_roomColor { + display: inline-block; + position: relative; + width: 37px; + height: 37px; + border: 1px solid #979797; + margin-right: 13px; + cursor: pointer; +} + +.mx_ColorSettings_roomColor_selected { + position: absolute; + left: 10px; + top: 4px; + cursor: default ! important; +} + +.mx_ColorSettings_roomColorPrimary { + height: 10px; + position: absolute; + bottom: 0px; + width: 100%; +} diff --git a/res/css/views/rooms/_RoomSettings.scss b/res/css/views/rooms/_RoomSettings.scss deleted file mode 100644 index 5ed9168aab9..00000000000 --- a/res/css/views/rooms/_RoomSettings.scss +++ /dev/null @@ -1,260 +0,0 @@ -/* -Copyright 2015, 2016 OpenMarket Ltd -Copyright 2017 Vector Creations Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -.mx_RoomSettings { - margin: 40px; -} - -.mx_RoomSettings_upgradeButton, -.mx_RoomSettings_leaveButton, -.mx_RoomSettings_unbanButton { - @mixin mx_DialogButton; - position: relative; - margin-right: 8px; -} - -.mx_RoomSettings_devtoolsButton { - @mixin mx_DialogButton; - position: relative; - padding: 4px 1.5em; - margin-top: 8px; -} - -.mx_RoomSettings_upgradeButton, -.mx_RoomSettings_leaveButton:hover, -.mx_RoomSettings_unbanButton:hover { - @mixin mx_DialogButton_hover; -} - -.mx_RoomSettings_upgradeButton.danger { - @mixin mx_DialogButton_danger; -} - -.mx_RoomSettings_integrationsButton_error { - position: relative; - cursor: not-allowed; -} -.mx_RoomSettings_integrationsButton_error img { - position: absolute; - right: -5px; - top: -5px; -} -.mx_RoomSettings_leaveButton, -.mx_RoomSettings_integrationsButton_error { - float: right; -} -.mx_RoomSettings_integrationsButton_error .mx_RoomSettings_integrationsButton_errorPopup { - display: none; -} -.mx_RoomSettings_integrationsButton_error:hover .mx_RoomSettings_integrationsButton_errorPopup { - display: inline; -} -.mx_RoomSettings_integrationsButton_errorPopup { - position: absolute; - top: 110%; - left: -275%; - width: 550%; - padding: 30%; - font-size: 10pt; - line-height: 1.5em; - border-radius: 5px; - background-color: $accent-color; - color: $accent-fg-color; - text-align: center; - z-index: 1000; -} -.mx_RoomSettings_unbanButton { - display: inline; -} - -.mx_RoomSettings_e2eIcon { - padding-left: 4px; - padding-right: 7px; -} - -.mx_RoomSettings_leaveButton { - margin-right: 32px; -} - -.mx_RoomSettings_powerLevels { - display: table; -} - -.mx_RoomSettings_powerLevel { - display: table-row; -} - -.mx_RoomSettings_powerLevelKey, -.mx_RoomSettings_powerLevel .mx_PowerSelector { - display: table-cell; - padding-bottom: 5px; -} - -.mx_RoomSettings_powerLevelKey { - text-align: right; - padding-right: 0.3em; -} - -.mx_RoomSettings h3 { - text-transform: uppercase; - color: $h3-color; - font-weight: 600; - font-size: 13px; - margin-top: 36px; - margin-bottom: 10px; -} - -.mx_RoomSettings .mx_RoomSettings_toggles label { - margin-bottom: 8px; - display: block; -} - -.mx_RoomSettings .mx_RoomSettings_toggles input[type="checkbox"], -.mx_RoomSettings .mx_RoomSettings_toggles input[type="radio"] { - margin-right: 7px; -} - -.mx_RoomSettings .mx_RoomSettings_tags input[type="checkbox"] { - margin-left: 1em; - margin-right: 7px; -} - -.mx_RoomSettings .mx_RoomSettings_tags { - margin-bottom: 8px; -} - -.mx_RoomSettings .mx_RoomSettings_roomColor { - display: inline-block; - position: relative; - width: 37px; - height: 37px; - border: 1px solid #979797; - margin-right: 13px; - cursor: pointer; -} - -.mx_RoomSettings .mx_RoomSettings_roomColor_selected { - position: absolute; - left: 10px; - top: 4px; - cursor: default ! important; -} - -.mx_RoomSettings .mx_RoomSettings_roomColorPrimary { - height: 10px; - position: absolute; - bottom: 0px; - width: 100%; -} - -.mx_RoomSettings .mx_RoomSettings_aliasLabel { - margin-bottom: 8px; -} - -.mx_RoomSettings .mx_RoomSettings_aliasesTable { - margin-top: 12px; - margin-bottom: 0px; - margin-left: 56px; - display: table; -} - -.mx_RoomSettings .mx_RoomSettings_aliasesTableRow { - display: table-row; - margin-bottom: 16px; -} - -.mx_RoomSettings .mx_RoomSettings_alias { - max-width: 400px; - margin-bottom: 16px; - /* - commented out so margin applies - display: table-cell; */ -} - -.mx_RoomSettings .mx_RoomSettings_addAlias, -.mx_RoomSettings .mx_RoomSettings_deleteAlias { - display: table-cell; - padding-left: 0.5em; - position: relative; - cursor: pointer; -} - -.mx_RoomSettings .mx_RoomSettings_addAlias img, -.mx_RoomSettings .mx_RoomSettings_deleteAlias img { - visibility: hidden; -} - -.mx_RoomSettings .mx_RoomSettings_aliasesTableRow:hover .mx_RoomSettings_addAlias img, -.mx_RoomSettings .mx_RoomSettings_aliasesTableRow:hover .mx_RoomSettings_deleteAlias img { - visibility: visible; -} - -.mx_RoomSettings_warning { - color: $warning-color; - font-weight: bold; - margin-top: 8px; - margin-bottom: 8px; -} - -.mx_RoomSettings_editable { - border: 0px; - border-bottom: 1px solid $strong-input-border-color; - padding: 0px; - min-width: 240px; -} - -.mx_RoomSettings_editable:focus { - border-bottom: 1px solid $accent-color; - outline: none; - box-shadow: none; -} - -.mx_RoomSettings_deleteAlias, -.mx_RoomSettings_addAlias { - display: table-cell; - visibility: visible; -} - -.mx_RoomSettings_deleteAlias:hover, -.mx_RoomSettings_addAlias:hover { - visibility: visible; -} - -.mx_RoomSettings_aliasPlaceholder { - color: $settings-grey-fg-color; -} - -.mx_RoomSettings_buttons { - text-align: right; - margin-bottom: 16px; -} - -.mx_RoomSettings_button { - display: inline; - border: 0px; - height: 36px; - border-radius: 36px; - font-weight: 400; - font-size: 15px; - color: $accent-fg-color; - background-color: $accent-color; - width: auto; - margin: auto; - padding: 6px; - padding-left: 1em; - padding-right: 1em; -} diff --git a/res/css/views/settings/_Notifications.scss b/res/css/views/settings/_Notifications.scss index 171b4c2e05a..d6c0b5dbeb4 100644 --- a/res/css/views/settings/_Notifications.scss +++ b/res/css/views/settings/_Notifications.scss @@ -59,10 +59,15 @@ limitations under the License. color: $accent-color; } -.mx_UserSettings_devicesTable td { +.mx_UserNotifSettings_devicesTable td { padding-left: 20px; padding-right: 20px; } -.mx_UserSettings_devicesTable_nodevices { - font-style: italic; + +.mx_UserNotifSettings_notifTable { + display: table; +} + +.mx_UserNotifSettings_notifTable .mx_Spinner { + position: absolute; } diff --git a/src/Analytics.js b/src/Analytics.js index 3a8c5d6e10b..44b85e48425 100644 --- a/src/Analytics.js +++ b/src/Analytics.js @@ -267,7 +267,7 @@ class Analytics { const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog'); Modal.createTrackedDialog('Analytics Details', '', ErrorDialog, { title: _t('Analytics'), - description:
+ description:
{ _t('The information being sent to us to help make Riot.im better includes:') }
diff --git a/src/PageTypes.js b/src/PageTypes.js index 60111723fb8..09e0eadbd7a 100644 --- a/src/PageTypes.js +++ b/src/PageTypes.js @@ -19,7 +19,6 @@ limitations under the License. export default { HomePage: "home_page", RoomView: "room_view", - UserSettings: "user_settings", RoomDirectory: "room_directory", UserView: "user_view", GroupView: "group_view", diff --git a/src/UserSettingsStore.js b/src/UserSettingsStore.js index b40d0529a22..c9ba1956ad4 100644 --- a/src/UserSettingsStore.js +++ b/src/UserSettingsStore.js @@ -1,6 +1,6 @@ /* Copyright 2015, 2016 OpenMarket Ltd -Copyright 2017 New Vector Ltd +Copyright 2017, 2019 New Vector Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,47 +15,11 @@ See the License for the specific language governing permissions and limitations under the License. */ -import Promise from 'bluebird'; import MatrixClientPeg from './MatrixClientPeg'; -/* - * TODO: Make things use this. This is all WIP - see UserSettings.js for usage. - */ +// TODO: Decommission. +// Ref: https://github.com/vector-im/riot-web/issues/8424 export default { - loadProfileInfo: function() { - const cli = MatrixClientPeg.get(); - return cli.getProfileInfo(cli.credentials.userId); - }, - - saveDisplayName: function(newDisplayname) { - return MatrixClientPeg.get().setDisplayName(newDisplayname); - }, - - loadThreePids: function() { - if (MatrixClientPeg.get().isGuest()) { - return Promise.resolve({ - threepids: [], - }); // guests can't poke 3pid endpoint - } - return MatrixClientPeg.get().getThreePids(); - }, - - saveThreePids: function(threePids) { - // TODO - }, - - changePassword: function(oldPassword, newPassword) { - const cli = MatrixClientPeg.get(); - - const authDict = { - type: 'm.login.password', - user: cli.credentials.userId, - password: oldPassword, - }; - - return cli.setPassword(authDict, newPassword); - }, - /* * Returns the email pusher (pusher of type 'email') for a given * email address. Email pushers all have the same app ID, so since @@ -74,10 +38,6 @@ export default { return undefined; }, - hasEmailPusher: function(pushers, address) { - return this.getEmailPusher(pushers, address) !== undefined; - }, - addEmailPusher: function(address, data) { return MatrixClientPeg.get().setPusher({ kind: 'email', diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js index 3024677388d..eac4422de0c 100644 --- a/src/components/structures/LoggedInView.js +++ b/src/components/structures/LoggedInView.js @@ -57,7 +57,6 @@ const LoggedInView = React.createClass({ matrixClient: PropTypes.instanceOf(Matrix.MatrixClient).isRequired, page_type: PropTypes.string.isRequired, onRoomCreated: PropTypes.func, - onUserSettingsClose: PropTypes.func, // Called with the credentials of a registered user (if they were a ROU that // transitioned to PWLU) @@ -421,7 +420,6 @@ const LoggedInView = React.createClass({ render: function() { const LeftPanel = sdk.getComponent('structures.LeftPanel'); const RoomView = sdk.getComponent('structures.RoomView'); - const UserSettings = sdk.getComponent('structures.UserSettings'); const HomePage = sdk.getComponent('structures.HomePage'); const GroupView = sdk.getComponent('structures.GroupView'); const MyGroups = sdk.getComponent('structures.MyGroups'); @@ -451,13 +449,6 @@ const LoggedInView = React.createClass({ />; break; - case PageTypes.UserSettings: - pageElement = ; - break; - case PageTypes.MyGroups: pageElement = ; break; diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 24db0dc9958..bb90b2aeb5c 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -136,10 +136,6 @@ export default React.createClass({ appConfig: PropTypes.object, }, - AuxPanel: { - RoomSettings: "room_settings", - }, - getChildContext: function() { return { appConfig: this.props.config, @@ -572,40 +568,16 @@ export default React.createClass({ this._viewIndexedRoom(payload.roomIndex); break; case 'view_user_settings': { - if (true || SettingsStore.isFeatureEnabled("feature_tabbed_settings")) { - const UserSettingsDialog = sdk.getComponent("dialogs.UserSettingsDialog"); - Modal.createTrackedDialog('User settings', '', UserSettingsDialog, {}, 'mx_SettingsDialog'); - } else { - this._setPage(PageTypes.UserSettings); - this.notifyNewScreen('settings'); + const UserSettingsDialog = sdk.getComponent("dialogs.UserSettingsDialog"); + Modal.createTrackedDialog('User settings', '', UserSettingsDialog, {}, 'mx_SettingsDialog'); + + // View the home page if we need something to look at + if (!this.state.currentGroupId && !this.state.currentRoomId) { + this._setPage(PageTypes.HomePage); + this.notifyNewScreen('home'); } break; } - case 'view_old_user_settings': - this._setPage(PageTypes.UserSettings); - this.notifyNewScreen('settings'); - break; - case 'close_settings': - this.setState({ - leftDisabled: false, - rightDisabled: false, - middleDisabled: false, - }); - if (this.state.page_type === PageTypes.UserSettings) { - // We do this to get setPage and notifyNewScreen - if (this.state.currentRoomId) { - this._viewRoom({ - room_id: this.state.currentRoomId, - }); - } else if (this.state.currentGroupId) { - this._viewGroup({ - group_id: this.state.currentGroupId, - }); - } else { - this._viewHome(); - } - } - break; case 'view_create_room': this._createRoom(); break; @@ -1063,7 +1035,6 @@ export default React.createClass({ modal.close(); if (this.state.currentRoomId === roomId) { dis.dispatch({action: 'view_next_room'}); - dis.dispatch({action: 'close_room_settings'}); } }, (err) => { modal.close(); diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index c01b01f0b65..4778f1a76a0 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -119,8 +119,6 @@ module.exports = React.createClass({ isInitialEventHighlighted: null, forwardingEvent: null, - editingRoomSettings: false, - uploadingRoomSettings: false, numUnreadMessages: 0, draggingFile: false, searching: false, @@ -229,11 +227,8 @@ module.exports = React.createClass({ forwardingEvent: RoomViewStore.getForwardingEvent(), shouldPeek: RoomViewStore.shouldPeek(), showingPinned: SettingsStore.getValue("PinnedEvents.isOpen", RoomViewStore.getRoomId()), - editingRoomSettings: RoomViewStore.isEditingSettings(), }; - if (this.state.editingRoomSettings && !newState.editingRoomSettings) dis.dispatch({action: 'focus_composer'}); - // Temporary logging to diagnose https://github.com/vector-im/riot-web/issues/4307 console.log( 'RVS update:', @@ -1117,7 +1112,7 @@ module.exports = React.createClass({ // favour longer (more specific) terms first highlights = highlights.sort(function(a, b) { return b.length - a.length; -}); + }); self.setState({ searchHighlights: highlights, @@ -1231,50 +1226,9 @@ module.exports = React.createClass({ dis.dispatch({ action: 'open_room_settings' }); }, - onSettingsSaveClick: function() { - if (!this.refs.room_settings) return; - - this.setState({ - uploadingRoomSettings: true, - }); - - const newName = this.refs.header.getEditedName(); - if (newName !== undefined) { - this.refs.room_settings.setName(newName); - } - const newTopic = this.refs.header.getEditedTopic(); - if (newTopic !== undefined) { - this.refs.room_settings.setTopic(newTopic); - } - - this.refs.room_settings.save().then((results) => { - const fails = results.filter(function(result) { return result.state !== "fulfilled"; }); - console.log("Settings saved with %s errors", fails.length); - if (fails.length) { - fails.forEach(function(result) { - console.error(result.reason); - }); - const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); - Modal.createTrackedDialog('Failed to save room settings', '', ErrorDialog, { - title: _t("Failed to save settings"), - description: fails.map(function(result) { return result.reason; }).join("\n"), - }); - // still editing room settings - } else { - dis.dispatch({ action: 'close_settings' }); - } - }).finally(() => { - this.setState({ - uploadingRoomSettings: false, - }); - dis.dispatch({ action: 'close_settings' }); - }).done(); - }, - onCancelClick: function() { console.log("updateTint from onCancelClick"); this.updateTint(); - dis.dispatch({ action: 'close_settings' }); if (this.state.forwardingEvent) { dis.dispatch({ action: 'forward_event', @@ -1432,7 +1386,7 @@ module.exports = React.createClass({ (83 + // height of RoomHeader 36 + // height of the status area 72 + // minimum height of the message compmoser - (this.state.editingRoomSettings ? (window.innerHeight * 0.3) : 120)); // amount of desired scrollback + 120); // amount of desired scrollback // XXX: this is a bit of a hack and might possibly cause the video to push out the page anyway // but it's better than the video going missing entirely @@ -1532,7 +1486,6 @@ module.exports = React.createClass({ const RoomHeader = sdk.getComponent('rooms.RoomHeader'); const MessageComposer = sdk.getComponent('rooms.MessageComposer'); const ForwardMessage = sdk.getComponent("rooms.ForwardMessage"); - const RoomSettings = sdk.getComponent("rooms.RoomSettings"); const AuxPanel = sdk.getComponent("rooms.AuxPanel"); const SearchBar = sdk.getComponent("rooms.SearchBar"); const PinnedEventsPanel = sdk.getComponent("rooms.PinnedEventsPanel"); @@ -1690,11 +1643,7 @@ module.exports = React.createClass({ let aux = null; let hideCancel = false; - if (this.state.editingRoomSettings) { - aux = ; - } else if (this.state.uploadingRoomSettings) { - aux = ; - } else if (this.state.forwardingEvent !== null) { + if (this.state.forwardingEvent !== null) { aux = ; } else if (this.state.searching) { hideCancel = true; // has own cancel @@ -1736,7 +1685,7 @@ module.exports = React.createClass({ const auxPanel = ( + hideAppsDrawer={false} > { aux } ); @@ -1904,14 +1853,11 @@ module.exports = React.createClass({
'; - -// Simple method to help prettify GH Release Tags and Commit Hashes. -const semVerRegex = /^v?(\d+\.\d+\.\d+(?:-rc.+)?)(?:-(?:\d+-g)?([0-9a-fA-F]+))?(?:-dirty)?$/i; -const gHVersionLabel = function(repo, token='') { - const match = token.match(semVerRegex); - let url; - if (match && match[1]) { // basic semVer string possibly with commit hash - url = (match.length > 1 && match[2]) - ? `https://github.com/${repo}/commit/${match[2]}` - : `https://github.com/${repo}/releases/tag/v${match[1]}`; - } else { - url = `https://github.com/${repo}/commit/${token.split('-')[0]}`; - } - return { token }; -}; - -// Enumerate some simple 'flip a bit' UI settings (if any). The strings provided here -// must be settings defined in SettingsStore. -const SIMPLE_SETTINGS = [ - { id: "urlPreviewsEnabled" }, - { id: "autoplayGifsAndVideos" }, - { id: "alwaysShowEncryptionIcons" }, - { id: "showRoomRecoveryReminder" }, - { id: "showReadReceipts" }, - { id: "sendTypingNotifications" }, - { id: "alwaysShowTimestamps" }, - { id: "showTwelveHourTimestamps" }, - { id: "showJoinLeaves" }, - { id: "showAvatarChanges" }, - { id: "showDisplaynameChanges" }, - { id: "useCompactLayout" }, - { id: "showRedactions" }, - { id: "enableSyntaxHighlightLanguageDetection" }, - { id: "MessageComposerInput.autoReplaceEmoji" }, - { id: "MessageComposerInput.suggestEmoji" }, - { id: "Pill.shouldShowPillAvatar" }, - { id: "TextualBody.enableBigEmoji" }, - { id: "VideoView.flipVideoHorizontally" }, - { id: "TagPanel.enableTagPanel" }, - { id: "enableWidgetScreenshots" }, - { id: "pinMentionedRooms" }, - { id: "pinUnreadRooms" }, - { id: "showDeveloperTools" }, - { id: "promptBeforeInviteUnknownUsers" }, -]; - -// These settings must be defined in SettingsStore -const ANALYTICS_SETTINGS = [ - { - id: 'analyticsOptIn', - fn: function(checked) { - checked ? Analytics.enable() : Analytics.disable(); - }, - }, -]; - -// These settings must be defined in SettingsStore -const WEBRTC_SETTINGS = [ - { - id: 'webRtcForcePeerToPeer', - fn: (val) => { - MatrixClientPeg.get().setForceTURN(!val); - }, - }, -]; - -// These settings must be defined in SettingsStore -const CRYPTO_SETTINGS = [ - { - id: 'blacklistUnverifiedDevices', - fn: function(checked) { - MatrixClientPeg.get().setGlobalBlacklistUnverifiedDevices(checked); - }, - }, -]; - -// Enumerate the available themes, with a nice human text label. -// 'label' is how we describe it in the UI. -// 'value' is the value for the theme setting -// -// XXX: Ideally we would have a theme manifest or something and they'd be nicely -// packaged up in a single directory, and/or located at the application layer. -// But for now for expedience we just hardcode them here. -const THEMES = [ - { label: _td('Light theme'), value: 'light' }, - { label: _td('Dark theme'), value: 'dark' }, - { label: _td('2018 theme'), value: 'dharma' }, - { label: _td('Status.im theme'), value: 'status' }, -]; - -const IgnoredUser = React.createClass({ - propTypes: { - userId: PropTypes.string.isRequired, - onUnignored: PropTypes.func.isRequired, - }, - - _onUnignoreClick: function() { - const ignoredUsers = MatrixClientPeg.get().getIgnoredUsers(); - const index = ignoredUsers.indexOf(this.props.userId); - if (index !== -1) { - ignoredUsers.splice(index, 1); - MatrixClientPeg.get().setIgnoredUsers(ignoredUsers) - .then(() => this.props.onUnignored(this.props.userId)); - } else this.props.onUnignored(this.props.userId); - }, - - render: function() { - return ( -
  • - - { _t("Unignore") } - - { this.props.userId } -
  • - ); - }, -}); - -module.exports = React.createClass({ - displayName: 'UserSettings', - - propTypes: { - onClose: PropTypes.func, - // The brand string given when creating email pushers - brand: PropTypes.string, - }, - - getDefaultProps: function() { - return { - onClose: function() {}, - }; - }, - - getInitialState: function() { - return { - avatarUrl: null, - threepids: [], - phase: "UserSettings.LOADING", // LOADING, DISPLAY - email_add_pending: false, - vectorVersion: undefined, - canSelfUpdate: null, - rejectingInvites: false, - mediaDevices: null, - ignoredUsers: [], - autoLaunchEnabled: null, - }; - }, - - componentWillMount: function() { - this._unmounted = false; - this._addThreepid = null; - - if (PlatformPeg.get()) { - Promise.resolve().then(() => { - return PlatformPeg.get().getAppVersion(); - }).done((appVersion) => { - if (this._unmounted) return; - this.setState({ - vectorVersion: appVersion, - }); - }, (e) => { - console.log("Failed to fetch app version", e); - }); - - PlatformPeg.get().canSelfUpdate().then((canUpdate) => { - if (this._unmounted) return; - this.setState({ - canSelfUpdate: canUpdate, - }); - }); - } - - this._refreshMediaDevices(); - this._refreshIgnoredUsers(); - - // Bulk rejecting invites: - // /sync won't have had time to return when UserSettings re-renders from state changes, so getRooms() - // will still return rooms with invites. To get around this, add a listener for - // membership updates and kick the UI. - MatrixClientPeg.get().on("RoomMember.membership", this._onInviteStateChange); - - dis.dispatch({ - action: 'panel_disable', - sideDisabled: true, - middleDisabled: true, - }); - this._refreshFromServer(); - - if (PlatformPeg.get().supportsAutoLaunch()) { - PlatformPeg.get().getAutoLaunchEnabled().then(enabled => { - this.setState({ - autoLaunchEnabled: enabled, - }); - }); - } - - this.setState({ - language: languageHandler.getCurrentLanguage(), - }); - - this._sessionStore = sessionStore; - this._sessionStoreToken = this._sessionStore.addListener( - this._setStateFromSessionStore, - ); - this._setStateFromSessionStore(); - }, - - componentDidMount: function() { - this.dispatcherRef = dis.register(this.onAction); - this._me = MatrixClientPeg.get().credentials.userId; - }, - - componentWillUnmount: function() { - this._unmounted = true; - dis.dispatch({ - action: 'panel_disable', - sideDisabled: false, - middleDisabled: false, - }); - dis.unregister(this.dispatcherRef); - const cli = MatrixClientPeg.get(); - if (cli) { - cli.removeListener("RoomMember.membership", this._onInviteStateChange); - } - }, - - // `UserSettings` assumes that the client peg will not be null, so give it some - // sort of assurance here by only allowing a re-render if the client is truthy. - // - // This is required because `UserSettings` maintains its own state and if this state - // updates (e.g. during _setStateFromSessionStore) after the client peg has been made - // null (during logout), then it will attempt to re-render and throw errors. - shouldComponentUpdate: function() { - return Boolean(MatrixClientPeg.get()); - }, - - _setStateFromSessionStore: function() { - this.setState({ - userHasGeneratedPassword: Boolean(this._sessionStore.getCachedPassword()), - }); - }, - - _refreshMediaDevices: function(stream) { - if (stream) { - // kill stream so that we don't leave it lingering around with webcam enabled etc - // as here we called gUM to ask user for permission to their device names only - stream.getTracks().forEach((track) => track.stop()); - } - - Promise.resolve().then(() => { - return CallMediaHandler.getDevices(); - }).then((mediaDevices) => { - // console.log("got mediaDevices", mediaDevices, this._unmounted); - if (this._unmounted) return; - this.setState({ - mediaDevices, - activeAudioOutput: SettingsStore.getValueAt(SettingLevel.DEVICE, 'webrtc_audiooutput'), - activeAudioInput: SettingsStore.getValueAt(SettingLevel.DEVICE, 'webrtc_audioinput'), - activeVideoInput: SettingsStore.getValueAt(SettingLevel.DEVICE, 'webrtc_videoinput'), - }); - }); - }, - - _refreshFromServer: function() { - const self = this; - Promise.all([ - UserSettingsStore.loadProfileInfo(), UserSettingsStore.loadThreePids(), - ]).done(function(resps) { - self.setState({ - avatarUrl: resps[0].avatar_url, - threepids: resps[1].threepids, - phase: "UserSettings.DISPLAY", - }); - }, function(error) { - const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); - console.error("Failed to load user settings: " + error); - Modal.createTrackedDialog('Can\'t load user settings', '', ErrorDialog, { - title: _t("Can't load user settings"), - description: ((error && error.message) ? error.message : _t("Server may be unavailable or overloaded")), - }); - }); - }, - - _refreshIgnoredUsers: function(userIdUnignored=null) { - const users = MatrixClientPeg.get().getIgnoredUsers(); - if (userIdUnignored) { - const index = users.indexOf(userIdUnignored); - if (index !== -1) users.splice(index, 1); - } - this.setState({ - ignoredUsers: users, - }); - }, - - onAction: function(payload) { - if (payload.action === "notifier_enabled") { - this.forceUpdate(); - } else if (payload.action === "ignore_state_changed") { - this._refreshIgnoredUsers(); - } - }, - - onAvatarPickerClick: function(ev) { - if (this.refs.file_label) { - this.refs.file_label.click(); - } - }, - - onAvatarSelected: function(ev) { - const self = this; - const changeAvatar = this.refs.changeAvatar; - if (!changeAvatar) { - console.error("No ChangeAvatar found to upload image to!"); - return; - } - changeAvatar.onFileSelected(ev).done(function() { - // dunno if the avatar changed, re-check it. - self._refreshFromServer(); - }, function(err) { - // const errMsg = (typeof err === "string") ? err : (err.error || ""); - console.error("Failed to set avatar: " + err); - const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); - Modal.createTrackedDialog('Failed to set avatar', '', ErrorDialog, { - title: _t("Failed to set avatar."), - description: ((err && err.message) ? err.message : _t("Operation failed")), - }); - }); - }, - - onAvatarRemoveClick: function() { - MatrixClientPeg.get().setAvatarUrl(null); - this.setState({avatarUrl: null}); // the avatar update will complete async for us - }, - - onLogoutClicked: function(ev) { - const LogoutDialog = sdk.getComponent("dialogs.LogoutDialog"); - Modal.createTrackedDialog('Logout E2E Export', '', LogoutDialog); - }, - - onPasswordChangeError: function(err) { - let errMsg = err.error || ""; - if (err.httpStatus === 403) { - errMsg = _t("Failed to change password. Is your password correct?"); - } else if (err.httpStatus) { - errMsg += ` (HTTP status ${err.httpStatus})`; - } - const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); - console.error("Failed to change password: " + errMsg); - Modal.createTrackedDialog('Failed to change password', '', ErrorDialog, { - title: _t("Error"), - description: errMsg, - }); - }, - - onPasswordChanged: function() { - const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); - Modal.createTrackedDialog('Password changed', '', ErrorDialog, { - title: _t("Success"), - description: _t( - "Your password was successfully changed. You will not receive " + - "push notifications on other devices until you log back in to them", - ) + ".", - }); - }, - - _onAddEmailEditFinished: function(value, shouldSubmit) { - if (!shouldSubmit) return; - this._addEmail(); - }, - - _addEmail: function() { - const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); - const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); - - const emailAddress = this.refs.add_email_input.value; - if (!Email.looksValid(emailAddress)) { - Modal.createTrackedDialog('Invalid email address', '', ErrorDialog, { - title: _t("Invalid Email Address"), - description: _t("This doesn't appear to be a valid email address"), - }); - return; - } - this._addThreepid = new AddThreepid(); - // we always bind emails when registering, so let's do the - // same here. - this._addThreepid.addEmailAddress(emailAddress, true).done(() => { - Modal.createTrackedDialog('Verification Pending', '', QuestionDialog, { - title: _t("Verification Pending"), - description: _t( - "Please check your email and click on the link it contains. Once this " + - "is done, click continue.", - ), - button: _t('Continue'), - onFinished: this.onEmailDialogFinished, - }); - }, (err) => { - this.setState({email_add_pending: false}); - console.error("Unable to add email address " + emailAddress + " " + err); - Modal.createTrackedDialog('Unable to add email address', '', ErrorDialog, { - title: _t("Unable to add email address"), - description: ((err && err.message) ? err.message : _t("Operation failed")), - }); - }); - ReactDOM.findDOMNode(this.refs.add_email_input).blur(); - this.setState({email_add_pending: true}); - }, - - onRemoveThreepidClicked: function(threepid) { - const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); - Modal.createTrackedDialog('Remove 3pid', '', QuestionDialog, { - title: _t("Remove Contact Information?"), - description: _t("Remove %(threePid)s?", { threePid: threepid.address }), - button: _t('Remove'), - onFinished: (submit) => { - if (submit) { - this.setState({ - phase: "UserSettings.LOADING", - }); - MatrixClientPeg.get().deleteThreePid(threepid.medium, threepid.address).then(() => { - return this._refreshFromServer(); - }).catch((err) => { - const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); - console.error("Unable to remove contact information: " + err); - Modal.createTrackedDialog('Remove 3pid failed', '', ErrorDialog, { - title: _t("Unable to remove contact information"), - description: ((err && err.message) ? err.message : _t("Operation failed")), - }); - }).done(); - } - }, - }); - }, - - onEmailDialogFinished: function(ok) { - if (ok) { - this.verifyEmailAddress(); - } else { - this.setState({email_add_pending: false}); - } - }, - - verifyEmailAddress: function() { - this._addThreepid.checkEmailLinkClicked().done(() => { - this._addThreepid = null; - this.setState({ - phase: "UserSettings.LOADING", - }); - this._refreshFromServer(); - this.setState({email_add_pending: false}); - }, (err) => { - this.setState({email_add_pending: false}); - if (err.errcode == 'M_THREEPID_AUTH_FAILED') { - const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); - const message = _t("Unable to verify email address.") + " " + - _t("Please check your email and click on the link it contains. Once this is done, click continue."); - Modal.createTrackedDialog('Verification Pending', '', QuestionDialog, { - title: _t("Verification Pending"), - description: message, - button: _t('Continue'), - onFinished: this.onEmailDialogFinished, - }); - } else { - const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); - console.error("Unable to verify email address: " + err); - Modal.createTrackedDialog('Unable to verify email address', '', ErrorDialog, { - title: _t("Unable to verify email address."), - description: ((err && err.message) ? err.message : _t("Operation failed")), - }); - } - }); - }, - - _onDeactivateAccountClicked: function() { - const DeactivateAccountDialog = sdk.getComponent("dialogs.DeactivateAccountDialog"); - Modal.createTrackedDialog('Deactivate Account', '', DeactivateAccountDialog, {}); - }, - - _onBugReportClicked: function() { - const BugReportDialog = sdk.getComponent("dialogs.BugReportDialog"); - if (!BugReportDialog) { - return; - } - Modal.createTrackedDialog('Bug Report Dialog', '', BugReportDialog, {}); - }, - - _onClearCacheClicked: function() { - if (!PlatformPeg.get()) return; - - MatrixClientPeg.get().stopClient(); - MatrixClientPeg.get().store.deleteAllData().done(() => { - PlatformPeg.get().reload(); - }); - }, - - _onInviteStateChange: function(event, member, oldMembership) { - if (member.userId === this._me && oldMembership === "invite") { - this.forceUpdate(); - } - }, - - _onRejectAllInvitesClicked: function(rooms, ev) { - this.setState({ - rejectingInvites: true, - }); - // reject the invites - const promises = rooms.map((room) => { - return MatrixClientPeg.get().leave(room.roomId).catch((e) => { - // purposefully drop errors to the floor: we'll just have a non-zero number on the UI - // after trying to reject all the invites. - }); - }); - Promise.all(promises).then(() => { - this.setState({ - rejectingInvites: false, - }); - }); - }, - - _onExportE2eKeysClicked: function() { - Modal.createTrackedDialogAsync('Export E2E Keys', '', - import('../../async-components/views/dialogs/ExportE2eKeysDialog'), - { - matrixClient: MatrixClientPeg.get(), - }, - ); - }, - - _onImportE2eKeysClicked: function() { - Modal.createTrackedDialogAsync('Import E2E Keys', '', - import('../../async-components/views/dialogs/ImportE2eKeysDialog'), - { - matrixClient: MatrixClientPeg.get(), - }, - ); - }, - - _renderGroupSettings: function() { - const GroupUserSettings = sdk.getComponent('groups.GroupUserSettings'); - return ; - }, - - onLanguageChange: function(newLang) { - if (this.state.language !== newLang) { - SettingsStore.setValue("language", null, SettingLevel.DEVICE, newLang); - this.setState({ - language: newLang, - }); - PlatformPeg.get().reload(); - } - }, - - _renderLanguageSetting: function() { - const LanguageDropdown = sdk.getComponent('views.elements.LanguageDropdown'); - return
    - - -
    ; - }, - - _renderUserInterfaceSettings: function() { - // TODO: this ought to be a separate component so that we don't need - // to rebind the onChange each time we render - const onChange = (e) => - SettingsStore.setValue("autocompleteDelay", null, SettingLevel.DEVICE, e.target.value); - // HACK: Lack of translations for themes header. We're removing this view in the very near future, - // and the header is really only there to maintain some semblance of the UX the section once was. - return ( -
    -

    { _t("User Interface") }

    -
    - { SIMPLE_SETTINGS.map( this._renderAccountSetting ) } -
    Themes
    - { THEMES.map( this._renderThemeOption ) } - - - - - - - -
    { _t('Autocomplete Delay (ms):') } - -
    - { this._renderLanguageSetting() } -
    -
    - ); - }, - - _renderAccountSetting: function(setting) { - const SettingsFlag = sdk.getComponent("elements.SettingsFlag"); - return ( -
    - -
    - ); - }, - - _renderThemeOption: function(setting) { - // HACK: Temporary disablement of theme selection. - // We don't support changing themes on experimental anyways, and radio groups aren't - // a thing anymore for setting flags. We're also dropping this view in the very near - // future, so just replace the theme selection with placeholder text. - const currentTheme = SettingsStore.getValue("theme"); - return
    {_t(setting.label)} {currentTheme === setting.value ? '(current)' : null}
    ; - }, - - _renderCryptoInfo: function() { - const client = MatrixClientPeg.get(); - const deviceId = client.deviceId; - let identityKey = client.getDeviceEd25519Key(); - if (!identityKey) { - identityKey = _t(""); - } else { - identityKey = FormattingUtils.formatCryptoKey(identityKey); - } - - let importExportButtons = null; - - if (client.isCryptoEnabled) { - importExportButtons = ( -
    - - { _t("Export E2E room keys") } - - - { _t("Import E2E room keys") } - -
    - ); - } - - let keyBackupSection; - if (SettingsStore.isFeatureEnabled("feature_keybackup")) { - const KeyBackupPanel = sdk.getComponent('views.settings.KeyBackupPanel'); - keyBackupSection =
    -

    { _t("Key Backup") }

    - -
    ; - } - - return ( -
    -

    { _t("Cryptography") }

    -
    -
      -
    • - { deviceId }
    • -
    • - { identityKey }
    • -
    - { importExportButtons } -
    -
    - { CRYPTO_SETTINGS.map( this._renderDeviceSetting ) } -
    - {keyBackupSection} -
    - ); - }, - - _renderIgnoredUsers: function() { - if (this.state.ignoredUsers.length > 0) { - const updateHandler = this._refreshIgnoredUsers; - return ( -
    -

    { _t("Ignored Users") }

    -
    -
      - { this.state.ignoredUsers.map(function(userId) { - return (); - }) } -
    -
    -
    - ); - } else return (
    ); - }, - - _renderDeviceSetting: function(setting) { - const SettingsFlag = sdk.getComponent("elements.SettingsFlag"); - return ( -
    - -
    - ); - }, - - _renderDevicesPanel: function() { - const DevicesPanel = sdk.getComponent('settings.DevicesPanel'); - return ( -
    -

    { _t("Devices") }

    - -
    - ); - }, - - _renderBugReport: function() { - if (!SdkConfig.get().bug_report_endpoint_url) { - return
    ; - } - return ( -
    -

    { _t("Submit Debug Logs") }

    -
    -

    { - _t( "If you've submitted a bug via GitHub, debug logs can help " + - "us track down the problem. Debug logs contain application " + - "usage data including your username, the IDs or aliases of " + - "the rooms or groups you have visited and the usernames of " + - "other users. They do not contain messages.", - ) - }

    - -
    -
    - ); - }, - - _renderAnalyticsControl: function() { - if (!SdkConfig.get().piwik) return
    ; - - return
    -

    { _t('Analytics') }

    -
    - { _t('Riot collects anonymous analytics to allow us to improve the application.') } -
    - { _t('Privacy is important to us, so we don\'t collect any personal' - + ' or identifiable data for our analytics.') } - - { _t('Learn more about how we use analytics.') } - - { ANALYTICS_SETTINGS.map( this._renderDeviceSetting ) } -
    -
    ; - }, - - _renderLabs: function() { - const features = []; - SettingsStore.getLabsFeatures().forEach((featureId) => { - // TODO: this ought to be a separate component so that we don't need - // to rebind the onChange each time we render - const onChange = async (e) => { - const checked = e.target.checked; - if (featureId === "feature_lazyloading") { - const confirmed = await this._onLazyLoadChanging(checked); - if (!confirmed) { - e.preventDefault(); - return; - } - } - await SettingsStore.setFeatureEnabled(featureId, checked); - this.forceUpdate(); - }; - - features.push( -
    - - -
    ); - }); - - // No labs section when there are no features in labs - if (features.length === 0) { - return null; - } - - return ( -
    -

    { _t("Labs") }

    -
    -

    { _t("These are experimental features that may break in unexpected ways") }. { _t("Use with caution") }.

    - { features } -
    -
    - ); - }, - - _onLazyLoadChanging: async function(enabling) { - // don't prevent turning LL off when not supported - if (enabling) { - const supported = await MatrixClientPeg.get().doesServerSupportLazyLoading(); - if (!supported) { - await new Promise((resolve) => { - const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); - Modal.createDialog(QuestionDialog, { - title: _t("Lazy loading members not supported"), - description: -
    - { _t("Lazy loading is not supported by your " + - "current homeserver.") } -
    , - button: _t("OK"), - onFinished: resolve, - }); - }); - return false; - } - } - return true; - }, - - _renderDeactivateAccount: function() { - return
    -

    { _t("Deactivate Account") }

    -
    - { _t("Deactivate my account") } - -
    -
    ; - }, - - _renderTermsAndConditionsLinks: function() { - if (SdkConfig.get().terms_and_conditions_links) { - const tncLinks = []; - for (const tncEntry of SdkConfig.get().terms_and_conditions_links) { - tncLinks.push(); - } - return
    -

    { _t("Legal") }

    -
    - {tncLinks} -
    -
    ; - } else { - return null; - } - }, - - _renderClearCache: function() { - return
    -

    { _t("Clear Cache") }

    -
    - - { _t("Clear Cache and Reload") } - -
    -
    ; - }, - - _renderCheckUpdate: function() { - const platform = PlatformPeg.get(); - if (this.state.canSelfUpdate) { - return
    -

    { _t('Updates') }

    -
    - - { _t('Check for update') } - -
    -
    ; - } - return
    ; - }, - - _renderBulkOptions: function() { - const invitedRooms = MatrixClientPeg.get().getRooms().filter((r) => { - return r.hasMembershipState(this._me, "invite"); - }); - if (invitedRooms.length === 0) { - return null; - } - - const Spinner = sdk.getComponent("elements.Spinner"); - - let reject = ; - if (!this.state.rejectingInvites) { - // bind() the invited rooms so any new invites that may come in as this button is clicked - // don't inadvertently get rejected as well. - const onClick = this._onRejectAllInvitesClicked.bind(this, invitedRooms); - reject = ( - - { _t("Reject all %(invitedRooms)s invites", {invitedRooms: invitedRooms.length}) } - - ); - } - - return
    -

    { _t("Bulk Options") }

    -
    - { reject } -
    -
    ; - }, - - _renderElectronSettings: function() { - if (!PlatformPeg.get().supportsAutoLaunch()) return; - - // TODO: This should probably be a granular setting, but it only applies to electron - // and ends up being get/set outside of matrix anyways (local system setting). - return
    -

    { _t('Desktop specific') }

    -
    -
    - - -
    -
    -
    ; - }, - - _onAutoLaunchChanged: function(e) { - PlatformPeg.get().setAutoLaunchEnabled(e.target.checked).then(() => { - this.setState({ - autoLaunchEnabled: e.target.checked, - }); - }); - }, - - _mapWebRtcDevicesToSpans: function(devices) { - return devices.map((device) => { device.label }); - }, - - _setAudioOutput: function(deviceId) { - this.setState({activeAudioOutput: deviceId}); - CallMediaHandler.setAudioOutput(deviceId); - }, - - _setAudioInput: function(deviceId) { - this.setState({activeAudioInput: deviceId}); - CallMediaHandler.setAudioInput(deviceId); - }, - - _setVideoInput: function(deviceId) { - this.setState({activeVideoInput: deviceId}); - CallMediaHandler.setVideoInput(deviceId); - }, - - _requestMediaPermissions: function(event) { - const getUserMedia = ( - window.navigator.getUserMedia || window.navigator.webkitGetUserMedia || window.navigator.mozGetUserMedia - ); - if (getUserMedia) { - return getUserMedia.apply(window.navigator, [ - { video: true, audio: true }, - this._refreshMediaDevices, - function() { - const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog'); - Modal.createTrackedDialog('No media permissions', '', ErrorDialog, { - title: _t('No media permissions'), - description: _t('You may need to manually permit Riot to access your microphone/webcam'), - }); - }, - ]); - } - }, - - _renderWebRtcDeviceSettings: function() { - if (this.state.mediaDevices === false) { - return ( - - { _t('Missing Media Permissions, click here to request.') } - - ); - } else if (!this.state.mediaDevices) return; - - const Dropdown = sdk.getComponent('elements.Dropdown'); - - let speakerDropdown =

    { _t('No Audio Outputs detected') }

    ; - let microphoneDropdown =

    { _t('No Microphones detected') }

    ; - let webcamDropdown =

    { _t('No Webcams detected') }

    ; - - const defaultOption = { - deviceId: '', - label: _t('Default Device'), - }; - - const audioOutputs = this.state.mediaDevices.audiooutput.slice(0); - if (audioOutputs.length > 0) { - let defaultOutput = ''; - if (!audioOutputs.some((input) => input.deviceId === 'default')) { - audioOutputs.unshift(defaultOption); - } else { - defaultOutput = 'default'; - } - - speakerDropdown =
    -

    { _t('Audio Output') }

    - - { this._mapWebRtcDevicesToSpans(audioOutputs) } - -
    ; - } - - const audioInputs = this.state.mediaDevices.audioinput.slice(0); - if (audioInputs.length > 0) { - let defaultInput = ''; - if (!audioInputs.some((input) => input.deviceId === 'default')) { - audioInputs.unshift(defaultOption); - } else { - defaultInput = 'default'; - } - - microphoneDropdown =
    -

    { _t('Microphone') }

    - - { this._mapWebRtcDevicesToSpans(audioInputs) } - -
    ; - } - - const videoInputs = this.state.mediaDevices.videoinput.slice(0); - if (videoInputs.length > 0) { - let defaultInput = ''; - if (!videoInputs.some((input) => input.deviceId === 'default')) { - videoInputs.unshift(defaultOption); - } else { - defaultInput = 'default'; - } - - webcamDropdown =
    -

    { _t('Camera') }

    - - { this._mapWebRtcDevicesToSpans(videoInputs) } - -
    ; - } - - return
    - { speakerDropdown } - { microphoneDropdown } - { webcamDropdown } -
    ; - }, - - _renderWebRtcSettings: function() { - return
    -

    { _t('VoIP') }

    -
    - { WEBRTC_SETTINGS.map(this._renderDeviceSetting) } - { this._renderWebRtcDeviceSettings() } -
    -
    ; - }, - - onSelfShareClick: function() { - const cli = MatrixClientPeg.get(); - const ShareDialog = sdk.getComponent("dialogs.ShareDialog"); - Modal.createTrackedDialog('share self dialog', '', ShareDialog, { - target: cli.getUser(this._me), - }); - }, - - _showSpoiler: function(event) { - const target = event.target; - target.innerHTML = target.getAttribute('data-spoiler'); - - const range = document.createRange(); - range.selectNodeContents(target); - - const selection = window.getSelection(); - selection.removeAllRanges(); - selection.addRange(range); - }, - - nameForMedium: function(medium) { - if (medium === 'msisdn') return _t('Phone'); - if (medium === 'email') return _t('Email'); - return medium[0].toUpperCase() + medium.slice(1); - }, - - presentableTextForThreepid: function(threepid) { - if (threepid.medium === 'msisdn') { - return '+' + threepid.address; - } else { - return threepid.address; - } - }, - - render: function() { - const Loader = sdk.getComponent("elements.Spinner"); - switch (this.state.phase) { - case "UserSettings.LOADING": - return ( - - ); - case "UserSettings.DISPLAY": - break; // quit the switch to return the common state - default: - throw new Error("Unknown state.phase => " + this.state.phase); - } - // can only get here if phase is UserSettings.DISPLAY - const SimpleRoomHeader = sdk.getComponent('rooms.SimpleRoomHeader'); - const ChangeDisplayName = sdk.getComponent("views.settings.ChangeDisplayName"); - const ChangePassword = sdk.getComponent("views.settings.ChangePassword"); - const ChangeAvatar = sdk.getComponent('settings.ChangeAvatar'); - const Notifications = sdk.getComponent("settings.Notifications"); - const EditableText = sdk.getComponent('elements.EditableText'); - const GeminiScrollbarWrapper = sdk.getComponent("elements.GeminiScrollbarWrapper"); - - const avatarUrl = ( - this.state.avatarUrl ? MatrixClientPeg.get().mxcUrlToHttp(this.state.avatarUrl) : null - ); - - const threepidsSection = this.state.threepids.map((val, pidIndex) => { - const id = "3pid-" + val.address; - // TODO: make a separate component to avoid having to rebind onClick - // each time we render - const onRemoveClick = (e) => this.onRemoveThreepidClicked(val); - return ( -
    -
    - -
    -
    - -
    -
    - -
    -
    - ); - }); - let addEmailSection; - if (this.state.email_add_pending) { - addEmailSection = ; - } else { - addEmailSection = ( -
    -
    - -
    -
    - -
    -
    - -
    -
    - ); - } - const AddPhoneNumber = sdk.getComponent('views.settings.AddPhoneNumber'); - const addMsisdnSection = ( - - ); - threepidsSection.push(addEmailSection); - threepidsSection.push(addMsisdnSection); - - const accountJsx = ( - - ); - - let notificationArea; - if (this.state.threepids !== undefined) { - notificationArea = (
    -

    { _t("Notifications") }

    - -
    - -
    -
    ); - } - - const olmVersion = MatrixClientPeg.get().olmVersion; - // If the olmVersion is not defined then either crypto is disabled, or - // we are using a version old version of olm. We assume the former. - let olmVersionString = ""; - if (olmVersion) { - olmVersionString = `${olmVersion[0]}.${olmVersion[1]}.${olmVersion[2]}`; - } - - return ( -
    - - - - -

    { _t("Profile") }

    - -
    -
    -
    -
    - -
    -
    - -
    -
    - { threepidsSection } -
    - -
    - - {_t("Remove - -
    - -
    -
    - - -
    -
    -
    - -

    { _t("Account") }

    - -
    - - { _t("Sign out") } - - { this.state.userHasGeneratedPassword ? -
    - { _t("To return to your account in future you need to set a password") } -
    : null - } - - { accountJsx } -
    - - { this._renderGroupSettings() } - - { notificationArea } - - { this._renderUserInterfaceSettings() } - { this._renderLabs() } - { this._renderWebRtcSettings() } - { this._renderDevicesPanel() } - { this._renderCryptoInfo() } - { this._renderIgnoredUsers() } - { this._renderBulkOptions() } - { this._renderBugReport() } - - { this._renderElectronSettings() } - - { this._renderAnalyticsControl() } - -

    { _t("Advanced") }

    - -
    -
    - { _t("Logged in as:") + ' ' } - - { this._me } - -
    -
    - { _t('Access Token:') + ' ' } - - <{ _t("click to reveal") }> - -
    -
    - { _t("Homeserver is") } { MatrixClientPeg.get().getHomeserverUrl() } -
    -
    - { _t("Identity Server is") } { MatrixClientPeg.get().getIdentityServerUrl() } -
    -
    - { _t('matrix-react-sdk version:') } { (REACT_SDK_VERSION !== '') - ? gHVersionLabel('matrix-org/matrix-react-sdk', REACT_SDK_VERSION) - : REACT_SDK_VERSION - }
    - { _t('riot-web version:') } { (this.state.vectorVersion !== undefined) - ? gHVersionLabel('vector-im/riot-web', this.state.vectorVersion) - : 'unknown' - }
    - { _t("olm version:") } { olmVersionString }
    -
    -
    - - { this._renderCheckUpdate() } - - { this._renderClearCache() } - - { this._renderDeactivateAccount() } - - { this._renderTermsAndConditionsLinks() } - -
    -
    - ); - }, -}); diff --git a/src/components/views/auth/InteractiveAuthEntryComponents.js b/src/components/views/auth/InteractiveAuthEntryComponents.js index 1df0a487ccc..4851222e498 100644 --- a/src/components/views/auth/InteractiveAuthEntryComponents.js +++ b/src/components/views/auth/InteractiveAuthEntryComponents.js @@ -334,7 +334,7 @@ export const TermsAuthEntry = React.createClass({ let submitButton; if (this.props.showContinue !== false) { // XXX: button classes - submitButton = ; } @@ -525,7 +525,7 @@ export const MsisdnAuthEntry = React.createClass({ const enableSubmit = Boolean(this.state.token); const submitClasses = classnames({ mx_InteractiveAuthEntryComponents_msisdnSubmit: true, - mx_UserSettings_button: true, // XXX button classes + mx_GeneralButton: true, }); let errorSection; if (this.state.errorText) { diff --git a/src/components/views/dialogs/DeviceVerifyDialog.js b/src/components/views/dialogs/DeviceVerifyDialog.js index e84010726d0..d0643a60c4f 100644 --- a/src/components/views/dialogs/DeviceVerifyDialog.js +++ b/src/components/views/dialogs/DeviceVerifyDialog.js @@ -241,7 +241,7 @@ export default class DeviceVerifyDialog extends React.Component { "and ask them whether the key they see in their User Settings " + "for this device matches the key below:") }

    -
    +