From eac50aa800887583e46b19a2fd91455dc1c76bcb Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 4 Feb 2019 13:25:26 -0700 Subject: [PATCH 1/3] Remove old user and room settings This takes out the old user and room settings, replacing the paths with the new dialog editions. The labs setting has been removed in order to support this change. In addition to removing the old components outright, some older components which were only used by the settings pages have been removed. The exception is the ColorSettings component as it has a high chance of sticking around in the future. Styles that were shared by the settings components have been broken out to dedicated sections, making it easier to remove the old styles entirely. Some stability testing of the app has been performed to ensure the app still works, however given the scope of this change there is a possibility of some broken functionality. --- .eslintignore.errorfiles | 2 - res/css/_common.scss | 13 + res/css/_components.scss | 7 +- res/css/structures/_TabbedView.scss | 5 - res/css/structures/_UserSettings.scss | 269 ---- res/css/views/dialogs/_Analytics.scss | 19 + .../views/dialogs/_DeviceVerifyDialog.scss | 29 + .../views/elements/_ManageIntegsButton.scss | 53 + .../views/room_settings/_AliasSettings.scss | 28 + .../views/room_settings/_ColorSettings.scss | 39 + res/css/views/rooms/_RoomSettings.scss | 260 --- res/css/views/settings/_Notifications.scss | 11 +- src/Analytics.js | 2 +- src/PageTypes.js | 1 - src/UserSettingsStore.js | 45 +- src/components/structures/LoggedInView.js | 9 - src/components/structures/MatrixChat.js | 43 +- src/components/structures/RoomView.js | 64 +- src/components/structures/TabbedView.js | 1 - src/components/structures/UserSettings.js | 1393 ----------------- .../auth/InteractiveAuthEntryComponents.js | 4 +- .../views/dialogs/DeviceVerifyDialog.js | 2 +- .../views/dialogs/InteractiveAuthDialog.js | 2 +- .../views/dialogs/RoomSettingsDialog.js | 35 - .../views/dialogs/UserSettingsDialog.js | 22 - .../views/elements/ManageIntegsButton.js | 4 +- .../views/room_settings/AliasSettings.js | 8 +- .../views/room_settings/ColorSettings.js | 11 +- src/components/views/rooms/RoomHeader.js | 227 +-- src/components/views/rooms/RoomSettings.js | 1039 ------------ .../views/settings/AddPhoneNumber.js | 174 -- .../views/settings/KeyBackupPanel.js | 2 +- .../views/settings/Notifications.js | 6 +- src/i18n/strings/en_EN.json | 61 +- src/settings/Settings.js | 6 - src/stores/RoomViewStore.js | 34 +- 36 files changed, 296 insertions(+), 3634 deletions(-) delete mode 100644 res/css/structures/_UserSettings.scss create mode 100644 res/css/views/dialogs/_Analytics.scss create mode 100644 res/css/views/dialogs/_DeviceVerifyDialog.scss create mode 100644 res/css/views/elements/_ManageIntegsButton.scss create mode 100644 res/css/views/room_settings/_AliasSettings.scss create mode 100644 res/css/views/room_settings/_ColorSettings.scss delete mode 100644 res/css/views/rooms/_RoomSettings.scss delete mode 100644 src/components/structures/UserSettings.js delete mode 100644 src/components/views/rooms/RoomSettings.js delete mode 100644 src/components/views/settings/AddPhoneNumber.js 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..b2db17c77b7 100644 --- a/res/css/_common.scss +++ b/res/css/_common.scss @@ -270,6 +270,19 @@ 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. +.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..293ae22b9da 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,10 @@ 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. 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 +37,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 613d310da97..5682f966341 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:") }

    -
    +