Merge remote-tracking branch 'origin/develop' into develop
|
@ -44,13 +44,11 @@ src/components/views/rooms/MessageComposer.js
|
||||||
src/components/views/rooms/PinnedEventTile.js
|
src/components/views/rooms/PinnedEventTile.js
|
||||||
src/components/views/rooms/RoomList.js
|
src/components/views/rooms/RoomList.js
|
||||||
src/components/views/rooms/RoomPreviewBar.js
|
src/components/views/rooms/RoomPreviewBar.js
|
||||||
src/components/views/rooms/RoomSettings.js
|
|
||||||
src/components/views/rooms/SearchableEntityList.js
|
src/components/views/rooms/SearchableEntityList.js
|
||||||
src/components/views/rooms/SearchBar.js
|
src/components/views/rooms/SearchBar.js
|
||||||
src/components/views/rooms/SearchResultTile.js
|
src/components/views/rooms/SearchResultTile.js
|
||||||
src/components/views/rooms/TopUnreadMessagesBar.js
|
src/components/views/rooms/TopUnreadMessagesBar.js
|
||||||
src/components/views/rooms/UserTile.js
|
src/components/views/rooms/UserTile.js
|
||||||
src/components/views/settings/AddPhoneNumber.js
|
|
||||||
src/components/views/settings/ChangeAvatar.js
|
src/components/views/settings/ChangeAvatar.js
|
||||||
src/components/views/settings/ChangePassword.js
|
src/components/views/settings/ChangePassword.js
|
||||||
src/components/views/settings/DevicesPanel.js
|
src/components/views/settings/DevicesPanel.js
|
||||||
|
|
|
@ -91,13 +91,13 @@ module.exports = {
|
||||||
// to JSX.
|
// to JSX.
|
||||||
ignorePattern: '^\\s*<',
|
ignorePattern: '^\\s*<',
|
||||||
ignoreComments: true,
|
ignoreComments: true,
|
||||||
|
ignoreRegExpLiterals: true,
|
||||||
code: 120,
|
code: 120,
|
||||||
}],
|
}],
|
||||||
"valid-jsdoc": ["warn"],
|
"valid-jsdoc": ["warn"],
|
||||||
"new-cap": ["warn"],
|
"new-cap": ["warn"],
|
||||||
"key-spacing": ["warn"],
|
"key-spacing": ["warn"],
|
||||||
"prefer-const": ["warn"],
|
"prefer-const": ["warn"],
|
||||||
"arrow-parens": "off",
|
|
||||||
|
|
||||||
// crashes currently: https://github.com/eslint/eslint/issues/6274
|
// crashes currently: https://github.com/eslint/eslint/issues/6274
|
||||||
"generator-star-spacing": "off",
|
"generator-star-spacing": "off",
|
||||||
|
|
|
@ -270,6 +270,20 @@ textarea {
|
||||||
opacity: 0.7;
|
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 {
|
.mx_linkButton {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: $accent-color;
|
color: $accent-color;
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
@import "./structures/_CompatibilityPage.scss";
|
@import "./structures/_CompatibilityPage.scss";
|
||||||
@import "./structures/_ContextualMenu.scss";
|
@import "./structures/_ContextualMenu.scss";
|
||||||
@import "./structures/_CreateRoom.scss";
|
@import "./structures/_CreateRoom.scss";
|
||||||
|
@import "./structures/_CustomRoomTagPanel.scss";
|
||||||
@import "./structures/_FilePanel.scss";
|
@import "./structures/_FilePanel.scss";
|
||||||
@import "./structures/_GroupView.scss";
|
@import "./structures/_GroupView.scss";
|
||||||
@import "./structures/_HomePage.scss";
|
@import "./structures/_HomePage.scss";
|
||||||
|
@ -19,9 +20,9 @@
|
||||||
@import "./structures/_SearchBox.scss";
|
@import "./structures/_SearchBox.scss";
|
||||||
@import "./structures/_TabbedView.scss";
|
@import "./structures/_TabbedView.scss";
|
||||||
@import "./structures/_TagPanel.scss";
|
@import "./structures/_TagPanel.scss";
|
||||||
|
@import "./structures/_TagPanelButtons.scss";
|
||||||
@import "./structures/_TopLeftMenuButton.scss";
|
@import "./structures/_TopLeftMenuButton.scss";
|
||||||
@import "./structures/_UploadBar.scss";
|
@import "./structures/_UploadBar.scss";
|
||||||
@import "./structures/_UserSettings.scss";
|
|
||||||
@import "./structures/_ViewSource.scss";
|
@import "./structures/_ViewSource.scss";
|
||||||
@import "./structures/auth/_Login.scss";
|
@import "./structures/auth/_Login.scss";
|
||||||
@import "./views/auth/_AuthBody.scss";
|
@import "./views/auth/_AuthBody.scss";
|
||||||
|
@ -35,6 +36,7 @@
|
||||||
@import "./views/auth/_LanguageSelector.scss";
|
@import "./views/auth/_LanguageSelector.scss";
|
||||||
@import "./views/auth/_ServerConfig.scss";
|
@import "./views/auth/_ServerConfig.scss";
|
||||||
@import "./views/auth/_ServerTypeSelector.scss";
|
@import "./views/auth/_ServerTypeSelector.scss";
|
||||||
|
@import "./views/auth/_Welcome.scss";
|
||||||
@import "./views/avatars/_BaseAvatar.scss";
|
@import "./views/avatars/_BaseAvatar.scss";
|
||||||
@import "./views/avatars/_MemberStatusMessageAvatar.scss";
|
@import "./views/avatars/_MemberStatusMessageAvatar.scss";
|
||||||
@import "./views/context_menus/_MessageContextMenu.scss";
|
@import "./views/context_menus/_MessageContextMenu.scss";
|
||||||
|
@ -42,6 +44,7 @@
|
||||||
@import "./views/context_menus/_StatusMessageContextMenu.scss";
|
@import "./views/context_menus/_StatusMessageContextMenu.scss";
|
||||||
@import "./views/context_menus/_TagTileContextMenu.scss";
|
@import "./views/context_menus/_TagTileContextMenu.scss";
|
||||||
@import "./views/context_menus/_TopLeftMenu.scss";
|
@import "./views/context_menus/_TopLeftMenu.scss";
|
||||||
|
@import "./views/dialogs/_Analytics.scss";
|
||||||
@import "./views/dialogs/_BugReportDialog.scss";
|
@import "./views/dialogs/_BugReportDialog.scss";
|
||||||
@import "./views/dialogs/_ChangelogDialog.scss";
|
@import "./views/dialogs/_ChangelogDialog.scss";
|
||||||
@import "./views/dialogs/_ChatCreateOrReuseChatDialog.scss";
|
@import "./views/dialogs/_ChatCreateOrReuseChatDialog.scss";
|
||||||
|
@ -50,6 +53,7 @@
|
||||||
@import "./views/dialogs/_CreateGroupDialog.scss";
|
@import "./views/dialogs/_CreateGroupDialog.scss";
|
||||||
@import "./views/dialogs/_CreateRoomDialog.scss";
|
@import "./views/dialogs/_CreateRoomDialog.scss";
|
||||||
@import "./views/dialogs/_DeactivateAccountDialog.scss";
|
@import "./views/dialogs/_DeactivateAccountDialog.scss";
|
||||||
|
@import "./views/dialogs/_DeviceVerifyDialog.scss";
|
||||||
@import "./views/dialogs/_DevtoolsDialog.scss";
|
@import "./views/dialogs/_DevtoolsDialog.scss";
|
||||||
@import "./views/dialogs/_EncryptedEventDialog.scss";
|
@import "./views/dialogs/_EncryptedEventDialog.scss";
|
||||||
@import "./views/dialogs/_GroupAddressPicker.scss";
|
@import "./views/dialogs/_GroupAddressPicker.scss";
|
||||||
|
@ -74,9 +78,9 @@
|
||||||
@import "./views/elements/_Dropdown.scss";
|
@import "./views/elements/_Dropdown.scss";
|
||||||
@import "./views/elements/_EditableItemList.scss";
|
@import "./views/elements/_EditableItemList.scss";
|
||||||
@import "./views/elements/_Field.scss";
|
@import "./views/elements/_Field.scss";
|
||||||
@import "./views/elements/_HexVerify.scss";
|
|
||||||
@import "./views/elements/_ImageView.scss";
|
@import "./views/elements/_ImageView.scss";
|
||||||
@import "./views/elements/_InlineSpinner.scss";
|
@import "./views/elements/_InlineSpinner.scss";
|
||||||
|
@import "./views/elements/_ManageIntegsButton.scss";
|
||||||
@import "./views/elements/_MemberEventListSummary.scss";
|
@import "./views/elements/_MemberEventListSummary.scss";
|
||||||
@import "./views/elements/_ProgressBar.scss";
|
@import "./views/elements/_ProgressBar.scss";
|
||||||
@import "./views/elements/_ReplyThread.scss";
|
@import "./views/elements/_ReplyThread.scss";
|
||||||
|
@ -104,9 +108,12 @@
|
||||||
@import "./views/messages/_SenderProfile.scss";
|
@import "./views/messages/_SenderProfile.scss";
|
||||||
@import "./views/messages/_TextualEvent.scss";
|
@import "./views/messages/_TextualEvent.scss";
|
||||||
@import "./views/messages/_UnknownBody.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/_AppsDrawer.scss";
|
||||||
@import "./views/rooms/_Autocomplete.scss";
|
@import "./views/rooms/_Autocomplete.scss";
|
||||||
@import "./views/rooms/_AuxPanel.scss";
|
@import "./views/rooms/_AuxPanel.scss";
|
||||||
|
@import "./views/rooms/_E2EIcon.scss";
|
||||||
@import "./views/rooms/_EntityTile.scss";
|
@import "./views/rooms/_EntityTile.scss";
|
||||||
@import "./views/rooms/_EventTile.scss";
|
@import "./views/rooms/_EventTile.scss";
|
||||||
@import "./views/rooms/_JumpToBottomButton.scss";
|
@import "./views/rooms/_JumpToBottomButton.scss";
|
||||||
|
@ -124,7 +131,6 @@
|
||||||
@import "./views/rooms/_RoomList.scss";
|
@import "./views/rooms/_RoomList.scss";
|
||||||
@import "./views/rooms/_RoomPreviewBar.scss";
|
@import "./views/rooms/_RoomPreviewBar.scss";
|
||||||
@import "./views/rooms/_RoomRecoveryReminder.scss";
|
@import "./views/rooms/_RoomRecoveryReminder.scss";
|
||||||
@import "./views/rooms/_RoomSettings.scss";
|
|
||||||
@import "./views/rooms/_RoomTile.scss";
|
@import "./views/rooms/_RoomTile.scss";
|
||||||
@import "./views/rooms/_RoomTooltip.scss";
|
@import "./views/rooms/_RoomTooltip.scss";
|
||||||
@import "./views/rooms/_RoomUpgradeWarningBar.scss";
|
@import "./views/rooms/_RoomUpgradeWarningBar.scss";
|
||||||
|
@ -150,6 +156,7 @@
|
||||||
@import "./views/settings/tabs/_SecuritySettingsTab.scss";
|
@import "./views/settings/tabs/_SecuritySettingsTab.scss";
|
||||||
@import "./views/settings/tabs/_SettingsTab.scss";
|
@import "./views/settings/tabs/_SettingsTab.scss";
|
||||||
@import "./views/settings/tabs/_VoiceSettingsTab.scss";
|
@import "./views/settings/tabs/_VoiceSettingsTab.scss";
|
||||||
|
@import "./views/verification/_VerificationShowSas.scss";
|
||||||
@import "./views/voip/_CallView.scss";
|
@import "./views/voip/_CallView.scss";
|
||||||
@import "./views/voip/_IncomingCallbox.scss";
|
@import "./views/voip/_IncomingCallbox.scss";
|
||||||
@import "./views/voip/_VideoView.scss";
|
@import "./views/voip/_VideoView.scss";
|
||||||
|
|
45
res/css/structures/_CustomRoomTagPanel.scss
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
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_LeftPanel_tagPanelContainer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_CustomRoomTagPanel {
|
||||||
|
background-color: $tagpanel-bg-color;
|
||||||
|
max-height: 40vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_CustomRoomTagPanel_scroller {
|
||||||
|
max-height: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_CustomRoomTagPanel .mx_AccessibleButton {
|
||||||
|
margin: 9px auto;
|
||||||
|
width: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_CustomRoomTagPanel .mx_BaseAvatar_image {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_CustomRoomTagPanel .mx_AccessibleButton.CustomRoomTagPanel_tileSelected .mx_BaseAvatar_image {
|
||||||
|
border: 3px solid $warning-color;
|
||||||
|
border-radius: 40px;
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2016 OpenMarket Ltd
|
Copyright 2016 OpenMarket Ltd
|
||||||
Copyright 2017 Vector Creations Ltd
|
Copyright 2017 Vector Creations Ltd
|
||||||
|
Copyright 2019 New Vector Ltd
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -22,27 +23,3 @@ limitations under the License.
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_HomePage iframe {
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
border: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_HomePage_body {
|
|
||||||
// margin-left: 63px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_HomePage_guest_warning {
|
|
||||||
display: flex;
|
|
||||||
background-color: $secondary-accent-color;
|
|
||||||
border: 1px solid $accent-color;
|
|
||||||
margin: 20px;
|
|
||||||
padding: 20px 40px;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_HomePage_guest_warning img {
|
|
||||||
padding-right: 10px;
|
|
||||||
}
|
|
||||||
|
|
|
@ -33,6 +33,11 @@ limitations under the License.
|
||||||
flex: 0 0 140px;
|
flex: 0 0 140px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_LeftPanel_tagPanelContainer {
|
||||||
|
flex: 0 0 70px;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_LeftPanel_hideButton {
|
.mx_LeftPanel_hideButton {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 10px;
|
top: 10px;
|
||||||
|
|
|
@ -121,7 +121,7 @@ limitations under the License.
|
||||||
|
|
||||||
.mx_RoomStatusBar_connectionLostBar img {
|
.mx_RoomStatusBar_connectionLostBar img {
|
||||||
padding-left: 10px;
|
padding-left: 10px;
|
||||||
padding-right: 22px;
|
padding-right: 10px;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
float: left;
|
float: left;
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,6 +75,7 @@ limitations under the License.
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
padding: 0 5px;
|
padding: 0 5px;
|
||||||
background-color: $roomtile-name-color;
|
background-color: $roomtile-name-color;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomSubList_addRoom, .mx_RoomSubList_badge {
|
.mx_RoomSubList_addRoom, .mx_RoomSubList_badge {
|
||||||
|
|
|
@ -17,17 +17,21 @@ limitations under the License.
|
||||||
|
|
||||||
.mx_TabbedView {
|
.mx_TabbedView {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0 0 0 58px;
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 100%;
|
flex-direction: column;
|
||||||
height: 100%;
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_TabbedView_tabLabels {
|
.mx_TabbedView_tabLabels {
|
||||||
width: 150px;
|
width: 150px;
|
||||||
max-width: 150px;
|
max-width: 150px;
|
||||||
height: 100%;
|
|
||||||
color: $tab-label-fg-color;
|
color: $tab-label-fg-color;
|
||||||
|
position: fixed;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_TabbedView_tabLabel {
|
.mx_TabbedView_tabLabel {
|
||||||
|
@ -47,11 +51,6 @@ limitations under the License.
|
||||||
color: $tab-label-active-fg-color;
|
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 {;
|
.mx_TabbedView_maskedIcon {;
|
||||||
margin-left: 6px;
|
margin-left: 6px;
|
||||||
margin-right: 9px;
|
margin-right: 9px;
|
||||||
|
@ -82,13 +81,15 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_TabbedView_tabPanel {
|
.mx_TabbedView_tabPanel {
|
||||||
width: calc(100% - 320px);
|
margin-left: 220px; // 150px sidebar + 70px padding
|
||||||
display: inline-block;
|
|
||||||
margin-left: 70px;
|
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 0; // firefox
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_TabbedView_tabPanelContent {
|
.mx_TabbedView_tabPanelContent {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
min-width: 560px;
|
overflow: auto;
|
||||||
|
min-height: 0; // firefox
|
||||||
}
|
}
|
|
@ -15,7 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.mx_TagPanel {
|
.mx_TagPanel {
|
||||||
flex: 0 0 70px;
|
flex: 1;
|
||||||
background-color: $tagpanel-bg-color;
|
background-color: $tagpanel-bg-color;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
|
@ -68,10 +68,13 @@ limitations under the License.
|
||||||
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
.mx_TagPanel .mx_TagPanel_tagTileContainer > div {
|
||||||
|
height: 40px;
|
||||||
|
padding: 5px 0 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_TagPanel .mx_TagTile {
|
.mx_TagPanel .mx_TagTile {
|
||||||
padding-top: 9px;
|
margin: 9px 0;
|
||||||
padding-bottom: 9px;
|
|
||||||
// opacity: 0.5;
|
// opacity: 0.5;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
@ -81,13 +84,7 @@ limitations under the License.
|
||||||
// opacity: 1;
|
// opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_TagPanel .mx_TagTile.mx_TagTile_selected {
|
|
||||||
/* To offset border of mx_TagTile_avatar */
|
|
||||||
padding: 3px 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_TagPanel .mx_TagTile.mx_TagTile_selected .mx_TagTile_avatar .mx_BaseAvatar {
|
.mx_TagPanel .mx_TagTile.mx_TagTile_selected .mx_TagTile_avatar .mx_BaseAvatar {
|
||||||
border: 3px solid $accent-color;
|
|
||||||
background-color: $accent-color;
|
background-color: $accent-color;
|
||||||
border-radius: 40px;
|
border-radius: 40px;
|
||||||
|
|
||||||
|
@ -97,6 +94,13 @@ limitations under the License.
|
||||||
width: 40px;
|
width: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_TagPanel .mx_TagTile_selected .mx_BaseAvatar_image {
|
||||||
|
border: 3px solid $accent-color;
|
||||||
|
height: 40px;
|
||||||
|
width: 40px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_TagPanel .mx_TagTile.mx_AccessibleButton:focus {
|
.mx_TagPanel .mx_TagTile.mx_AccessibleButton:focus {
|
||||||
filter: none;
|
filter: none;
|
||||||
}
|
}
|
||||||
|
@ -112,7 +116,7 @@ limitations under the License.
|
||||||
height: 15px;
|
height: 15px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: -5px;
|
right: -5px;
|
||||||
top: 1px;
|
top: -8px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
background-color: $neutral-badge-color;
|
background-color: $neutral-badge-color;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
|
@ -124,39 +128,22 @@ limitations under the License.
|
||||||
padding-right: 4px;
|
padding-right: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_TagPanel_groupsButton {
|
.mx_TagTile_avatar {
|
||||||
flex: 0;
|
|
||||||
margin: 17px 0 3px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_TagPanel_groupsButton > .mx_GroupsButton:before {
|
|
||||||
mask: url('$(res)/img/feather-icons/users.svg');
|
|
||||||
mask-position: center 11px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_TagPanel_groupsButton > .mx_TagPanel_report:before {
|
|
||||||
mask: url('$(res)/img/feather-icons/life-buoy.svg');
|
|
||||||
mask-position: center 9px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_TagPanel_groupsButton > .mx_AccessibleButton {
|
|
||||||
margin-bottom: 12px;
|
|
||||||
height: 40px;
|
|
||||||
width: 40px;
|
|
||||||
border-radius: 20px;
|
|
||||||
background-color: $roomheader-addroom-color;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
/* overwrite mx_RoleButton inline-block */
|
}
|
||||||
display: block !important;
|
|
||||||
|
|
||||||
&:before {
|
.mx_TagTile_badge {
|
||||||
background-color: $tagpanel-bg-color;
|
|
||||||
mask-repeat: no-repeat;
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
right: -4px;
|
||||||
bottom: 0;
|
top: -2px;
|
||||||
left: 0;
|
border-radius: 8px;
|
||||||
right: 0;
|
color: $accent-fg-color;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 0 5px;
|
||||||
|
background-color: $roomtile-name-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_TagTile_badgeHighlight {
|
||||||
|
background-color: $warning-color;
|
||||||
}
|
}
|
||||||
|
|
56
res/css/structures/_TagPanelButtons.scss
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
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_TagPanelButtons {
|
||||||
|
background-color: $tagpanel-bg-color;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 17px 0 3px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_TagPanelButtons > .mx_GroupsButton:before {
|
||||||
|
mask: url('$(res)/img/feather-icons/users.svg');
|
||||||
|
mask-position: center 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_TagPanelButtons > .mx_TagPanelButtons_report:before {
|
||||||
|
mask: url('$(res)/img/feather-icons/life-buoy.svg');
|
||||||
|
mask-position: center 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_TagPanelButtons > .mx_AccessibleButton {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
height: 40px;
|
||||||
|
width: 40px;
|
||||||
|
border-radius: 20px;
|
||||||
|
background-color: $roomheader-addroom-color;
|
||||||
|
position: relative;
|
||||||
|
/* overwrite mx_RoleButton inline-block */
|
||||||
|
display: block !important;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
background-color: $tagpanel-bg-color;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -19,7 +19,8 @@ limitations under the License.
|
||||||
width: 100%;
|
width: 100%;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
opacity: 0.72;
|
opacity: 0.72;
|
||||||
margin: 20px 0;
|
padding: 20px 0;
|
||||||
|
background: linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.8));
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_AuthFooter a:link,
|
.mx_AuthFooter a:link,
|
||||||
|
|
|
@ -18,6 +18,6 @@ limitations under the License.
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
width: 206px;
|
width: 206px;
|
||||||
padding: 25px 50px;
|
padding: 25px 40px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
||||||
.mx_AuthHeaderLogo {
|
.mx_AuthHeaderLogo {
|
||||||
margin-top: 15px;
|
margin-top: 15px;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
padding: 0 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_AuthHeaderLogo img {
|
.mx_AuthHeaderLogo img {
|
||||||
|
|
|
@ -26,8 +26,6 @@ limitations under the License.
|
||||||
display: flex;
|
display: flex;
|
||||||
margin: 100px auto auto;
|
margin: 100px auto auto;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
// Not currently supported in Firefox: https://bugzilla.mozilla.org/show_bug.cgi?id=1178765
|
|
||||||
backdrop-filter: blur(10px);
|
|
||||||
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.33);
|
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.33);
|
||||||
background-color: $authpage-modal-bg-color;
|
background-color: $authpage-modal-bg-color;
|
||||||
}
|
}
|
||||||
|
|
26
res/css/views/auth/_Welcome.scss
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
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_Welcome {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_Welcome .mx_AuthBody_language {
|
||||||
|
width: 120px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
|
@ -27,6 +27,10 @@ limitations under the License.
|
||||||
margin: 5px 0;
|
margin: 5px 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
|
li.mx_TopLeftMenu_icon_home::after {
|
||||||
|
mask-image: url('$(res)/img/feather-icons/home.svg');
|
||||||
|
}
|
||||||
|
|
||||||
li.mx_TopLeftMenu_icon_settings::after {
|
li.mx_TopLeftMenu_icon_settings::after {
|
||||||
mask-image: url('$(res)/img/feather-icons/settings.svg');
|
mask-image: url('$(res)/img/feather-icons/settings.svg');
|
||||||
}
|
}
|
||||||
|
|
19
res/css/views/dialogs/_Analytics.scss
Normal file
|
@ -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;
|
||||||
|
}
|
|
@ -14,21 +14,16 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.mx_HexVerify {
|
.mx_DeviceVerifyDialog_cryptoSection ul {
|
||||||
text-align: center;
|
display: table;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_HexVerify_pair {
|
.mx_DeviceVerifyDialog_cryptoSection li {
|
||||||
display: inline-block;
|
display: table-row;
|
||||||
font-weight: bold;
|
|
||||||
padding-left: 3px;
|
|
||||||
padding-right: 3px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_HexVerify_pair_verified {
|
.mx_DeviceVerifyDialog_cryptoSection label,
|
||||||
color: $accent-color;
|
.mx_DeviceVerifyDialog_cryptoSection span {
|
||||||
}
|
display: table-cell;
|
||||||
|
padding-right: 1em;
|
||||||
.mx_HexVerify_pair:hover{
|
|
||||||
color: $accent-color;
|
|
||||||
}
|
}
|
|
@ -16,55 +16,32 @@ limitations under the License.
|
||||||
|
|
||||||
.mx_SettingsDialog {
|
.mx_SettingsDialog {
|
||||||
.mx_Dialog {
|
.mx_Dialog {
|
||||||
max-width: 784px; // 900px - 58px (left padding) - 58px (right padding)
|
max-width: 900px;
|
||||||
width: 80%;
|
width: 80%;
|
||||||
height: 80%;
|
height: 80%;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
padding-top: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
padding-left: 0;
|
||||||
|
|
||||||
.mx_TabbedView_tabLabels {
|
.mx_TabbedView {
|
||||||
// Force the sidebar to be always visible, letting the rest of the content scroll
|
top: 65px;
|
||||||
position: fixed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_TabbedView_tabPanel {
|
.mx_TabbedView .mx_SettingsTab {
|
||||||
max-width: 485px;
|
box-sizing: border-box;
|
||||||
margin-left: 206px; // 70px margin + 136px for the sidebar
|
min-width: 550px;
|
||||||
|
padding-right: 130px;
|
||||||
|
|
||||||
|
// Put some padding on the bottom to avoid the settings tab from
|
||||||
|
// colliding harshly with the dialog when scrolled down.
|
||||||
|
padding-bottom: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_SettingsDialog_header {
|
.mx_Dialog_title {
|
||||||
font-size: 24px;
|
|
||||||
display: block;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: $dialog-title-fg-color;
|
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_SettingsDialog_close {
|
|
||||||
position: absolute;
|
|
||||||
top: 16px;
|
|
||||||
right: 25px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_SettingsDialog_closeIcon {
|
|
||||||
width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_SettingsDialog_closeIcon:before {
|
|
||||||
mask: url('$(res)/img/feather-icons/cancel.svg');
|
|
||||||
background-color: $dialog-close-fg-color;
|
|
||||||
mask-repeat: no-repeat;
|
|
||||||
mask-size: 16px;
|
|
||||||
mask-position: center;
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,10 @@ limitations under the License.
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_UnknownDeviceDialog .mx_DeviceVerifyButtons {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_UnknownDeviceDialog .mx_Dialog_content {
|
.mx_UnknownDeviceDialog .mx_Dialog_content {
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,7 @@ limitations under the License.
|
||||||
.mx_Dropdown_arrow {
|
.mx_Dropdown_arrow {
|
||||||
width: 10px;
|
width: 10px;
|
||||||
height: 6px;
|
height: 6px;
|
||||||
padding-right: 8px;
|
padding-right: 9px;
|
||||||
mask: url('$(res)/img/feather-icons/dropdown-arrow.svg');
|
mask: url('$(res)/img/feather-icons/dropdown-arrow.svg');
|
||||||
mask-repeat: no-repeat;
|
mask-repeat: no-repeat;
|
||||||
background: $primary-fg-color;
|
background: $primary-fg-color;
|
||||||
|
@ -54,6 +54,7 @@ limitations under the License.
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_Dropdown_option {
|
.mx_Dropdown_option {
|
||||||
|
|
|
@ -100,7 +100,8 @@ limitations under the License.
|
||||||
top 0.25s ease-out 0s,
|
top 0.25s ease-out 0s,
|
||||||
background-color 0.25s ease-out 0s;
|
background-color 0.25s ease-out 0s;
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
top: -14px;
|
top: -13px;
|
||||||
|
padding: 0 2px;
|
||||||
background-color: $field-focused-label-bg-color;
|
background-color: $field-focused-label-bg-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,7 +112,11 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_Field select:disabled,
|
.mx_Field select:disabled,
|
||||||
.mx_Field input:disabled {
|
.mx_Field select:disabled + label,
|
||||||
|
.mx_Field input:disabled,
|
||||||
|
.mx_Field input:disabled + label,
|
||||||
|
.mx_Field textarea:disabled,
|
||||||
|
.mx_Field textarea:disabled + label {
|
||||||
background-color: $field-focused-label-bg-color;
|
background-color: $field-focused-label-bg-color;
|
||||||
color: $greyed-fg-color;
|
color: $greyed-fg-color;
|
||||||
}
|
}
|
||||||
|
|
53
res/css/views/elements/_ManageIntegsButton.scss
Normal file
|
@ -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;
|
||||||
|
}
|
28
res/css/views/room_settings/_AliasSettings.scss
Normal file
|
@ -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;
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2018 New Vector
|
Copyright 2019 New Vector Ltd.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -14,16 +14,26 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import SettingController from "./SettingController";
|
.mx_ColorSettings_roomColor {
|
||||||
import MatrixClientPeg from "../../MatrixClientPeg";
|
display: inline-block;
|
||||||
import PlatformPeg from "../../PlatformPeg";
|
position: relative;
|
||||||
|
width: 37px;
|
||||||
export default class LazyLoadingController extends SettingController {
|
height: 37px;
|
||||||
async onChange(level, roomId, newValue) {
|
border: 1px solid #979797;
|
||||||
if (!PlatformPeg.get()) return;
|
margin-right: 13px;
|
||||||
|
cursor: pointer;
|
||||||
MatrixClientPeg.get().stopClient();
|
|
||||||
await MatrixClientPeg.get().store.deleteAllData();
|
|
||||||
PlatformPeg.get().reload();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_ColorSettings_roomColor_selected {
|
||||||
|
position: absolute;
|
||||||
|
left: 10px;
|
||||||
|
top: 4px;
|
||||||
|
cursor: default ! important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_ColorSettings_roomColorPrimary {
|
||||||
|
height: 10px;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0px;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
|
@ -56,9 +56,8 @@ limitations under the License.
|
||||||
max-width: 960px;
|
max-width: 960px;
|
||||||
width: 50%;
|
width: 50%;
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
border: 1px solid $primary-hairline-color;
|
border: 5px solid $widget-menu-bar-bg-color;
|
||||||
border-radius: 2px;
|
border-radius: 4px;
|
||||||
background-color: $dialog-background-bg-color;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_AppTile:last-child {
|
.mx_AppTile:last-child {
|
||||||
|
@ -71,8 +70,8 @@ limitations under the License.
|
||||||
height: 100%;
|
height: 100%;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
border: 1px solid $primary-hairline-color;
|
border: 5px solid $widget-menu-bar-bg-color;
|
||||||
border-radius: 2px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_AppTile_mini {
|
.mx_AppTile_mini {
|
||||||
|
@ -93,9 +92,7 @@ limitations under the License.
|
||||||
|
|
||||||
.mx_AppTileMenuBar {
|
.mx_AppTileMenuBar {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 2px 10px;
|
font-size: 12px;
|
||||||
border-bottom: 1px solid $primary-hairline-color;
|
|
||||||
font-size: 10px;
|
|
||||||
background-color: $widget-menu-bar-bg-color;
|
background-color: $widget-menu-bar-bg-color;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
@ -104,6 +101,10 @@ limitations under the License.
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_AppTileMenuBar_expanded {
|
||||||
|
padding-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_AppTileMenuBarTitle {
|
.mx_AppTileMenuBarTitle {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
@ -111,6 +112,10 @@ limitations under the License.
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_AppTileMenuBarTitle > :last-child {
|
||||||
|
margin-left: 9px;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_AppTileMenuBarWidgets {
|
.mx_AppTileMenuBarWidgets {
|
||||||
float: right;
|
float: right;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -118,6 +123,53 @@ limitations under the License.
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_AppTileMenuBar_iconButton {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
mask-position: 0 center;
|
||||||
|
mask-size: auto 12px;
|
||||||
|
background-color: $topleftmenu-color;
|
||||||
|
margin: 0 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_AppTileMenuBar_iconButton.mx_AppTileMenuBar_iconButton_minimise {
|
||||||
|
mask-image: url('$(res)/img/feather-icons/widget/minimise.svg');
|
||||||
|
background-color: $accent-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_AppTileMenuBar_iconButton.mx_AppTileMenuBar_iconButton_maximise {
|
||||||
|
mask-image: url('$(res)/img/feather-icons/widget/maximise.svg');
|
||||||
|
background-color: $accent-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_AppTileMenuBar_iconButton.mx_AppTileMenuBar_iconButton_reload {
|
||||||
|
mask-image: url('$(res)/img/feather-icons/widget/refresh.svg');
|
||||||
|
mask-size: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_AppTileMenuBar_iconButton.mx_AppTileMenuBar_iconButton_popout {
|
||||||
|
mask-image: url('$(res)/img/feather-icons/widget/external-link.svg');
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_AppTileMenuBar_iconButton.mx_AppTileMenuBar_iconButton_snapshot {
|
||||||
|
mask-image: url('$(res)/img/feather-icons/widget/camera.svg');
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_AppTileMenuBar_iconButton.mx_AppTileMenuBar_iconButton_edit {
|
||||||
|
mask-image: url('$(res)/img/feather-icons/widget/edit.svg');
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_AppTileMenuBar_iconButton.mx_AppTileMenuBar_iconButton_delete {
|
||||||
|
mask-image: url('$(res)/img/feather-icons/widget/bin.svg');
|
||||||
|
background-color: $warning-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_AppTileMenuBar_iconButton.mx_AppTileMenuBar_iconButton_cancel {
|
||||||
|
mask-image: url('$(res)/img/feather-icons/widget/x-circle.svg');
|
||||||
|
}
|
||||||
|
|
||||||
|
/* delete ? */
|
||||||
.mx_AppTileMenuBarWidget {
|
.mx_AppTileMenuBarWidget {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
width: 10px;
|
width: 10px;
|
||||||
|
@ -265,14 +317,11 @@ form.mx_Custom_Widget_Form div {
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_AppPermissionButton {
|
.mx_AppPermissionButton {
|
||||||
padding: 5px;
|
border: none;
|
||||||
|
padding: 5px 20px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
color: $warning-color;
|
background-color: $button-bg-color;
|
||||||
background-color: $primary-bg-color;
|
color: $button-fg-color;
|
||||||
}
|
|
||||||
|
|
||||||
.mx_AppPermissionButton:hover {
|
|
||||||
background-color: $primary-fg-color;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
33
res/css/views/rooms/_E2EIcon.scss
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
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_E2EIcon {
|
||||||
|
width: 25px;
|
||||||
|
height: 25px;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
mask-position: center 0;
|
||||||
|
margin: 0 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_E2EIcon_verified {
|
||||||
|
mask-image: url('$(res)/img/feather-icons/e2e/lock-verified.svg');
|
||||||
|
background-color: $accent-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_E2EIcon_warning {
|
||||||
|
mask-image: url('$(res)/img/feather-icons/e2e/lock-warning.svg');
|
||||||
|
background-color: $warning-color;
|
||||||
|
}
|
|
@ -281,9 +281,24 @@ limitations under the License.
|
||||||
.mx_EventTile_e2eIcon {
|
.mx_EventTile_e2eIcon {
|
||||||
display: block;
|
display: block;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 9px;
|
top: 8px;
|
||||||
left: 46px;
|
left: 46px;
|
||||||
|
width: 15px;
|
||||||
|
height: 15px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
mask-size: 14px;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
mask-position: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_EventTile_e2eIcon_undecryptable, .mx_EventTile_e2eIcon_unverified {
|
||||||
|
mask-image: url('$(res)/img/feather-icons/e2e/warning.svg');
|
||||||
|
background-color: $warning-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_EventTile_e2eIcon_unencrypted {
|
||||||
|
mask-image: url('$(res)/img/feather-icons/e2e/warning.svg');
|
||||||
|
background-color: $composer-e2e-icon-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_EventTile_e2eIcon_hidden {
|
.mx_EventTile_e2eIcon_hidden {
|
||||||
|
|
|
@ -20,6 +20,25 @@ limitations under the License.
|
||||||
align-items: start;
|
align-items: start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_MemberDeviceInfo_icon {
|
||||||
|
margin-top: 4px;
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
.mx_MemberDeviceInfo_icon_blacklisted {
|
||||||
|
mask-image: url('$(res)/img/feather-icons/e2e/blacklisted.svg');
|
||||||
|
background-color: $warning-color;
|
||||||
|
}
|
||||||
|
.mx_MemberDeviceInfo_icon_verified {
|
||||||
|
mask-image: url('$(res)/img/feather-icons/e2e/verified.svg');
|
||||||
|
background-color: $accent-color;
|
||||||
|
}
|
||||||
|
.mx_MemberDeviceInfo_icon_unverified {
|
||||||
|
mask-image: url('$(res)/img/feather-icons/e2e/warning.svg');
|
||||||
|
background-color: $warning-color;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_MemberDeviceInfo > .mx_DeviceVerifyButtons {
|
.mx_MemberDeviceInfo > .mx_DeviceVerifyButtons {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
|
@ -26,6 +26,10 @@ limitations under the License.
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_MemberInfo_name > .mx_E2EIcon {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_MemberInfo_cancel {
|
.mx_MemberInfo_cancel {
|
||||||
height: 16px;
|
height: 16px;
|
||||||
padding: 10px 15px;
|
padding: 10px 15px;
|
||||||
|
|
|
@ -23,6 +23,10 @@ limitations under the License.
|
||||||
padding-left: 84px;
|
padding-left: 84px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_MessageComposer_wrapper.mx_MessageComposer_hasE2EIcon {
|
||||||
|
padding-left: 109px;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_MessageComposer_replaced_wrapper {
|
.mx_MessageComposer_replaced_wrapper {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
|
@ -71,9 +75,10 @@ limitations under the License.
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_MessageComposer_e2eIcon {
|
.mx_MessageComposer_e2eIcon.mx_E2EIcon {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 60px;
|
left: 60px;
|
||||||
|
background-color: $composer-e2e-icon-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_MessageComposer_noperm_error {
|
.mx_MessageComposer_noperm_error {
|
||||||
|
|
|
@ -38,7 +38,6 @@ limitations under the License.
|
||||||
margin: 0 10px;
|
margin: 0 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomRecoveryReminder_button.mx_RoomRecoveryReminder_secondary {
|
.mx_RoomRecoveryReminder_secondary {
|
||||||
@mixin mx_DialogButton_secondary;
|
font-size: 90%;
|
||||||
background-color: transparent;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -59,10 +59,15 @@ limitations under the License.
|
||||||
color: $accent-color;
|
color: $accent-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_UserSettings_devicesTable td {
|
.mx_UserNotifSettings_devicesTable td {
|
||||||
padding-left: 20px;
|
padding-left: 20px;
|
||||||
padding-right: 20px;
|
padding-right: 20px;
|
||||||
}
|
}
|
||||||
.mx_UserSettings_devicesTable_nodevices {
|
|
||||||
font-style: italic;
|
.mx_UserNotifSettings_notifTable {
|
||||||
|
display: table;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_UserNotifSettings_notifTable .mx_Spinner {
|
||||||
|
position: absolute;
|
||||||
}
|
}
|
||||||
|
|
48
res/css/views/verification/_VerificationShowSas.scss
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
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_VerificationShowSas_decimalSas {
|
||||||
|
text-align: center;
|
||||||
|
font-weight: bold;
|
||||||
|
padding-left: 3px;
|
||||||
|
padding-right: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_VerificationShowSas_decimalSas span {
|
||||||
|
margin-left: 5px;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_VerificationShowSas_emojiSas {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_VerificationShowSas_emojiSas_block {
|
||||||
|
display: inline-block;
|
||||||
|
margin-left: 15px;
|
||||||
|
margin-right: 15px;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_VerificationShowSas_emojiSas_emoji {
|
||||||
|
font-size: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_VerificationShowSas_emojiSas_label {
|
||||||
|
text-align: center;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
BIN
res/img/03b381.png
Normal file
After Width: | Height: | Size: 169 B |
BIN
res/img/368bd6.png
Normal file
After Width: | Height: | Size: 171 B |
Before Width: | Height: | Size: 146 B |
Before Width: | Height: | Size: 966 B |
Before Width: | Height: | Size: 146 B |
BIN
res/img/ac3ba8.png
Normal file
After Width: | Height: | Size: 170 B |
Before Width: | Height: | Size: 146 B |
6
res/img/feather-icons/e2e/blacklisted.svg
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="12" height="12" viewBox="0 0 12 12">
|
||||||
|
<defs>
|
||||||
|
<path id="a" d="M5 10A5 5 0 1 0 5 0a5 5 0 0 0 0 10zM2.5 3.5h5a1.5 1.5 0 0 1 0 3h-5a1.5 1.5 0 0 1 0-3z"/>
|
||||||
|
</defs>
|
||||||
|
<use fill="#F56679" fill-rule="evenodd" stroke="#F56679" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.2" transform="translate(1 1)" xlink:href="#a"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 442 B |
6
res/img/feather-icons/e2e/lock-verified.svg
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="25" viewBox="0 0 22 25">
|
||||||
|
<g fill="none" fill-rule="evenodd" stroke="#7AC9A1" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path stroke-width="2" d="M8.23 21.01l-5.233-.007a1.995 1.995 0 0 1-1.997-2V11a2 2 0 0 1 2-2h14c1.259 0 2 .939 2 1M5 9V6a5 5 0 1 1 10 0v3"/>
|
||||||
|
<path fill="#7AC9A1" d="M15.5 24s5.5-2.4 5.5-6v-4.2L15.5 12 10 13.8V18c0 3.6 5.5 6 5.5 6z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 454 B |
9
res/img/feather-icons/e2e/lock-warning.svg
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="23" height="25" viewBox="0 0 23 25">
|
||||||
|
<defs>
|
||||||
|
<path id="a" d="M15 23a6 6 0 1 0 0-12 6 6 0 0 0 0 12zm0-10.5a1.5 1.5 0 0 1 1.5 1.5v3a1.5 1.5 0 0 1-3 0v-3a1.5 1.5 0 0 1 1.5-1.5zm0 9a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3z"/>
|
||||||
|
</defs>
|
||||||
|
<g fill="none" fill-rule="evenodd" stroke="#F56679" stroke-linecap="round" stroke-linejoin="round" transform="translate(1 1)">
|
||||||
|
<path stroke-width="2" d="M7.23 20.01l-5.233-.007a2 2 0 0 1-1.997-2V10a2 2 0 0 1 2-2h14c1.259 0 2 .939 2 1M4 8V5a5 5 0 1 1 10 0v3"/>
|
||||||
|
<use fill="#F56679" xlink:href="#a"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 665 B |
3
res/img/feather-icons/e2e/verified.svg
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="11" height="12" viewBox="0 0 11 12">
|
||||||
|
<path fill="#7AC9A1" fill-rule="evenodd" stroke="#7AC9A1" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.2" d="M5.5 11S10 9 10 6V2.5L5.5 1 1 2.5V6c0 3 4.5 5 4.5 5z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 278 B |
6
res/img/feather-icons/e2e/warning.svg
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="12" height="12" viewBox="0 0 12 12">
|
||||||
|
<defs>
|
||||||
|
<path id="a" d="M5 10A5 5 0 1 0 5 0a5 5 0 0 0 0 10zM5 .5A1.5 1.5 0 0 1 6.5 2v3a1.5 1.5 0 0 1-3 0V2A1.5 1.5 0 0 1 5 .5zm0 9a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3z"/>
|
||||||
|
</defs>
|
||||||
|
<use fill="#F56679" fill-rule="evenodd" stroke="#F56679" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.2" transform="translate(1 1)" xlink:href="#a"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 500 B |
1
res/img/feather-icons/home.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-home"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path><polyline points="9 22 9 12 15 12 15 22"></polyline></svg>
|
After Width: | Height: | Size: 332 B |
65
res/img/feather-icons/widget/bin.svg
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="11.014242"
|
||||||
|
height="12"
|
||||||
|
viewBox="0 0 11.014242 12"
|
||||||
|
version="1.1"
|
||||||
|
id="svg6"
|
||||||
|
sodipodi:docname="bin.svg"
|
||||||
|
inkscape:version="0.92.3 (2405546, 2018-03-11)">
|
||||||
|
<metadata
|
||||||
|
id="metadata12">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs10" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="750"
|
||||||
|
inkscape:window-height="480"
|
||||||
|
id="namedview8"
|
||||||
|
showgrid="false"
|
||||||
|
fit-margin-top="0.5"
|
||||||
|
fit-margin-left="0.5"
|
||||||
|
fit-margin-bottom="0.5"
|
||||||
|
fit-margin-right="0.5"
|
||||||
|
inkscape:zoom="19.666667"
|
||||||
|
inkscape:cx="5.5071212"
|
||||||
|
inkscape:cy="6"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="27"
|
||||||
|
inkscape:window-maximized="0"
|
||||||
|
inkscape:current-layer="svg6" />
|
||||||
|
<g
|
||||||
|
id="g4"
|
||||||
|
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
|
||||||
|
transform="translate(1.0071212)">
|
||||||
|
<path
|
||||||
|
d="M 0,3 H 9 M 8,3 v 7 A 1,1 0 0 1 7,11 H 2 A 1,1 0 0 1 1,10 V 3 M 2.5,3 V 2 a 1,1 0 0 1 1,-1 h 2 a 1,1 0 0 1 1,1 v 1 m -3,2.5 v 3 m 2,-3 v 3"
|
||||||
|
id="path2"
|
||||||
|
style="stroke:#000000;stroke-opacity:1"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2 KiB |
6
res/img/feather-icons/widget/camera.svg
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="11" viewBox="0 0 13 11">
|
||||||
|
<g fill="none" fill-rule="evenodd" stroke="#212121" stroke-linecap="round" stroke-linejoin="round" transform="translate(1 1)">
|
||||||
|
<path d="M11 8a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1V2.5a1 1 0 0 1 1-1h2L4 0h3l1 1.5h2a1 1 0 0 1 1 1V8z"/>
|
||||||
|
<circle cx="5.5" cy="5" r="2"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 378 B |
6
res/img/feather-icons/widget/edit.svg
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="11" viewBox="0 0 12 11">
|
||||||
|
<g fill="none" fill-rule="evenodd" stroke="#212121" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path d="M10 6.33V9a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h2.67"/>
|
||||||
|
<path d="M9 0l2 2-5 5H4V5z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 324 B |
5
res/img/feather-icons/widget/external-link.svg
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="11" height="11" viewBox="0 0 11 11">
|
||||||
|
<g fill="none" fill-rule="evenodd" stroke="#212121" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path d="M8.5 6v3a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V3.5a1 1 0 0 1 1-1h3M7 1h3v3M4.5 6.5L10 1"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 304 B |
63
res/img/feather-icons/widget/maximise.svg
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="11"
|
||||||
|
height="11"
|
||||||
|
viewBox="0 0 11 11"
|
||||||
|
version="1.1"
|
||||||
|
id="svg6"
|
||||||
|
sodipodi:docname="maximise.svg"
|
||||||
|
inkscape:version="0.92.3 (2405546, 2018-03-11)">
|
||||||
|
<metadata
|
||||||
|
id="metadata12">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs10" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="750"
|
||||||
|
inkscape:window-height="480"
|
||||||
|
id="namedview8"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="21.454545"
|
||||||
|
inkscape:cx="5.5"
|
||||||
|
inkscape:cy="5.5"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="27"
|
||||||
|
inkscape:window-maximized="0"
|
||||||
|
inkscape:current-layer="svg6" />
|
||||||
|
<g
|
||||||
|
fill="none"
|
||||||
|
fill-rule="evenodd"
|
||||||
|
stroke="#7AC9A1"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
id="g4"
|
||||||
|
style="stroke:#000000;stroke-opacity:1">
|
||||||
|
<path
|
||||||
|
d="M7 1h3v3M4 10H1V7M10 1L6.5 4.5M1 10l3.5-3.5"
|
||||||
|
id="path2"
|
||||||
|
style="stroke:#000000;stroke-opacity:1" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.7 KiB |
65
res/img/feather-icons/widget/minimise.svg
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="10.2101"
|
||||||
|
height="10.2101"
|
||||||
|
viewBox="0 0 10.2101 10.210099"
|
||||||
|
version="1.1"
|
||||||
|
id="svg6"
|
||||||
|
sodipodi:docname="minimise.svg"
|
||||||
|
inkscape:version="0.92.3 (2405546, 2018-03-11)">
|
||||||
|
<metadata
|
||||||
|
id="metadata12">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs10" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1016"
|
||||||
|
id="namedview8"
|
||||||
|
showgrid="false"
|
||||||
|
fit-margin-top="0.1"
|
||||||
|
fit-margin-left="0.1"
|
||||||
|
fit-margin-right="0.1"
|
||||||
|
fit-margin-bottom="0.1"
|
||||||
|
inkscape:zoom="26.222222"
|
||||||
|
inkscape:cx="-1.5686788"
|
||||||
|
inkscape:cy="5.0287789"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="27"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg6" />
|
||||||
|
<g
|
||||||
|
id="g4"
|
||||||
|
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
|
||||||
|
transform="translate(-0.39494988,0.60505012)">
|
||||||
|
<path
|
||||||
|
d="m 1.5,5.5 h 3 v 3 m 5,-5 h -3 v -3 m 0,3 L 10,0 M 1,9 4.5,5.5"
|
||||||
|
id="path2"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="stroke:#000000;stroke-opacity:1" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.9 KiB |
6
res/img/feather-icons/widget/refresh.svg
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="11" viewBox="0 0 13 11">
|
||||||
|
<g fill="none" fill-rule="evenodd" stroke="#212121" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<path d="M12 1.5v3H9M1 9.5v-3h3"/>
|
||||||
|
<path d="M2.255 4A4.5 4.5 0 0 1 9.68 2.32L12 4.5m-11 2l2.32 2.18A4.5 4.5 0 0 0 10.745 7"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 346 B |
6
res/img/feather-icons/widget/x-circle.svg
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12">
|
||||||
|
<g fill="none" fill-rule="evenodd" stroke="#212121" stroke-linecap="round" stroke-linejoin="round" transform="translate(1 1)">
|
||||||
|
<circle cx="5" cy="5" r="5"/>
|
||||||
|
<path d="M6.5 3.5l-3 3M3.5 3.5l3 3"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 315 B |
|
@ -142,6 +142,8 @@ $roomheader-addroom-color: #91A1C0;
|
||||||
$roomtopic-color: #9fa9ba;
|
$roomtopic-color: #9fa9ba;
|
||||||
$eventtile-meta-color: $roomtopic-color;
|
$eventtile-meta-color: $roomtopic-color;
|
||||||
|
|
||||||
|
$composer-e2e-icon-color: #c9ced6;
|
||||||
|
|
||||||
// ********************
|
// ********************
|
||||||
|
|
||||||
$roomtile-name-color: #61708b;
|
$roomtile-name-color: #61708b;
|
||||||
|
@ -170,7 +172,7 @@ $panel-divider-color: #dee1f3;
|
||||||
|
|
||||||
// ********************
|
// ********************
|
||||||
|
|
||||||
$widget-menu-bar-bg-color: $tertiary-accent-color;
|
$widget-menu-bar-bg-color: $secondary-accent-color;
|
||||||
|
|
||||||
// ********************
|
// ********************
|
||||||
|
|
||||||
|
|
|
@ -134,6 +134,9 @@ $roomheader-color: $primary-fg-color;
|
||||||
$roomheader-addroom-color: $primary-bg-color;
|
$roomheader-addroom-color: $primary-bg-color;
|
||||||
$roomtopic-color: $settings-grey-fg-color;
|
$roomtopic-color: $settings-grey-fg-color;
|
||||||
$eventtile-meta-color: $roomtopic-color;
|
$eventtile-meta-color: $roomtopic-color;
|
||||||
|
|
||||||
|
$composer-e2e-icon-color: #c9ced6;
|
||||||
|
|
||||||
// ********************
|
// ********************
|
||||||
|
|
||||||
$roomtile-name-color: rgba(69, 69, 69, 0.8);
|
$roomtile-name-color: rgba(69, 69, 69, 0.8);
|
||||||
|
|
|
@ -19,7 +19,7 @@ import MatrixClientPeg from './MatrixClientPeg';
|
||||||
import { _t } from './languageHandler';
|
import { _t } from './languageHandler';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows a user to add a third party identifier to their Home Server and,
|
* Allows a user to add a third party identifier to their homeserver and,
|
||||||
* optionally, the identity servers.
|
* optionally, the identity servers.
|
||||||
*
|
*
|
||||||
* This involves getting an email token from the identity server to "prove" that
|
* This involves getting an email token from the identity server to "prove" that
|
||||||
|
|
|
@ -267,7 +267,7 @@ class Analytics {
|
||||||
const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog');
|
const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog');
|
||||||
Modal.createTrackedDialog('Analytics Details', '', ErrorDialog, {
|
Modal.createTrackedDialog('Analytics Details', '', ErrorDialog, {
|
||||||
title: _t('Analytics'),
|
title: _t('Analytics'),
|
||||||
description: <div className="mx_UserSettings_analyticsModal">
|
description: <div className="mx_AnalyticsModal">
|
||||||
<div>
|
<div>
|
||||||
{ _t('The information being sent to us to help make Riot.im better includes:') }
|
{ _t('The information being sent to us to help make Riot.im better includes:') }
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -51,7 +51,7 @@ module.exports = {
|
||||||
},
|
},
|
||||||
|
|
||||||
defaultAvatarUrlForString: function(s) {
|
defaultAvatarUrlForString: function(s) {
|
||||||
const images = ['76cfa6', '50e2c2', 'f4c371'];
|
const images = ['03b381', '368bd6', 'ac3ba8'];
|
||||||
let total = 0;
|
let total = 0;
|
||||||
for (let i = 0; i < s.length; ++i) {
|
for (let i = 0; i < s.length; ++i) {
|
||||||
total += s.charCodeAt(i);
|
total += s.charCodeAt(i);
|
||||||
|
|
|
@ -19,16 +19,21 @@ limitations under the License.
|
||||||
|
|
||||||
import ReplyThread from "./components/views/elements/ReplyThread";
|
import ReplyThread from "./components/views/elements/ReplyThread";
|
||||||
|
|
||||||
const React = require('react');
|
import React from 'react';
|
||||||
const sanitizeHtml = require('sanitize-html');
|
import sanitizeHtml from 'sanitize-html';
|
||||||
const highlight = require('highlight.js');
|
import highlight from 'highlight.js';
|
||||||
const linkifyMatrix = require('./linkify-matrix');
|
import * as linkify from 'linkifyjs';
|
||||||
|
import linkifyMatrix from './linkify-matrix';
|
||||||
|
import _linkifyElement from 'linkifyjs/element';
|
||||||
|
import _linkifyString from 'linkifyjs/string';
|
||||||
import escape from 'lodash/escape';
|
import escape from 'lodash/escape';
|
||||||
import emojione from 'emojione';
|
import emojione from 'emojione';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import MatrixClientPeg from './MatrixClientPeg';
|
import MatrixClientPeg from './MatrixClientPeg';
|
||||||
import url from 'url';
|
import url from 'url';
|
||||||
|
|
||||||
|
linkifyMatrix(linkify);
|
||||||
|
|
||||||
emojione.imagePathSVG = 'emojione/svg/';
|
emojione.imagePathSVG = 'emojione/svg/';
|
||||||
// Store PNG path for displaying many flags at once (for increased performance over SVG)
|
// Store PNG path for displaying many flags at once (for increased performance over SVG)
|
||||||
emojione.imagePathPNG = 'emojione/png/';
|
emojione.imagePathPNG = 'emojione/png/';
|
||||||
|
@ -63,8 +68,10 @@ export function containsEmoji(str) {
|
||||||
/* modified from https://github.com/Ranks/emojione/blob/master/lib/js/emojione.js
|
/* modified from https://github.com/Ranks/emojione/blob/master/lib/js/emojione.js
|
||||||
* because we want to include emoji shortnames in title text
|
* because we want to include emoji shortnames in title text
|
||||||
*/
|
*/
|
||||||
function unicodeToImage(str) {
|
function unicodeToImage(str, addAlt) {
|
||||||
let replaceWith; let unicode; let alt; let short; let fname;
|
if (addAlt === undefined) addAlt = true;
|
||||||
|
|
||||||
|
let replaceWith; let unicode; let short; let fname;
|
||||||
const mappedUnicode = emojione.mapUnicodeToShort();
|
const mappedUnicode = emojione.mapUnicodeToShort();
|
||||||
|
|
||||||
str = str.replace(emojione.regUnicode, function(unicodeChar) {
|
str = str.replace(emojione.regUnicode, function(unicodeChar) {
|
||||||
|
@ -79,10 +86,14 @@ function unicodeToImage(str) {
|
||||||
fname = emojione.emojioneList[short].fname;
|
fname = emojione.emojioneList[short].fname;
|
||||||
|
|
||||||
// depending on the settings, we'll either add the native unicode as the alt tag, otherwise the shortname
|
// depending on the settings, we'll either add the native unicode as the alt tag, otherwise the shortname
|
||||||
alt = (emojione.unicodeAlt) ? emojione.convert(unicode.toUpperCase()) : mappedUnicode[unicode];
|
|
||||||
const title = mappedUnicode[unicode];
|
const title = mappedUnicode[unicode];
|
||||||
|
|
||||||
|
if (addAlt) {
|
||||||
|
const alt = (emojione.unicodeAlt) ? emojione.convert(unicode.toUpperCase()) : mappedUnicode[unicode];
|
||||||
replaceWith = `<img class="mx_emojione" title="${title}" alt="${alt}" src="${emojione.imagePathSVG}${fname}.svg${emojione.cacheBustParam}"/>`;
|
replaceWith = `<img class="mx_emojione" title="${title}" alt="${alt}" src="${emojione.imagePathSVG}${fname}.svg${emojione.cacheBustParam}"/>`;
|
||||||
|
} else {
|
||||||
|
replaceWith = `<img class="mx_emojione" src="${emojione.imagePathSVG}${fname}.svg${emojione.cacheBustParam}"/>`;
|
||||||
|
}
|
||||||
return replaceWith;
|
return replaceWith;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -503,8 +514,39 @@ export function bodyToHtml(content, highlights, opts={}) {
|
||||||
<span className={className} dir="auto">{ strippedBody }</span>;
|
<span className={className} dir="auto">{ strippedBody }</span>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function emojifyText(text) {
|
export function emojifyText(text, addAlt) {
|
||||||
return {
|
return {
|
||||||
__html: unicodeToImage(escape(text)),
|
__html: unicodeToImage(escape(text), addAlt),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Linkifies the given string. This is a wrapper around 'linkifyjs/string'.
|
||||||
|
*
|
||||||
|
* @param {string} str
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export function linkifyString(str) {
|
||||||
|
return _linkifyString(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Linkifies the given DOM element. This is a wrapper around 'linkifyjs/element'.
|
||||||
|
*
|
||||||
|
* @param {object} element DOM element to linkify
|
||||||
|
* @param {object} [options] Options for linkifyElement. Default: linkifyMatrix.options
|
||||||
|
* @returns {object}
|
||||||
|
*/
|
||||||
|
export function linkifyElement(element, options = linkifyMatrix.options) {
|
||||||
|
return _linkifyElement(element, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Linkify the given string and sanitize the HTML afterwards.
|
||||||
|
*
|
||||||
|
* @param {string} dirtyHtml The HTML string to sanitize and linkify
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export function linkifyAndSanitizeHtml(dirtyHtml) {
|
||||||
|
return sanitizeHtml(linkifyString(dirtyHtml), sanitizeHtmlParams);
|
||||||
|
}
|
||||||
|
|
|
@ -85,7 +85,7 @@ class MatrixClientPeg {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replace this MatrixClientPeg's client with a client instance that has
|
* Replace this MatrixClientPeg's client with a client instance that has
|
||||||
* Home Server / Identity Server URLs and active credentials
|
* homeserver / identity server URLs and active credentials
|
||||||
*/
|
*/
|
||||||
replaceUsingCreds(creds: MatrixClientCreds) {
|
replaceUsingCreds(creds: MatrixClientCreds) {
|
||||||
this._currentClientCreds = creds;
|
this._currentClientCreds = creds;
|
||||||
|
@ -135,14 +135,7 @@ class MatrixClientPeg {
|
||||||
const opts = utils.deepCopy(this.opts);
|
const opts = utils.deepCopy(this.opts);
|
||||||
// the react sdk doesn't work without this, so don't allow
|
// the react sdk doesn't work without this, so don't allow
|
||||||
opts.pendingEventOrdering = "detached";
|
opts.pendingEventOrdering = "detached";
|
||||||
|
|
||||||
const LAZY_LOADING_FEATURE = "feature_lazyloading";
|
|
||||||
if (SettingsStore.isFeatureEnabled(LAZY_LOADING_FEATURE)) {
|
|
||||||
const userId = this.matrixClient.credentials.userId;
|
|
||||||
if (phasedRollOutExpiredForUser(userId, LAZY_LOADING_FEATURE, Date.now())) {
|
|
||||||
opts.lazyLoadMembers = true;
|
opts.lazyLoadMembers = true;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Connect the matrix client to the dispatcher
|
// Connect the matrix client to the dispatcher
|
||||||
MatrixActionCreators.start(this.matrixClient);
|
MatrixActionCreators.start(this.matrixClient);
|
||||||
|
|
|
@ -19,7 +19,6 @@ limitations under the License.
|
||||||
export default {
|
export default {
|
||||||
HomePage: "home_page",
|
HomePage: "home_page",
|
||||||
RoomView: "room_view",
|
RoomView: "room_view",
|
||||||
UserSettings: "user_settings",
|
|
||||||
RoomDirectory: "room_directory",
|
RoomDirectory: "room_directory",
|
||||||
UserView: "user_view",
|
UserView: "user_view",
|
||||||
GroupView: "group_view",
|
GroupView: "group_view",
|
||||||
|
|
|
@ -33,7 +33,7 @@ class Presence {
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Start listening the user activity to evaluate his presence state.
|
* Start listening the user activity to evaluate his presence state.
|
||||||
* Any state change will be sent to the Home Server.
|
* Any state change will be sent to the homeserver.
|
||||||
*/
|
*/
|
||||||
async start() {
|
async start() {
|
||||||
this._unavailableTimer = new Timer(UNAVAILABLE_TIME_MS);
|
this._unavailableTimer = new Timer(UNAVAILABLE_TIME_MS);
|
||||||
|
@ -78,7 +78,7 @@ class Presence {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the presence state.
|
* Set the presence state.
|
||||||
* If the state has changed, the Home Server will be notified.
|
* If the state has changed, the homeserver will be notified.
|
||||||
* @param {string} newState the new presence state (see PRESENCE enum)
|
* @param {string} newState the new presence state (see PRESENCE enum)
|
||||||
*/
|
*/
|
||||||
async setState(newState) {
|
async setState(newState) {
|
||||||
|
|
|
@ -35,8 +35,10 @@ export const SAFE_LOCALPART_REGEX = /^[a-z0-9=_\-./]+$/;
|
||||||
* on what the HS supports
|
* on what the HS supports
|
||||||
*
|
*
|
||||||
* @param {object} options
|
* @param {object} options
|
||||||
* @param {bool} options.go_home_on_cancel If true, goes to
|
* @param {bool} options.go_home_on_cancel
|
||||||
* the hame page if the user cancels the action
|
* If true, goes to the home page if the user cancels the action
|
||||||
|
* @param {bool} options.go_welcome_on_cancel
|
||||||
|
* If true, goes to the welcome page if the user cancels the action
|
||||||
*/
|
*/
|
||||||
export async function startAnyRegistrationFlow(options) {
|
export async function startAnyRegistrationFlow(options) {
|
||||||
if (options === undefined) options = {};
|
if (options === undefined) options = {};
|
||||||
|
@ -73,6 +75,8 @@ export async function startAnyRegistrationFlow(options) {
|
||||||
dis.dispatch({action: 'start_registration'});
|
dis.dispatch({action: 'start_registration'});
|
||||||
} else if (options.go_home_on_cancel) {
|
} else if (options.go_home_on_cancel) {
|
||||||
dis.dispatch({action: 'view_home_page'});
|
dis.dispatch({action: 'view_home_page'});
|
||||||
|
} else if (options.go_welcome_on_cancel) {
|
||||||
|
dis.dispatch({action: 'view_welcome_page'});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -23,6 +23,47 @@ export const ALL_MESSAGES = 'all_messages';
|
||||||
export const MENTIONS_ONLY = 'mentions_only';
|
export const MENTIONS_ONLY = 'mentions_only';
|
||||||
export const MUTE = 'mute';
|
export const MUTE = 'mute';
|
||||||
|
|
||||||
|
|
||||||
|
function _shouldShowNotifBadge(roomNotifState) {
|
||||||
|
const showBadgeInStates = [ALL_MESSAGES, ALL_MESSAGES_LOUD];
|
||||||
|
return showBadgeInStates.indexOf(roomNotifState) > -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _shouldShowMentionBadge(roomNotifState) {
|
||||||
|
return roomNotifState !== MUTE;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function aggregateNotificationCount(rooms) {
|
||||||
|
return rooms.reduce((result, room, index) => {
|
||||||
|
const roomNotifState = getRoomNotifsState(room.roomId);
|
||||||
|
const highlight = room.getUnreadNotificationCount('highlight') > 0;
|
||||||
|
const notificationCount = room.getUnreadNotificationCount();
|
||||||
|
|
||||||
|
const notifBadges = notificationCount > 0 && _shouldShowNotifBadge(roomNotifState);
|
||||||
|
const mentionBadges = highlight && _shouldShowMentionBadge(roomNotifState);
|
||||||
|
const badges = notifBadges || mentionBadges;
|
||||||
|
|
||||||
|
if (badges) {
|
||||||
|
result.count += notificationCount;
|
||||||
|
if (highlight) {
|
||||||
|
result.highlight = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}, {count: 0, highlight: false});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getRoomHasBadge(room) {
|
||||||
|
const roomNotifState = getRoomNotifsState(room.roomId);
|
||||||
|
const highlight = room.getUnreadNotificationCount('highlight') > 0;
|
||||||
|
const notificationCount = room.getUnreadNotificationCount();
|
||||||
|
|
||||||
|
const notifBadges = notificationCount > 0 && _shouldShowNotifBadge(roomNotifState);
|
||||||
|
const mentionBadges = highlight && _shouldShowMentionBadge(roomNotifState);
|
||||||
|
|
||||||
|
return notifBadges || mentionBadges;
|
||||||
|
}
|
||||||
|
|
||||||
export function getRoomNotifsState(roomId) {
|
export function getRoomNotifsState(roomId) {
|
||||||
if (MatrixClientPeg.get().isGuest()) return ALL_MESSAGES;
|
if (MatrixClientPeg.get().isGuest()) return ALL_MESSAGES;
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ import SettingsStore, {SettingLevel} from './settings/SettingsStore';
|
||||||
import {MATRIXTO_URL_PATTERN} from "./linkify-matrix";
|
import {MATRIXTO_URL_PATTERN} from "./linkify-matrix";
|
||||||
import * as querystring from "querystring";
|
import * as querystring from "querystring";
|
||||||
import MultiInviter from './utils/MultiInviter';
|
import MultiInviter from './utils/MultiInviter';
|
||||||
|
import { linkifyAndSanitizeHtml } from './HtmlUtils';
|
||||||
|
|
||||||
class Command {
|
class Command {
|
||||||
constructor({name, args='', description, runFn, hideCompletionAfterSpace=false}) {
|
constructor({name, args='', description, runFn, hideCompletionAfterSpace=false}) {
|
||||||
|
@ -137,13 +137,26 @@ export const CommandMap = {
|
||||||
|
|
||||||
topic: new Command({
|
topic: new Command({
|
||||||
name: 'topic',
|
name: 'topic',
|
||||||
args: '<topic>',
|
args: '[<topic>]',
|
||||||
description: _td('Sets the room topic'),
|
description: _td('Gets or sets the room topic'),
|
||||||
runFn: function(roomId, args) {
|
runFn: function(roomId, args) {
|
||||||
|
const cli = MatrixClientPeg.get();
|
||||||
if (args) {
|
if (args) {
|
||||||
return success(MatrixClientPeg.get().setRoomTopic(roomId, args));
|
return success(cli.setRoomTopic(roomId, args));
|
||||||
}
|
}
|
||||||
return reject(this.getUsage());
|
const room = cli.getRoom(roomId);
|
||||||
|
if (!room) return reject('Bad room ID: ' + roomId);
|
||||||
|
|
||||||
|
const topicEvents = room.currentState.getStateEvents('m.room.topic', '');
|
||||||
|
const topic = topicEvents && topicEvents.getContent().topic;
|
||||||
|
const topicHtml = topic ? linkifyAndSanitizeHtml(topic) : _t('This room has no topic.');
|
||||||
|
|
||||||
|
const InfoDialog = sdk.getComponent('dialogs.InfoDialog');
|
||||||
|
Modal.createTrackedDialog('Slash Commands', 'Topic', InfoDialog, {
|
||||||
|
title: room.name,
|
||||||
|
description: <div dangerouslySetInnerHTML={{ __html: topicHtml }} />,
|
||||||
|
});
|
||||||
|
return success();
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
@ -391,13 +404,12 @@ export const CommandMap = {
|
||||||
ignoredUsers.push(userId); // de-duped internally in the js-sdk
|
ignoredUsers.push(userId); // de-duped internally in the js-sdk
|
||||||
return success(
|
return success(
|
||||||
cli.setIgnoredUsers(ignoredUsers).then(() => {
|
cli.setIgnoredUsers(ignoredUsers).then(() => {
|
||||||
const QuestionDialog = sdk.getComponent('dialogs.QuestionDialog');
|
const InfoDialog = sdk.getComponent('dialogs.InfoDialog');
|
||||||
Modal.createTrackedDialog('Slash Commands', 'User ignored', QuestionDialog, {
|
Modal.createTrackedDialog('Slash Commands', 'User ignored', InfoDialog, {
|
||||||
title: _t('Ignored user'),
|
title: _t('Ignored user'),
|
||||||
description: <div>
|
description: <div>
|
||||||
<p>{ _t('You are now ignoring %(userId)s', {userId}) }</p>
|
<p>{ _t('You are now ignoring %(userId)s', {userId}) }</p>
|
||||||
</div>,
|
</div>,
|
||||||
hasCancelButton: false,
|
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
@ -423,13 +435,12 @@ export const CommandMap = {
|
||||||
if (index !== -1) ignoredUsers.splice(index, 1);
|
if (index !== -1) ignoredUsers.splice(index, 1);
|
||||||
return success(
|
return success(
|
||||||
cli.setIgnoredUsers(ignoredUsers).then(() => {
|
cli.setIgnoredUsers(ignoredUsers).then(() => {
|
||||||
const QuestionDialog = sdk.getComponent('dialogs.QuestionDialog');
|
const InfoDialog = sdk.getComponent('dialogs.InfoDialog');
|
||||||
Modal.createTrackedDialog('Slash Commands', 'User unignored', QuestionDialog, {
|
Modal.createTrackedDialog('Slash Commands', 'User unignored', InfoDialog, {
|
||||||
title: _t('Unignored user'),
|
title: _t('Unignored user'),
|
||||||
description: <div>
|
description: <div>
|
||||||
<p>{ _t('You are no longer ignoring %(userId)s', {userId}) }</p>
|
<p>{ _t('You are no longer ignoring %(userId)s', {userId}) }</p>
|
||||||
</div>,
|
</div>,
|
||||||
hasCancelButton: false,
|
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
@ -546,8 +557,8 @@ export const CommandMap = {
|
||||||
return cli.setDeviceVerified(userId, deviceId, true);
|
return cli.setDeviceVerified(userId, deviceId, true);
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
// Tell the user we verified everything
|
// Tell the user we verified everything
|
||||||
const QuestionDialog = sdk.getComponent('dialogs.QuestionDialog');
|
const InfoDialog = sdk.getComponent('dialogs.InfoDialog');
|
||||||
Modal.createTrackedDialog('Slash Commands', 'Verified key', QuestionDialog, {
|
Modal.createTrackedDialog('Slash Commands', 'Verified key', InfoDialog, {
|
||||||
title: _t('Verified key'),
|
title: _t('Verified key'),
|
||||||
description: <div>
|
description: <div>
|
||||||
<p>
|
<p>
|
||||||
|
@ -558,7 +569,6 @@ export const CommandMap = {
|
||||||
}
|
}
|
||||||
</p>
|
</p>
|
||||||
</div>,
|
</div>,
|
||||||
hasCancelButton: false,
|
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
|
@ -134,6 +134,38 @@ function textForTombstoneEvent(ev) {
|
||||||
return _t('%(senderDisplayName)s upgraded this room.', {senderDisplayName});
|
return _t('%(senderDisplayName)s upgraded this room.', {senderDisplayName});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function textForJoinRulesEvent(ev) {
|
||||||
|
const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
|
||||||
|
switch (ev.getContent().join_rule) {
|
||||||
|
case "public":
|
||||||
|
return _t('%(senderDisplayName)s made the room public to whoever knows the link.', {senderDisplayName});
|
||||||
|
case "invite":
|
||||||
|
return _t('%(senderDisplayName)s made the room invite only.', {senderDisplayName});
|
||||||
|
default:
|
||||||
|
// The spec supports "knock" and "private", however nothing implements these.
|
||||||
|
return _t('%(senderDisplayName)s changed the join rule to %(rule)s', {
|
||||||
|
senderDisplayName,
|
||||||
|
rule: ev.getContent().join_rule,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function textForGuestAccessEvent(ev) {
|
||||||
|
const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
|
||||||
|
switch (ev.getContent().guest_access) {
|
||||||
|
case "can_join":
|
||||||
|
return _t('%(senderDisplayName)s has allowed guests to join the room.', {senderDisplayName});
|
||||||
|
case "forbidden":
|
||||||
|
return _t('%(senderDisplayName)s has prevented guests from joining the room.', {senderDisplayName});
|
||||||
|
default:
|
||||||
|
// There's no other options we can expect, however just for safety's sake we'll do this.
|
||||||
|
return _t('%(senderDisplayName)s changed guest access to %(rule)s', {
|
||||||
|
senderDisplayName,
|
||||||
|
rule: ev.getContent().guest_access,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function textForServerACLEvent(ev) {
|
function textForServerACLEvent(ev) {
|
||||||
const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
|
const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
|
||||||
const prevContent = ev.getPrevContent();
|
const prevContent = ev.getPrevContent();
|
||||||
|
@ -439,6 +471,8 @@ const stateHandlers = {
|
||||||
'm.room.pinned_events': textForPinnedEvent,
|
'm.room.pinned_events': textForPinnedEvent,
|
||||||
'm.room.server_acl': textForServerACLEvent,
|
'm.room.server_acl': textForServerACLEvent,
|
||||||
'm.room.tombstone': textForTombstoneEvent,
|
'm.room.tombstone': textForTombstoneEvent,
|
||||||
|
'm.room.join_rules': textForJoinRulesEvent,
|
||||||
|
'm.room.guest_access': textForGuestAccessEvent,
|
||||||
|
|
||||||
'im.vector.modular.widgets': textForWidgetEvent,
|
'im.vector.modular.widgets': textForWidgetEvent,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
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");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -15,47 +15,11 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Promise from 'bluebird';
|
|
||||||
import MatrixClientPeg from './MatrixClientPeg';
|
import MatrixClientPeg from './MatrixClientPeg';
|
||||||
|
|
||||||
/*
|
// TODO: Decommission.
|
||||||
* TODO: Make things use this. This is all WIP - see UserSettings.js for usage.
|
// Ref: https://github.com/vector-im/riot-web/issues/8424
|
||||||
*/
|
|
||||||
export default {
|
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
|
* Returns the email pusher (pusher of type 'email') for a given
|
||||||
* email address. Email pushers all have the same app ID, so since
|
* email address. Email pushers all have the same app ID, so since
|
||||||
|
@ -74,10 +38,6 @@ export default {
|
||||||
return undefined;
|
return undefined;
|
||||||
},
|
},
|
||||||
|
|
||||||
hasEmailPusher: function(pushers, address) {
|
|
||||||
return this.getEmailPusher(pushers, address) !== undefined;
|
|
||||||
},
|
|
||||||
|
|
||||||
addEmailPusher: function(address, data) {
|
addEmailPusher: function(address, data) {
|
||||||
return MatrixClientPeg.get().setPusher({
|
return MatrixClientPeg.get().setPusher({
|
||||||
kind: 'email',
|
kind: 'email',
|
||||||
|
|
125
src/components/structures/CustomRoomTagPanel.js
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import CustomRoomTagStore from '../../stores/CustomRoomTagStore';
|
||||||
|
import AutoHideScrollbar from './AutoHideScrollbar';
|
||||||
|
import sdk from '../../index';
|
||||||
|
import dis from '../../dispatcher';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import * as FormattingUtils from '../../utils/FormattingUtils';
|
||||||
|
|
||||||
|
class CustomRoomTagPanel extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
tags: CustomRoomTagStore.getSortedTags(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillMount() {
|
||||||
|
this._tagStoreToken = CustomRoomTagStore.addListener(() => {
|
||||||
|
this.setState({tags: CustomRoomTagStore.getSortedTags()});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
if (this._tagStoreToken) {
|
||||||
|
this._tagStoreToken.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const tags = this.state.tags.map((tag) => {
|
||||||
|
return (<CustomRoomTagTile tag={tag} key={tag.name} />);
|
||||||
|
});
|
||||||
|
|
||||||
|
const classes = classNames('mx_CustomRoomTagPanel', {
|
||||||
|
mx_CustomRoomTagPanel_empty: this.state.tags.length === 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (<div className={classes}>
|
||||||
|
<div className="mx_CustomRoomTagPanel_divider" />
|
||||||
|
<AutoHideScrollbar className="mx_CustomRoomTagPanel_scroller">
|
||||||
|
{tags}
|
||||||
|
</AutoHideScrollbar>
|
||||||
|
</div>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CustomRoomTagTile extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {hover: false};
|
||||||
|
this.onClick = this.onClick.bind(this);
|
||||||
|
this.onMouseOut = this.onMouseOut.bind(this);
|
||||||
|
this.onMouseOver = this.onMouseOver.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMouseOver() {
|
||||||
|
this.setState({hover: true});
|
||||||
|
}
|
||||||
|
|
||||||
|
onMouseOut() {
|
||||||
|
this.setState({hover: false});
|
||||||
|
}
|
||||||
|
|
||||||
|
onClick() {
|
||||||
|
dis.dispatch({action: 'select_custom_room_tag', tag: this.props.tag.name});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
|
||||||
|
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||||
|
const RoomTooltip = sdk.getComponent('rooms.RoomTooltip');
|
||||||
|
|
||||||
|
const tag = this.props.tag;
|
||||||
|
const avatarHeight = 40;
|
||||||
|
const className = classNames({
|
||||||
|
CustomRoomTagPanel_tileSelected: tag.selected,
|
||||||
|
});
|
||||||
|
const name = tag.name;
|
||||||
|
const badge = tag.badge;
|
||||||
|
let badgeElement;
|
||||||
|
if (badge) {
|
||||||
|
const badgeClasses = classNames({
|
||||||
|
"mx_TagTile_badge": true,
|
||||||
|
"mx_TagTile_badgeHighlight": badge.highlight,
|
||||||
|
});
|
||||||
|
badgeElement = (<div className={badgeClasses}>{FormattingUtils.formatCount(badge.count)}</div>);
|
||||||
|
}
|
||||||
|
|
||||||
|
const tip = (this.state.hover ?
|
||||||
|
<RoomTooltip className="mx_TagTile_tooltip" label={name} /> :
|
||||||
|
<div />);
|
||||||
|
return (
|
||||||
|
<AccessibleButton className={className} onClick={this.onClick}>
|
||||||
|
<div className="mx_TagTile_avatar" onMouseOver={this.onMouseOver} onMouseOut={this.onMouseOut}>
|
||||||
|
<BaseAvatar
|
||||||
|
name={tag.avatarLetter}
|
||||||
|
idName={name}
|
||||||
|
width={avatarHeight}
|
||||||
|
height={avatarHeight}
|
||||||
|
/>
|
||||||
|
{ badgeElement }
|
||||||
|
{ tip }
|
||||||
|
</div>
|
||||||
|
</AccessibleButton>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CustomRoomTagPanel;
|
116
src/components/structures/EmbeddedPage.js
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016 OpenMarket Ltd
|
||||||
|
Copyright 2017 Vector Creations Ltd
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import request from 'browser-request';
|
||||||
|
import { _t } from '../../languageHandler';
|
||||||
|
import sanitizeHtml from 'sanitize-html';
|
||||||
|
import sdk from '../../index';
|
||||||
|
import { MatrixClient } from 'matrix-js-sdk';
|
||||||
|
import classnames from 'classnames';
|
||||||
|
|
||||||
|
export default class EmbeddedPage extends React.PureComponent {
|
||||||
|
static propTypes = {
|
||||||
|
// URL to request embedded page content from
|
||||||
|
url: PropTypes.string,
|
||||||
|
// Class name prefix to apply for a given instance
|
||||||
|
className: PropTypes.string,
|
||||||
|
// Whether to wrap the page in a scrollbar
|
||||||
|
scrollbar: PropTypes.bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
static contextTypes = {
|
||||||
|
matrixClient: PropTypes.instanceOf(MatrixClient),
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
page: '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
translate(s) {
|
||||||
|
// default implementation - skins may wish to extend this
|
||||||
|
return sanitizeHtml(_t(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillMount() {
|
||||||
|
this._unmounted = false;
|
||||||
|
|
||||||
|
if (!this.props.url) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we use request() to inline the page into the react component
|
||||||
|
// so that it can inherit CSS and theming easily rather than mess around
|
||||||
|
// with iframes and trying to synchronise document.stylesheets.
|
||||||
|
|
||||||
|
request(
|
||||||
|
{ method: "GET", url: this.props.url },
|
||||||
|
(err, response, body) => {
|
||||||
|
if (this._unmounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err || response.status < 200 || response.status >= 300) {
|
||||||
|
console.warn(`Error loading page: ${err}`);
|
||||||
|
this.setState({ page: _t("Couldn't load page") });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
body = body.replace(/_t\(['"]([\s\S]*?)['"]\)/mg, (match, g1)=>this.translate(g1));
|
||||||
|
this.setState({ page: body });
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this._unmounted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const client = this.context.matrixClient;
|
||||||
|
const isGuest = client ? client.isGuest() : true;
|
||||||
|
const className = this.props.className;
|
||||||
|
const classes = classnames({
|
||||||
|
[className]: true,
|
||||||
|
[`${className}_guest`]: isGuest,
|
||||||
|
});
|
||||||
|
|
||||||
|
const content = <div className={`${className}_body`}
|
||||||
|
dangerouslySetInnerHTML={{ __html: this.state.page }}
|
||||||
|
>
|
||||||
|
</div>;
|
||||||
|
|
||||||
|
if (this.props.scrollbar) {
|
||||||
|
const GeminiScrollbarWrapper = sdk.getComponent("elements.GeminiScrollbarWrapper");
|
||||||
|
return <GeminiScrollbarWrapper autoshow={true} className={classes}>
|
||||||
|
{content}
|
||||||
|
</GeminiScrollbarWrapper>;
|
||||||
|
} else {
|
||||||
|
return <div className={classes}>
|
||||||
|
{content}
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1324,7 +1324,7 @@ export default React.createClass({
|
||||||
} else {
|
} else {
|
||||||
let extraText;
|
let extraText;
|
||||||
if (this.state.error.errcode === 'M_UNRECOGNIZED') {
|
if (this.state.error.errcode === 'M_UNRECOGNIZED') {
|
||||||
extraText = <div>{ _t('This Home server does not support communities') }</div>;
|
extraText = <div>{ _t('This homeserver does not support communities') }</div>;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className="mx_GroupView_error">
|
<div className="mx_GroupView_error">
|
||||||
|
|
|
@ -1,144 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import request from 'browser-request';
|
|
||||||
import { _t } from '../../languageHandler';
|
|
||||||
import sanitizeHtml from 'sanitize-html';
|
|
||||||
import sdk from '../../index';
|
|
||||||
import { MatrixClient } from 'matrix-js-sdk';
|
|
||||||
import dis from '../../dispatcher';
|
|
||||||
|
|
||||||
class HomePage extends React.Component {
|
|
||||||
static displayName = 'HomePage';
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
// URL to use as the iFrame src. Defaults to /home.html.
|
|
||||||
homePageUrl: PropTypes.string,
|
|
||||||
};
|
|
||||||
|
|
||||||
static contextTypes = {
|
|
||||||
matrixClient: PropTypes.instanceOf(MatrixClient),
|
|
||||||
};
|
|
||||||
|
|
||||||
state = {
|
|
||||||
iframeSrc: '',
|
|
||||||
page: '',
|
|
||||||
};
|
|
||||||
|
|
||||||
translate(s) {
|
|
||||||
// default implementation - skins may wish to extend this
|
|
||||||
return sanitizeHtml(_t(s));
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillMount() {
|
|
||||||
this._unmounted = false;
|
|
||||||
|
|
||||||
// we use request() to inline the homepage into the react component
|
|
||||||
// so that it can inherit CSS and theming easily rather than mess around
|
|
||||||
// with iframes and trying to synchronise document.stylesheets.
|
|
||||||
|
|
||||||
const src = this.props.homePageUrl || 'home.html';
|
|
||||||
|
|
||||||
request(
|
|
||||||
{ method: "GET", url: src },
|
|
||||||
(err, response, body) => {
|
|
||||||
if (this._unmounted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (err || response.status < 200 || response.status >= 300) {
|
|
||||||
console.warn(`Error loading home page: ${err}`);
|
|
||||||
this.setState({ page: _t("Couldn't load home page") });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
body = body.replace(/_t\(['"]([\s\S]*?)['"]\)/mg, (match, g1)=>this.translate(g1));
|
|
||||||
this.setState({ page: body });
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
this._unmounted = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
onLoginClick(ev) {
|
|
||||||
ev.preventDefault();
|
|
||||||
ev.stopPropagation();
|
|
||||||
dis.dispatch({ action: 'start_login' });
|
|
||||||
}
|
|
||||||
|
|
||||||
onRegisterClick(ev) {
|
|
||||||
ev.preventDefault();
|
|
||||||
ev.stopPropagation();
|
|
||||||
dis.dispatch({ action: 'start_registration' });
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
let guestWarning = "";
|
|
||||||
if (this.context.matrixClient.isGuest()) {
|
|
||||||
guestWarning = (
|
|
||||||
<div className="mx_HomePage_guest_warning">
|
|
||||||
<img src={require("../../../res/img/warning.svg")} width="24" height="23" />
|
|
||||||
<div>
|
|
||||||
<div>
|
|
||||||
{ _t("You are currently using Riot anonymously as a guest.") }
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
{ _t(
|
|
||||||
'If you would like to create a Matrix account you can <a>register</a> now.',
|
|
||||||
{},
|
|
||||||
{ 'a': (sub) => <a href="#" onClick={this.onRegisterClick}>{ sub }</a> },
|
|
||||||
) }
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
{ _t(
|
|
||||||
'If you already have a Matrix account you can <a>log in</a> instead.',
|
|
||||||
{},
|
|
||||||
{ 'a': (sub) => <a href="#" onClick={this.onLoginClick}>{ sub }</a> },
|
|
||||||
) }
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.state.iframeSrc) {
|
|
||||||
return (
|
|
||||||
<div className="mx_HomePage">
|
|
||||||
{ guestWarning }
|
|
||||||
<iframe src={ this.state.iframeSrc } />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
const GeminiScrollbarWrapper = sdk.getComponent("elements.GeminiScrollbarWrapper");
|
|
||||||
return (
|
|
||||||
<GeminiScrollbarWrapper autoshow={true} className="mx_HomePage">
|
|
||||||
{ guestWarning }
|
|
||||||
<div className="mx_HomePage_body" dangerouslySetInnerHTML={{ __html: this.state.page }}>
|
|
||||||
</div>
|
|
||||||
</GeminiScrollbarWrapper>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = HomePage;
|
|
|
@ -24,7 +24,7 @@ import { KeyCode } from '../../Keyboard';
|
||||||
import sdk from '../../index';
|
import sdk from '../../index';
|
||||||
import dis from '../../dispatcher';
|
import dis from '../../dispatcher';
|
||||||
import VectorConferenceHandler from '../../VectorConferenceHandler';
|
import VectorConferenceHandler from '../../VectorConferenceHandler';
|
||||||
|
import TagPanelButtons from './TagPanelButtons';
|
||||||
import SettingsStore from '../../settings/SettingsStore';
|
import SettingsStore from '../../settings/SettingsStore';
|
||||||
|
|
||||||
|
|
||||||
|
@ -183,12 +183,23 @@ const LeftPanel = React.createClass({
|
||||||
render: function() {
|
render: function() {
|
||||||
const RoomList = sdk.getComponent('rooms.RoomList');
|
const RoomList = sdk.getComponent('rooms.RoomList');
|
||||||
const TagPanel = sdk.getComponent('structures.TagPanel');
|
const TagPanel = sdk.getComponent('structures.TagPanel');
|
||||||
|
const CustomRoomTagPanel = sdk.getComponent('structures.CustomRoomTagPanel');
|
||||||
const TopLeftMenuButton = sdk.getComponent('structures.TopLeftMenuButton');
|
const TopLeftMenuButton = sdk.getComponent('structures.TopLeftMenuButton');
|
||||||
const SearchBox = sdk.getComponent('structures.SearchBox');
|
const SearchBox = sdk.getComponent('structures.SearchBox');
|
||||||
const CallPreview = sdk.getComponent('voip.CallPreview');
|
const CallPreview = sdk.getComponent('voip.CallPreview');
|
||||||
|
|
||||||
const tagPanelEnabled = SettingsStore.getValue("TagPanel.enableTagPanel");
|
const tagPanelEnabled = SettingsStore.getValue("TagPanel.enableTagPanel");
|
||||||
const tagPanel = tagPanelEnabled ? <TagPanel /> : <div />;
|
let tagPanelContainer;
|
||||||
|
|
||||||
|
const isCustomTagsEnabled = SettingsStore.isFeatureEnabled("feature_custom_tags");
|
||||||
|
|
||||||
|
if (tagPanelEnabled) {
|
||||||
|
tagPanelContainer = (<div className="mx_LeftPanel_tagPanelContainer">
|
||||||
|
<TagPanel />
|
||||||
|
{ isCustomTagsEnabled ? <CustomRoomTagPanel /> : undefined }
|
||||||
|
<TagPanelButtons />
|
||||||
|
</div>);
|
||||||
|
}
|
||||||
|
|
||||||
const containerClasses = classNames(
|
const containerClasses = classNames(
|
||||||
"mx_LeftPanel_container", "mx_fadable",
|
"mx_LeftPanel_container", "mx_fadable",
|
||||||
|
@ -199,13 +210,15 @@ const LeftPanel = React.createClass({
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const searchBox = !this.props.collapsed ?
|
const searchBox = (<SearchBox
|
||||||
<SearchBox onSearch={ this.onSearch } onCleared={ this.onSearchCleared } /> :
|
onSearch={ this.onSearch }
|
||||||
undefined;
|
onCleared={ this.onSearchCleared }
|
||||||
|
collapsed={this.props.collapsed} />);
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={containerClasses}>
|
<div className={containerClasses}>
|
||||||
{ tagPanel }
|
{ tagPanelContainer }
|
||||||
<aside className={"mx_LeftPanel dark-panel"} onKeyDown={ this._onKeyDown } onFocus={ this._onFocus } onBlur={ this._onBlur }>
|
<aside className={"mx_LeftPanel dark-panel"} onKeyDown={ this._onKeyDown } onFocus={ this._onFocus } onBlur={ this._onBlur }>
|
||||||
<TopLeftMenuButton collapsed={ this.props.collapsed } />
|
<TopLeftMenuButton collapsed={ this.props.collapsed } />
|
||||||
{ searchBox }
|
{ searchBox }
|
||||||
|
|
|
@ -57,7 +57,6 @@ const LoggedInView = React.createClass({
|
||||||
matrixClient: PropTypes.instanceOf(Matrix.MatrixClient).isRequired,
|
matrixClient: PropTypes.instanceOf(Matrix.MatrixClient).isRequired,
|
||||||
page_type: PropTypes.string.isRequired,
|
page_type: PropTypes.string.isRequired,
|
||||||
onRoomCreated: PropTypes.func,
|
onRoomCreated: PropTypes.func,
|
||||||
onUserSettingsClose: PropTypes.func,
|
|
||||||
|
|
||||||
// Called with the credentials of a registered user (if they were a ROU that
|
// Called with the credentials of a registered user (if they were a ROU that
|
||||||
// transitioned to PWLU)
|
// transitioned to PWLU)
|
||||||
|
@ -421,8 +420,7 @@ const LoggedInView = React.createClass({
|
||||||
render: function() {
|
render: function() {
|
||||||
const LeftPanel = sdk.getComponent('structures.LeftPanel');
|
const LeftPanel = sdk.getComponent('structures.LeftPanel');
|
||||||
const RoomView = sdk.getComponent('structures.RoomView');
|
const RoomView = sdk.getComponent('structures.RoomView');
|
||||||
const UserSettings = sdk.getComponent('structures.UserSettings');
|
const EmbeddedPage = sdk.getComponent('structures.EmbeddedPage');
|
||||||
const HomePage = sdk.getComponent('structures.HomePage');
|
|
||||||
const GroupView = sdk.getComponent('structures.GroupView');
|
const GroupView = sdk.getComponent('structures.GroupView');
|
||||||
const MyGroups = sdk.getComponent('structures.MyGroups');
|
const MyGroups = sdk.getComponent('structures.MyGroups');
|
||||||
const MatrixToolbar = sdk.getComponent('globals.MatrixToolbar');
|
const MatrixToolbar = sdk.getComponent('globals.MatrixToolbar');
|
||||||
|
@ -451,13 +449,6 @@ const LoggedInView = React.createClass({
|
||||||
/>;
|
/>;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PageTypes.UserSettings:
|
|
||||||
pageElement = <UserSettings
|
|
||||||
onClose={this.props.onCloseAllSettings}
|
|
||||||
brand={this.props.config.brand}
|
|
||||||
/>;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PageTypes.MyGroups:
|
case PageTypes.MyGroups:
|
||||||
pageElement = <MyGroups />;
|
pageElement = <MyGroups />;
|
||||||
break;
|
break;
|
||||||
|
@ -468,8 +459,20 @@ const LoggedInView = React.createClass({
|
||||||
|
|
||||||
case PageTypes.HomePage:
|
case PageTypes.HomePage:
|
||||||
{
|
{
|
||||||
pageElement = <HomePage
|
const pagesConfig = this.props.config.embeddedPages;
|
||||||
homePageUrl={this.props.config.welcomePageUrl}
|
let pageUrl = null;
|
||||||
|
if (pagesConfig) {
|
||||||
|
pageUrl = pagesConfig.homeUrl;
|
||||||
|
}
|
||||||
|
if (!pageUrl) {
|
||||||
|
// This is a deprecated config option for the home page
|
||||||
|
// (despite the name, given we also now have a welcome
|
||||||
|
// page, which is not the same).
|
||||||
|
pageUrl = this.props.config.welcomePageUrl;
|
||||||
|
}
|
||||||
|
pageElement = <EmbeddedPage className="mx_HomePage"
|
||||||
|
url={pageUrl}
|
||||||
|
scrollbar={true}
|
||||||
/>;
|
/>;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -60,27 +60,30 @@ const VIEWS = {
|
||||||
// trying to re-animate a matrix client or register as a guest.
|
// trying to re-animate a matrix client or register as a guest.
|
||||||
LOADING: 0,
|
LOADING: 0,
|
||||||
|
|
||||||
|
// we are showing the welcome view
|
||||||
|
WELCOME: 1,
|
||||||
|
|
||||||
// we are showing the login view
|
// we are showing the login view
|
||||||
LOGIN: 1,
|
LOGIN: 2,
|
||||||
|
|
||||||
// we are showing the registration view
|
// we are showing the registration view
|
||||||
REGISTER: 2,
|
REGISTER: 3,
|
||||||
|
|
||||||
// completeing the registration flow
|
// completeing the registration flow
|
||||||
POST_REGISTRATION: 3,
|
POST_REGISTRATION: 4,
|
||||||
|
|
||||||
// showing the 'forgot password' view
|
// showing the 'forgot password' view
|
||||||
FORGOT_PASSWORD: 4,
|
FORGOT_PASSWORD: 5,
|
||||||
|
|
||||||
// we have valid matrix credentials (either via an explicit login, via the
|
// we have valid matrix credentials (either via an explicit login, via the
|
||||||
// initial re-animation/guest registration, or via a registration), and are
|
// initial re-animation/guest registration, or via a registration), and are
|
||||||
// now setting up a matrixclient to talk to it. This isn't an instant
|
// now setting up a matrixclient to talk to it. This isn't an instant
|
||||||
// process because we need to clear out indexeddb. While it is going on we
|
// process because we need to clear out indexeddb. While it is going on we
|
||||||
// show a big spinner.
|
// show a big spinner.
|
||||||
LOGGING_IN: 5,
|
LOGGING_IN: 6,
|
||||||
|
|
||||||
// we are logged in with an active matrix client.
|
// we are logged in with an active matrix client.
|
||||||
LOGGED_IN: 6,
|
LOGGED_IN: 7,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Actions that are redirected through the onboarding process prior to being
|
// Actions that are redirected through the onboarding process prior to being
|
||||||
|
@ -136,10 +139,6 @@ export default React.createClass({
|
||||||
appConfig: PropTypes.object,
|
appConfig: PropTypes.object,
|
||||||
},
|
},
|
||||||
|
|
||||||
AuxPanel: {
|
|
||||||
RoomSettings: "room_settings",
|
|
||||||
},
|
|
||||||
|
|
||||||
getChildContext: function() {
|
getChildContext: function() {
|
||||||
return {
|
return {
|
||||||
appConfig: this.props.config,
|
appConfig: this.props.config,
|
||||||
|
@ -358,8 +357,8 @@ export default React.createClass({
|
||||||
});
|
});
|
||||||
}).then((loadedSession) => {
|
}).then((loadedSession) => {
|
||||||
if (!loadedSession) {
|
if (!loadedSession) {
|
||||||
// fall back to showing the login screen
|
// fall back to showing the welcome screen
|
||||||
dis.dispatch({action: "start_login"});
|
dis.dispatch({action: "view_welcome_page"});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// Note we don't catch errors from this: we catch everything within
|
// Note we don't catch errors from this: we catch everything within
|
||||||
|
@ -572,40 +571,16 @@ export default React.createClass({
|
||||||
this._viewIndexedRoom(payload.roomIndex);
|
this._viewIndexedRoom(payload.roomIndex);
|
||||||
break;
|
break;
|
||||||
case 'view_user_settings': {
|
case 'view_user_settings': {
|
||||||
if (SettingsStore.isFeatureEnabled("feature_tabbed_settings")) {
|
|
||||||
const UserSettingsDialog = sdk.getComponent("dialogs.UserSettingsDialog");
|
const UserSettingsDialog = sdk.getComponent("dialogs.UserSettingsDialog");
|
||||||
Modal.createTrackedDialog('User settings', '', UserSettingsDialog, {}, 'mx_SettingsDialog');
|
Modal.createTrackedDialog('User settings', '', UserSettingsDialog, {}, 'mx_SettingsDialog');
|
||||||
} else {
|
|
||||||
this._setPage(PageTypes.UserSettings);
|
// View the home page if we need something to look at
|
||||||
this.notifyNewScreen('settings');
|
if (!this.state.currentGroupId && !this.state.currentRoomId) {
|
||||||
|
this._setPage(PageTypes.HomePage);
|
||||||
|
this.notifyNewScreen('home');
|
||||||
}
|
}
|
||||||
break;
|
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':
|
case 'view_create_room':
|
||||||
this._createRoom();
|
this._createRoom();
|
||||||
break;
|
break;
|
||||||
|
@ -634,6 +609,9 @@ export default React.createClass({
|
||||||
case 'view_group':
|
case 'view_group':
|
||||||
this._viewGroup(payload);
|
this._viewGroup(payload);
|
||||||
break;
|
break;
|
||||||
|
case 'view_welcome_page':
|
||||||
|
this._viewWelcome();
|
||||||
|
break;
|
||||||
case 'view_home_page':
|
case 'view_home_page':
|
||||||
this._viewHome();
|
this._viewHome();
|
||||||
break;
|
break;
|
||||||
|
@ -909,6 +887,13 @@ export default React.createClass({
|
||||||
this.notifyNewScreen('group/' + groupId);
|
this.notifyNewScreen('group/' + groupId);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_viewWelcome() {
|
||||||
|
this.setStateForNewView({
|
||||||
|
view: VIEWS.WELCOME,
|
||||||
|
});
|
||||||
|
this.notifyNewScreen('welcome');
|
||||||
|
},
|
||||||
|
|
||||||
_viewHome: function() {
|
_viewHome: function() {
|
||||||
// The home page requires the "logged in" view, so we'll set that.
|
// The home page requires the "logged in" view, so we'll set that.
|
||||||
this.setStateForNewView({
|
this.setStateForNewView({
|
||||||
|
@ -982,11 +967,11 @@ export default React.createClass({
|
||||||
}
|
}
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'require_registration',
|
action: 'require_registration',
|
||||||
// If the set_mxid dialog is cancelled, view /home because if the browser
|
// If the set_mxid dialog is cancelled, view /welcome because if the
|
||||||
// was pointing at /user/@someone:domain?action=chat, the URL needs to be
|
// browser was pointing at /user/@someone:domain?action=chat, the URL
|
||||||
// reset so that they can revisit /user/.. // (and trigger
|
// needs to be reset so that they can revisit /user/.. // (and trigger
|
||||||
// `_chatCreateOrReuse` again)
|
// `_chatCreateOrReuse` again)
|
||||||
go_home_on_cancel: true,
|
go_welcome_on_cancel: true,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1063,7 +1048,6 @@ export default React.createClass({
|
||||||
modal.close();
|
modal.close();
|
||||||
if (this.state.currentRoomId === roomId) {
|
if (this.state.currentRoomId === roomId) {
|
||||||
dis.dispatch({action: 'view_next_room'});
|
dis.dispatch({action: 'view_next_room'});
|
||||||
dis.dispatch({action: 'close_room_settings'});
|
|
||||||
}
|
}
|
||||||
}, (err) => {
|
}, (err) => {
|
||||||
modal.close();
|
modal.close();
|
||||||
|
@ -1208,9 +1192,13 @@ export default React.createClass({
|
||||||
action: 'view_room',
|
action: 'view_room',
|
||||||
room_id: localStorage.getItem('mx_last_room_id'),
|
room_id: localStorage.getItem('mx_last_room_id'),
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
if (MatrixClientPeg.get().isGuest) {
|
||||||
|
dis.dispatch({action: 'view_welcome_page'});
|
||||||
} else {
|
} else {
|
||||||
dis.dispatch({action: 'view_home_page'});
|
dis.dispatch({action: 'view_home_page'});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1495,6 +1483,10 @@ export default React.createClass({
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'view_user_settings',
|
action: 'view_user_settings',
|
||||||
});
|
});
|
||||||
|
} else if (screen == 'welcome') {
|
||||||
|
dis.dispatch({
|
||||||
|
action: 'view_welcome_page',
|
||||||
|
});
|
||||||
} else if (screen == 'home') {
|
} else if (screen == 'home') {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'view_home_page',
|
action: 'view_home_page',
|
||||||
|
@ -1676,11 +1668,6 @@ export default React.createClass({
|
||||||
this.showScreen("forgot_password");
|
this.showScreen("forgot_password");
|
||||||
},
|
},
|
||||||
|
|
||||||
onReturnToAppClick: function() {
|
|
||||||
// treat it the same as if the user had completed the login
|
|
||||||
this._onLoggedIn();
|
|
||||||
},
|
|
||||||
|
|
||||||
// returns a promise which resolves to the new MatrixClient
|
// returns a promise which resolves to the new MatrixClient
|
||||||
onRegistered: function(credentials) {
|
onRegistered: function(credentials) {
|
||||||
// XXX: This should be in state or ideally store(s) because we risk not
|
// XXX: This should be in state or ideally store(s) because we risk not
|
||||||
|
@ -1883,6 +1870,11 @@ export default React.createClass({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.state.view === VIEWS.WELCOME) {
|
||||||
|
const Welcome = sdk.getComponent('auth.Welcome');
|
||||||
|
return <Welcome />;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.state.view === VIEWS.REGISTER) {
|
if (this.state.view === VIEWS.REGISTER) {
|
||||||
const Registration = sdk.getComponent('structures.auth.Registration');
|
const Registration = sdk.getComponent('structures.auth.Registration');
|
||||||
return (
|
return (
|
||||||
|
@ -1891,7 +1883,6 @@ export default React.createClass({
|
||||||
sessionId={this.state.register_session_id}
|
sessionId={this.state.register_session_id}
|
||||||
idSid={this.state.register_id_sid}
|
idSid={this.state.register_id_sid}
|
||||||
email={this.props.startingFragmentQueryParams.email}
|
email={this.props.startingFragmentQueryParams.email}
|
||||||
referrer={this.props.startingFragmentQueryParams.referrer}
|
|
||||||
defaultServerDiscoveryError={this.state.defaultServerDiscoveryError}
|
defaultServerDiscoveryError={this.state.defaultServerDiscoveryError}
|
||||||
defaultHsUrl={this.getDefaultHsUrl()}
|
defaultHsUrl={this.getDefaultHsUrl()}
|
||||||
defaultIsUrl={this.getDefaultIsUrl()}
|
defaultIsUrl={this.getDefaultIsUrl()}
|
||||||
|
@ -1899,11 +1890,8 @@ export default React.createClass({
|
||||||
customHsUrl={this.getCurrentHsUrl()}
|
customHsUrl={this.getCurrentHsUrl()}
|
||||||
customIsUrl={this.getCurrentIsUrl()}
|
customIsUrl={this.getCurrentIsUrl()}
|
||||||
makeRegistrationUrl={this._makeRegistrationUrl}
|
makeRegistrationUrl={this._makeRegistrationUrl}
|
||||||
defaultDeviceDisplayName={this.props.defaultDeviceDisplayName}
|
|
||||||
onLoggedIn={this.onRegistered}
|
onLoggedIn={this.onRegistered}
|
||||||
onLoginClick={this.onLoginClick}
|
onLoginClick={this.onLoginClick}
|
||||||
onRegisterClick={this.onRegisterClick}
|
|
||||||
onCancelClick={MatrixClientPeg.get() ? this.onReturnToAppClick : null}
|
|
||||||
onServerConfigChange={this.onServerConfigChange}
|
onServerConfigChange={this.onServerConfigChange}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -1940,7 +1928,6 @@ export default React.createClass({
|
||||||
defaultDeviceDisplayName={this.props.defaultDeviceDisplayName}
|
defaultDeviceDisplayName={this.props.defaultDeviceDisplayName}
|
||||||
onForgotPasswordClick={this.onForgotPasswordClick}
|
onForgotPasswordClick={this.onForgotPasswordClick}
|
||||||
enableGuest={this.props.enableGuest}
|
enableGuest={this.props.enableGuest}
|
||||||
onCancelClick={MatrixClientPeg.get() ? this.onReturnToAppClick : null}
|
|
||||||
onServerConfigChange={this.onServerConfigChange}
|
onServerConfigChange={this.onServerConfigChange}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -642,14 +642,13 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
updateTimelineMinHeight: function() {
|
updateTimelineMinHeight: function() {
|
||||||
const scrollPanel = this.refs.scrollPanel;
|
const scrollPanel = this.refs.scrollPanel;
|
||||||
const whoIsTyping = this.refs.whoIsTyping;
|
|
||||||
const isTypingVisible = whoIsTyping && whoIsTyping.isVisible();
|
|
||||||
|
|
||||||
if (scrollPanel) {
|
if (scrollPanel) {
|
||||||
if (isTypingVisible) {
|
const isAtBottom = scrollPanel.isAtBottom();
|
||||||
|
const whoIsTyping = this.refs.whoIsTyping;
|
||||||
|
const isTypingVisible = whoIsTyping && whoIsTyping.isVisible();
|
||||||
|
if (isAtBottom && isTypingVisible) {
|
||||||
scrollPanel.blockShrinking();
|
scrollPanel.blockShrinking();
|
||||||
} else {
|
|
||||||
scrollPanel.clearBlockShrinking();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -24,10 +24,7 @@ const Modal = require('../../Modal');
|
||||||
const sdk = require('../../index');
|
const sdk = require('../../index');
|
||||||
const dis = require('../../dispatcher');
|
const dis = require('../../dispatcher');
|
||||||
|
|
||||||
const linkify = require('linkifyjs');
|
import { linkifyAndSanitizeHtml } from '../../HtmlUtils';
|
||||||
const linkifyString = require('linkifyjs/string');
|
|
||||||
const linkifyMatrix = require('../../linkify-matrix');
|
|
||||||
const sanitizeHtml = require('sanitize-html');
|
|
||||||
import Promise from 'bluebird';
|
import Promise from 'bluebird';
|
||||||
|
|
||||||
import { _t } from '../../languageHandler';
|
import { _t } from '../../languageHandler';
|
||||||
|
@ -37,8 +34,6 @@ import {instanceForInstanceId, protocolNameForInstanceId} from '../../utils/Dire
|
||||||
const MAX_NAME_LENGTH = 80;
|
const MAX_NAME_LENGTH = 80;
|
||||||
const MAX_TOPIC_LENGTH = 160;
|
const MAX_TOPIC_LENGTH = 160;
|
||||||
|
|
||||||
linkifyMatrix(linkify);
|
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
displayName: 'RoomDirectory',
|
displayName: 'RoomDirectory',
|
||||||
|
|
||||||
|
@ -96,9 +91,9 @@ module.exports = React.createClass({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||||
Modal.createTrackedDialog('Failed to get protocol list from Home Server', '', ErrorDialog, {
|
Modal.createTrackedDialog('Failed to get protocol list from homeserver', '', ErrorDialog, {
|
||||||
title: _t('Failed to get protocol list from Home Server'),
|
title: _t('Failed to get protocol list from homeserver'),
|
||||||
description: _t('The Home Server may be too old to support third party networks'),
|
description: _t('The homeserver may be too old to support third party networks'),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -438,7 +433,7 @@ module.exports = React.createClass({
|
||||||
if (topic.length > MAX_TOPIC_LENGTH) {
|
if (topic.length > MAX_TOPIC_LENGTH) {
|
||||||
topic = `${topic.substring(0, MAX_TOPIC_LENGTH)}...`;
|
topic = `${topic.substring(0, MAX_TOPIC_LENGTH)}...`;
|
||||||
}
|
}
|
||||||
topic = linkifyString(sanitizeHtml(topic));
|
topic = linkifyAndSanitizeHtml(topic);
|
||||||
|
|
||||||
rows.push(
|
rows.push(
|
||||||
<tr key={ rooms[i].room_id }
|
<tr key={ rooms[i].room_id }
|
||||||
|
|
|
@ -290,7 +290,7 @@ module.exports = React.createClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div className="mx_RoomStatusBar_connectionLostBar">
|
return <div className="mx_RoomStatusBar_connectionLostBar">
|
||||||
<img src={require("../../../res/img/warning.svg")} width="24" height="23" title={_t("Warning")} alt="" />
|
<img src={require("../../../res/img/feather-icons/e2e/warning.svg")} width="24" height="24" title={_t("Warning")} alt="" />
|
||||||
<div>
|
<div>
|
||||||
<div className="mx_RoomStatusBar_connectionLostBar_title">
|
<div className="mx_RoomStatusBar_connectionLostBar_title">
|
||||||
{ title }
|
{ title }
|
||||||
|
@ -309,7 +309,7 @@ module.exports = React.createClass({
|
||||||
if (this._shouldShowConnectionError()) {
|
if (this._shouldShowConnectionError()) {
|
||||||
return (
|
return (
|
||||||
<div className="mx_RoomStatusBar_connectionLostBar">
|
<div className="mx_RoomStatusBar_connectionLostBar">
|
||||||
<img src={require("../../../res/img/warning.svg")} width="24" height="23" title="/!\ " alt="/!\ " />
|
<img src={require("../../../res/img/feather-icons/e2e/warning.svg")} width="24" height="24" title="/!\ " alt="/!\ " />
|
||||||
<div>
|
<div>
|
||||||
<div className="mx_RoomStatusBar_connectionLostBar_title">
|
<div className="mx_RoomStatusBar_connectionLostBar_title">
|
||||||
{ _t('Connectivity to the server has been lost.') }
|
{ _t('Connectivity to the server has been lost.') }
|
||||||
|
|
|
@ -127,46 +127,6 @@ const RoomSubList = React.createClass({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
_shouldShowNotifBadge: function(roomNotifState) {
|
|
||||||
const showBadgeInStates = [RoomNotifs.ALL_MESSAGES, RoomNotifs.ALL_MESSAGES_LOUD];
|
|
||||||
return showBadgeInStates.indexOf(roomNotifState) > -1;
|
|
||||||
},
|
|
||||||
|
|
||||||
_shouldShowMentionBadge: function(roomNotifState) {
|
|
||||||
return roomNotifState !== RoomNotifs.MUTE;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Total up all the notification counts from the rooms
|
|
||||||
*
|
|
||||||
* @returns {Array} The array takes the form [total, highlight] where highlight is a bool
|
|
||||||
*/
|
|
||||||
roomNotificationCount: function() {
|
|
||||||
const self = this;
|
|
||||||
|
|
||||||
if (this.props.isInvite) {
|
|
||||||
return [0, true];
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.props.list.reduce(function(result, room, index) {
|
|
||||||
const roomNotifState = RoomNotifs.getRoomNotifsState(room.roomId);
|
|
||||||
const highlight = room.getUnreadNotificationCount('highlight') > 0;
|
|
||||||
const notificationCount = room.getUnreadNotificationCount();
|
|
||||||
|
|
||||||
const notifBadges = notificationCount > 0 && self._shouldShowNotifBadge(roomNotifState);
|
|
||||||
const mentionBadges = highlight && self._shouldShowMentionBadge(roomNotifState);
|
|
||||||
const badges = notifBadges || mentionBadges;
|
|
||||||
|
|
||||||
if (badges) {
|
|
||||||
result[0] += notificationCount;
|
|
||||||
if (highlight) {
|
|
||||||
result[1] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}, [0, false]);
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateSubListCount: function() {
|
_updateSubListCount: function() {
|
||||||
// Force an update by setting the state to the current state
|
// Force an update by setting the state to the current state
|
||||||
// Doing it this way rather than using forceUpdate(), so that the shouldComponentUpdate()
|
// Doing it this way rather than using forceUpdate(), so that the shouldComponentUpdate()
|
||||||
|
@ -197,22 +157,12 @@ const RoomSubList = React.createClass({
|
||||||
// prevent the roomsublist collapsing
|
// prevent the roomsublist collapsing
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
// find first room which has notifications and switch to it
|
const room = this.props.list.find(room => RoomNotifs.getRoomHasBadge(room));
|
||||||
for (const room of this.props.list) {
|
if (room) {
|
||||||
const roomNotifState = RoomNotifs.getRoomNotifsState(room.roomId);
|
|
||||||
const highlight = room.getUnreadNotificationCount('highlight') > 0;
|
|
||||||
const notificationCount = room.getUnreadNotificationCount();
|
|
||||||
|
|
||||||
const notifBadges = notificationCount > 0 && this._shouldShowNotifBadge(roomNotifState);
|
|
||||||
const mentionBadges = highlight && this._shouldShowMentionBadge(roomNotifState);
|
|
||||||
|
|
||||||
if (notifBadges || mentionBadges) {
|
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'view_room',
|
action: 'view_room',
|
||||||
room_id: room.roomId,
|
room_id: room.roomId,
|
||||||
});
|
});
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -240,9 +190,11 @@ const RoomSubList = React.createClass({
|
||||||
|
|
||||||
_getHeaderJsx: function(isCollapsed) {
|
_getHeaderJsx: function(isCollapsed) {
|
||||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||||
const subListNotifications = this.roomNotificationCount();
|
const subListNotifications = !this.props.isInvite ?
|
||||||
const subListNotifCount = subListNotifications[0];
|
RoomNotifs.aggregateNotificationCount(this.props.list) :
|
||||||
const subListNotifHighlight = subListNotifications[1];
|
{count: 0, highlight: true};
|
||||||
|
const subListNotifCount = subListNotifications.count;
|
||||||
|
const subListNotifHighlight = subListNotifications.highlight;
|
||||||
|
|
||||||
let badge;
|
let badge;
|
||||||
if (!this.props.collapsed) {
|
if (!this.props.collapsed) {
|
||||||
|
|
|
@ -78,7 +78,7 @@ module.exports = React.createClass({
|
||||||
// * invitedEmail (string) The email address that was invited to this room
|
// * invitedEmail (string) The email address that was invited to this room
|
||||||
thirdPartyInvite: PropTypes.object,
|
thirdPartyInvite: PropTypes.object,
|
||||||
|
|
||||||
// Any data about the room that would normally come from the Home Server
|
// Any data about the room that would normally come from the homeserver
|
||||||
// but has been passed out-of-band, eg. the room name and avatar URL
|
// but has been passed out-of-band, eg. the room name and avatar URL
|
||||||
// from an email invite (a workaround for the fact that we can't
|
// from an email invite (a workaround for the fact that we can't
|
||||||
// get this information from the HS using an email invite).
|
// get this information from the HS using an email invite).
|
||||||
|
@ -119,8 +119,6 @@ module.exports = React.createClass({
|
||||||
isInitialEventHighlighted: null,
|
isInitialEventHighlighted: null,
|
||||||
|
|
||||||
forwardingEvent: null,
|
forwardingEvent: null,
|
||||||
editingRoomSettings: false,
|
|
||||||
uploadingRoomSettings: false,
|
|
||||||
numUnreadMessages: 0,
|
numUnreadMessages: 0,
|
||||||
draggingFile: false,
|
draggingFile: false,
|
||||||
searching: false,
|
searching: false,
|
||||||
|
@ -168,6 +166,7 @@ module.exports = React.createClass({
|
||||||
MatrixClientPeg.get().on("Room.myMembership", this.onMyMembership);
|
MatrixClientPeg.get().on("Room.myMembership", this.onMyMembership);
|
||||||
MatrixClientPeg.get().on("accountData", this.onAccountData);
|
MatrixClientPeg.get().on("accountData", this.onAccountData);
|
||||||
MatrixClientPeg.get().on("crypto.keyBackupStatus", this.onKeyBackupStatus);
|
MatrixClientPeg.get().on("crypto.keyBackupStatus", this.onKeyBackupStatus);
|
||||||
|
MatrixClientPeg.get().on("deviceVerificationChanged", this.onDeviceVerificationChanged);
|
||||||
this._fetchMediaConfig();
|
this._fetchMediaConfig();
|
||||||
// Start listening for RoomViewStore updates
|
// Start listening for RoomViewStore updates
|
||||||
this._roomStoreToken = RoomViewStore.addListener(this._onRoomViewStoreUpdate);
|
this._roomStoreToken = RoomViewStore.addListener(this._onRoomViewStoreUpdate);
|
||||||
|
@ -228,11 +227,8 @@ module.exports = React.createClass({
|
||||||
forwardingEvent: RoomViewStore.getForwardingEvent(),
|
forwardingEvent: RoomViewStore.getForwardingEvent(),
|
||||||
shouldPeek: RoomViewStore.shouldPeek(),
|
shouldPeek: RoomViewStore.shouldPeek(),
|
||||||
showingPinned: SettingsStore.getValue("PinnedEvents.isOpen", RoomViewStore.getRoomId()),
|
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
|
// Temporary logging to diagnose https://github.com/vector-im/riot-web/issues/4307
|
||||||
console.log(
|
console.log(
|
||||||
'RVS update:',
|
'RVS update:',
|
||||||
|
@ -457,6 +453,7 @@ module.exports = React.createClass({
|
||||||
MatrixClientPeg.get().removeListener("RoomState.members", this.onRoomStateMember);
|
MatrixClientPeg.get().removeListener("RoomState.members", this.onRoomStateMember);
|
||||||
MatrixClientPeg.get().removeListener("accountData", this.onAccountData);
|
MatrixClientPeg.get().removeListener("accountData", this.onAccountData);
|
||||||
MatrixClientPeg.get().removeListener("crypto.keyBackupStatus", this.onKeyBackupStatus);
|
MatrixClientPeg.get().removeListener("crypto.keyBackupStatus", this.onKeyBackupStatus);
|
||||||
|
MatrixClientPeg.get().removeListener("deviceVerificationChanged", this.onDeviceVerificationChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
window.removeEventListener('beforeunload', this.onPageUnload);
|
window.removeEventListener('beforeunload', this.onPageUnload);
|
||||||
|
@ -589,6 +586,10 @@ module.exports = React.createClass({
|
||||||
this._updatePreviewUrlVisibility(room);
|
this._updatePreviewUrlVisibility(room);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ev.getType() === "m.room.encryption") {
|
||||||
|
this._updateE2EStatus(room);
|
||||||
|
}
|
||||||
|
|
||||||
// ignore anything but real-time updates at the end of the room:
|
// ignore anything but real-time updates at the end of the room:
|
||||||
// updates from pagination will happen when the paginate completes.
|
// updates from pagination will happen when the paginate completes.
|
||||||
if (toStartOfTimeline || !data || !data.liveEvent) return;
|
if (toStartOfTimeline || !data || !data.liveEvent) return;
|
||||||
|
@ -637,11 +638,11 @@ module.exports = React.createClass({
|
||||||
// called when state.room is first initialised (either at initial load,
|
// called when state.room is first initialised (either at initial load,
|
||||||
// after a successful peek, or after we join the room).
|
// after a successful peek, or after we join the room).
|
||||||
_onRoomLoaded: function(room) {
|
_onRoomLoaded: function(room) {
|
||||||
this._warnAboutEncryption(room);
|
|
||||||
this._calculatePeekRules(room);
|
this._calculatePeekRules(room);
|
||||||
this._updatePreviewUrlVisibility(room);
|
this._updatePreviewUrlVisibility(room);
|
||||||
this._loadMembersIfJoined(room);
|
this._loadMembersIfJoined(room);
|
||||||
this._calculateRecommendedVersion(room);
|
this._calculateRecommendedVersion(room);
|
||||||
|
this._updateE2EStatus(room);
|
||||||
},
|
},
|
||||||
|
|
||||||
_calculateRecommendedVersion: async function(room) {
|
_calculateRecommendedVersion: async function(room) {
|
||||||
|
@ -670,34 +671,6 @@ module.exports = React.createClass({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_warnAboutEncryption: function(room) {
|
|
||||||
if (!MatrixClientPeg.get().isRoomEncrypted(room.roomId)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let userHasUsedEncryption = false;
|
|
||||||
if (localStorage) {
|
|
||||||
userHasUsedEncryption = localStorage.getItem('mx_user_has_used_encryption');
|
|
||||||
}
|
|
||||||
if (!userHasUsedEncryption) {
|
|
||||||
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
|
||||||
Modal.createTrackedDialog('E2E Warning', '', QuestionDialog, {
|
|
||||||
title: _t("Warning!"),
|
|
||||||
hasCancelButton: false,
|
|
||||||
description: (
|
|
||||||
<div>
|
|
||||||
<p>{ _t("End-to-end encryption is in beta and may not be reliable") }.</p>
|
|
||||||
<p>{ _t("You should not yet trust it to secure data") }.</p>
|
|
||||||
<p>{ _t("Devices will not yet be able to decrypt history from before they joined the room") }.</p>
|
|
||||||
<p>{ _t("Encrypted messages will not be visible on clients that do not yet implement encryption") }.</p>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (localStorage) {
|
|
||||||
localStorage.setItem('mx_user_has_used_encryption', true);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_calculatePeekRules: function(room) {
|
_calculatePeekRules: function(room) {
|
||||||
const guestAccessEvent = room.currentState.getStateEvents("m.room.guest_access", "");
|
const guestAccessEvent = room.currentState.getStateEvents("m.room.guest_access", "");
|
||||||
if (guestAccessEvent && guestAccessEvent.getContent().guest_access === "can_join") {
|
if (guestAccessEvent && guestAccessEvent.getContent().guest_access === "can_join") {
|
||||||
|
@ -733,6 +706,23 @@ module.exports = React.createClass({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onDeviceVerificationChanged: function(userId, device) {
|
||||||
|
const room = this.state.room;
|
||||||
|
if (!room.currentState.getMember(userId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._updateE2EStatus(room);
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateE2EStatus: function(room) {
|
||||||
|
if (!MatrixClientPeg.get().isRoomEncrypted(room.roomId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
room.hasUnverifiedDevices().then((hasUnverifiedDevices) => {
|
||||||
|
this.setState({e2eStatus: hasUnverifiedDevices ? "warning" : "verified"});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
updateTint: function() {
|
updateTint: function() {
|
||||||
const room = this.state.room;
|
const room = this.state.room;
|
||||||
if (!room) return;
|
if (!room) return;
|
||||||
|
@ -1236,50 +1226,9 @@ module.exports = React.createClass({
|
||||||
dis.dispatch({ action: 'open_room_settings' });
|
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() {
|
onCancelClick: function() {
|
||||||
console.log("updateTint from onCancelClick");
|
console.log("updateTint from onCancelClick");
|
||||||
this.updateTint();
|
this.updateTint();
|
||||||
dis.dispatch({ action: 'close_settings' });
|
|
||||||
if (this.state.forwardingEvent) {
|
if (this.state.forwardingEvent) {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'forward_event',
|
action: 'forward_event',
|
||||||
|
@ -1437,7 +1386,7 @@ module.exports = React.createClass({
|
||||||
(83 + // height of RoomHeader
|
(83 + // height of RoomHeader
|
||||||
36 + // height of the status area
|
36 + // height of the status area
|
||||||
72 + // minimum height of the message compmoser
|
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
|
// 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
|
// but it's better than the video going missing entirely
|
||||||
|
@ -1537,7 +1486,6 @@ module.exports = React.createClass({
|
||||||
const RoomHeader = sdk.getComponent('rooms.RoomHeader');
|
const RoomHeader = sdk.getComponent('rooms.RoomHeader');
|
||||||
const MessageComposer = sdk.getComponent('rooms.MessageComposer');
|
const MessageComposer = sdk.getComponent('rooms.MessageComposer');
|
||||||
const ForwardMessage = sdk.getComponent("rooms.ForwardMessage");
|
const ForwardMessage = sdk.getComponent("rooms.ForwardMessage");
|
||||||
const RoomSettings = sdk.getComponent("rooms.RoomSettings");
|
|
||||||
const AuxPanel = sdk.getComponent("rooms.AuxPanel");
|
const AuxPanel = sdk.getComponent("rooms.AuxPanel");
|
||||||
const SearchBar = sdk.getComponent("rooms.SearchBar");
|
const SearchBar = sdk.getComponent("rooms.SearchBar");
|
||||||
const PinnedEventsPanel = sdk.getComponent("rooms.PinnedEventsPanel");
|
const PinnedEventsPanel = sdk.getComponent("rooms.PinnedEventsPanel");
|
||||||
|
@ -1575,6 +1523,7 @@ module.exports = React.createClass({
|
||||||
room={this.state.room}
|
room={this.state.room}
|
||||||
oobData={this.props.oobData}
|
oobData={this.props.oobData}
|
||||||
collapsedRhs={this.props.collapsedRhs}
|
collapsedRhs={this.props.collapsedRhs}
|
||||||
|
e2eStatus={this.state.e2eStatus}
|
||||||
/>
|
/>
|
||||||
<div className="mx_RoomView_body">
|
<div className="mx_RoomView_body">
|
||||||
<div className="mx_RoomView_auxPanel">
|
<div className="mx_RoomView_auxPanel">
|
||||||
|
@ -1622,6 +1571,7 @@ module.exports = React.createClass({
|
||||||
ref="header"
|
ref="header"
|
||||||
room={this.state.room}
|
room={this.state.room}
|
||||||
collapsedRhs={this.props.collapsedRhs}
|
collapsedRhs={this.props.collapsedRhs}
|
||||||
|
e2eStatus={this.state.e2eStatus}
|
||||||
/>
|
/>
|
||||||
<div className="mx_RoomView_body">
|
<div className="mx_RoomView_body">
|
||||||
<div className="mx_RoomView_auxPanel">
|
<div className="mx_RoomView_auxPanel">
|
||||||
|
@ -1685,7 +1635,6 @@ module.exports = React.createClass({
|
||||||
);
|
);
|
||||||
|
|
||||||
const showRoomRecoveryReminder = (
|
const showRoomRecoveryReminder = (
|
||||||
SettingsStore.isFeatureEnabled("feature_keybackup") &&
|
|
||||||
SettingsStore.getValue("showRoomRecoveryReminder") &&
|
SettingsStore.getValue("showRoomRecoveryReminder") &&
|
||||||
MatrixClientPeg.get().isRoomEncrypted(this.state.room.roomId) &&
|
MatrixClientPeg.get().isRoomEncrypted(this.state.room.roomId) &&
|
||||||
!MatrixClientPeg.get().getKeyBackupEnabled()
|
!MatrixClientPeg.get().getKeyBackupEnabled()
|
||||||
|
@ -1693,11 +1642,7 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
let aux = null;
|
let aux = null;
|
||||||
let hideCancel = false;
|
let hideCancel = false;
|
||||||
if (this.state.editingRoomSettings) {
|
if (this.state.forwardingEvent !== null) {
|
||||||
aux = <RoomSettings ref="room_settings" onSaveClick={this.onSettingsSaveClick} onCancelClick={this.onCancelClick} room={this.state.room} />;
|
|
||||||
} else if (this.state.uploadingRoomSettings) {
|
|
||||||
aux = <Loader />;
|
|
||||||
} else if (this.state.forwardingEvent !== null) {
|
|
||||||
aux = <ForwardMessage onCancelClick={this.onCancelClick} />;
|
aux = <ForwardMessage onCancelClick={this.onCancelClick} />;
|
||||||
} else if (this.state.searching) {
|
} else if (this.state.searching) {
|
||||||
hideCancel = true; // has own cancel
|
hideCancel = true; // has own cancel
|
||||||
|
@ -1739,7 +1684,7 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
const auxPanel = (
|
const auxPanel = (
|
||||||
<AuxPanel ref="auxPanel" room={this.state.room}
|
<AuxPanel ref="auxPanel" room={this.state.room}
|
||||||
fullHeight={this.state.editingRoomSettings}
|
fullHeight={false}
|
||||||
userId={MatrixClientPeg.get().credentials.userId}
|
userId={MatrixClientPeg.get().credentials.userId}
|
||||||
conferenceHandler={this.props.ConferenceHandler}
|
conferenceHandler={this.props.ConferenceHandler}
|
||||||
draggingFile={this.state.draggingFile}
|
draggingFile={this.state.draggingFile}
|
||||||
|
@ -1747,7 +1692,7 @@ module.exports = React.createClass({
|
||||||
maxHeight={this.state.auxPanelMaxHeight}
|
maxHeight={this.state.auxPanelMaxHeight}
|
||||||
onResize={this.onChildResize}
|
onResize={this.onChildResize}
|
||||||
showApps={this.state.showApps}
|
showApps={this.state.showApps}
|
||||||
hideAppsDrawer={this.state.editingRoomSettings} >
|
hideAppsDrawer={false} >
|
||||||
{ aux }
|
{ aux }
|
||||||
</AuxPanel>
|
</AuxPanel>
|
||||||
);
|
);
|
||||||
|
@ -1767,6 +1712,7 @@ module.exports = React.createClass({
|
||||||
disabled={this.props.disabled}
|
disabled={this.props.disabled}
|
||||||
showApps={this.state.showApps}
|
showApps={this.state.showApps}
|
||||||
uploadAllowed={this.isFileUploadAllowed}
|
uploadAllowed={this.isFileUploadAllowed}
|
||||||
|
e2eStatus={this.state.e2eStatus}
|
||||||
/>;
|
/>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1906,17 +1852,15 @@ module.exports = React.createClass({
|
||||||
<main className={"mx_RoomView" + (inCall ? " mx_RoomView_inCall" : "")} ref="roomView">
|
<main className={"mx_RoomView" + (inCall ? " mx_RoomView_inCall" : "")} ref="roomView">
|
||||||
<RoomHeader ref="header" room={this.state.room} searchInfo={searchInfo}
|
<RoomHeader ref="header" room={this.state.room} searchInfo={searchInfo}
|
||||||
oobData={this.props.oobData}
|
oobData={this.props.oobData}
|
||||||
editing={this.state.editingRoomSettings}
|
|
||||||
saving={this.state.uploadingRoomSettings}
|
|
||||||
inRoom={myMembership === 'join'}
|
inRoom={myMembership === 'join'}
|
||||||
collapsedRhs={this.props.collapsedRhs}
|
collapsedRhs={this.props.collapsedRhs}
|
||||||
onSearchClick={this.onSearchClick}
|
onSearchClick={this.onSearchClick}
|
||||||
onSettingsClick={this.onSettingsClick}
|
onSettingsClick={this.onSettingsClick}
|
||||||
onPinnedClick={this.onPinnedClick}
|
onPinnedClick={this.onPinnedClick}
|
||||||
onSaveClick={this.onSettingsSaveClick}
|
|
||||||
onCancelClick={(aux && !hideCancel) ? this.onCancelClick : null}
|
onCancelClick={(aux && !hideCancel) ? this.onCancelClick : null}
|
||||||
onForgetClick={(myMembership === "leave") ? this.onForgetClick : null}
|
onForgetClick={(myMembership === "leave") ? this.onForgetClick : null}
|
||||||
onLeaveClick={(myMembership === "join") ? this.onLeaveClick : null}
|
onLeaveClick={(myMembership === "join") ? this.onLeaveClick : null}
|
||||||
|
e2eStatus={this.state.e2eStatus}
|
||||||
/>
|
/>
|
||||||
<MainSplit panel={rightPanel} collapsedRhs={this.props.collapsedRhs}>
|
<MainSplit panel={rightPanel} collapsedRhs={this.props.collapsedRhs}>
|
||||||
<div className={fadableSectionClasses}>
|
<div className={fadableSectionClasses}>
|
||||||
|
|
|
@ -169,6 +169,10 @@ module.exports = React.createClass({
|
||||||
//
|
//
|
||||||
// This will also re-check the fill state, in case the paginate was inadequate
|
// This will also re-check the fill state, in case the paginate was inadequate
|
||||||
this.checkScroll();
|
this.checkScroll();
|
||||||
|
|
||||||
|
if (!this.isAtBottom()) {
|
||||||
|
this.clearBlockShrinking();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
componentWillUnmount: function() {
|
||||||
|
|
|
@ -71,7 +71,7 @@ module.exports = React.createClass({
|
||||||
function() {
|
function() {
|
||||||
this.props.onSearch(this.refs.search.value);
|
this.props.onSearch(this.refs.search.value);
|
||||||
},
|
},
|
||||||
100,
|
500,
|
||||||
),
|
),
|
||||||
|
|
||||||
_onKeyDown: function(ev) {
|
_onKeyDown: function(ev) {
|
||||||
|
@ -95,8 +95,13 @@ module.exports = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
const TintableSvg = sdk.getComponent('elements.TintableSvg');
|
// check for collapsed here and
|
||||||
|
// not at parent so we keep
|
||||||
|
// searchTerm in our state
|
||||||
|
// when collapsing and expanding
|
||||||
|
if (this.props.collapsed) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
const clearButton = this.state.searchTerm.length > 0 ?
|
const clearButton = this.state.searchTerm.length > 0 ?
|
||||||
(<AccessibleButton key="button"
|
(<AccessibleButton key="button"
|
||||||
className="mx_SearchBox_closeButton"
|
className="mx_SearchBox_closeButton"
|
||||||
|
|
|
@ -74,7 +74,6 @@ export class TabbedView extends React.Component {
|
||||||
|
|
||||||
const idx = this.props.tabs.indexOf(tab);
|
const idx = this.props.tabs.indexOf(tab);
|
||||||
if (idx === this._getActiveTabIndex()) classes += "mx_TabbedView_tabLabel_active";
|
if (idx === this._getActiveTabIndex()) classes += "mx_TabbedView_tabLabel_active";
|
||||||
if (tab.label === "Visit old settings") classes += "mx_TabbedView_tabLabel_TEMP_HACK";
|
|
||||||
|
|
||||||
let tabIcon = null;
|
let tabIcon = null;
|
||||||
if (tab.icon) {
|
if (tab.icon) {
|
||||||
|
@ -97,8 +96,10 @@ export class TabbedView extends React.Component {
|
||||||
_renderTabPanel(tab) {
|
_renderTabPanel(tab) {
|
||||||
return (
|
return (
|
||||||
<div className="mx_TabbedView_tabPanel" key={"mx_tabpanel_" + tab.label}>
|
<div className="mx_TabbedView_tabPanel" key={"mx_tabpanel_" + tab.label}>
|
||||||
|
<div className='mx_TabbedView_tabPanelContent'>
|
||||||
{tab.body}
|
{tab.body}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,6 @@ import GroupActions from '../../actions/GroupActions';
|
||||||
|
|
||||||
import sdk from '../../index';
|
import sdk from '../../index';
|
||||||
import dis from '../../dispatcher';
|
import dis from '../../dispatcher';
|
||||||
import Modal from '../../Modal';
|
|
||||||
import { _t } from '../../languageHandler';
|
import { _t } from '../../languageHandler';
|
||||||
|
|
||||||
import { Droppable } from 'react-beautiful-dnd';
|
import { Droppable } from 'react-beautiful-dnd';
|
||||||
|
@ -48,8 +47,6 @@ const TagPanel = React.createClass({
|
||||||
this.context.matrixClient.on("Group.myMembership", this._onGroupMyMembership);
|
this.context.matrixClient.on("Group.myMembership", this._onGroupMyMembership);
|
||||||
this.context.matrixClient.on("sync", this._onClientSync);
|
this.context.matrixClient.on("sync", this._onClientSync);
|
||||||
|
|
||||||
this._dispatcherRef = dis.register(this._onAction);
|
|
||||||
|
|
||||||
this._tagOrderStoreToken = TagOrderStore.addListener(() => {
|
this._tagOrderStoreToken = TagOrderStore.addListener(() => {
|
||||||
if (this.unmounted) {
|
if (this.unmounted) {
|
||||||
return;
|
return;
|
||||||
|
@ -70,9 +67,6 @@ const TagPanel = React.createClass({
|
||||||
if (this._filterStoreToken) {
|
if (this._filterStoreToken) {
|
||||||
this._filterStoreToken.remove();
|
this._filterStoreToken.remove();
|
||||||
}
|
}
|
||||||
if (this._dispatcherRef) {
|
|
||||||
dis.unregister(this._dispatcherRef);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_onGroupMyMembership() {
|
_onGroupMyMembership() {
|
||||||
|
@ -106,21 +100,11 @@ const TagPanel = React.createClass({
|
||||||
dis.dispatch({action: 'deselect_tags'});
|
dis.dispatch({action: 'deselect_tags'});
|
||||||
},
|
},
|
||||||
|
|
||||||
_onAction(payload) {
|
|
||||||
if (payload.action === "show_redesign_feedback_dialog") {
|
|
||||||
const RedesignFeedbackDialog =
|
|
||||||
sdk.getComponent("views.dialogs.RedesignFeedbackDialog");
|
|
||||||
Modal.createDialog(RedesignFeedbackDialog);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const GroupsButton = sdk.getComponent('elements.GroupsButton');
|
|
||||||
const DNDTagTile = sdk.getComponent('elements.DNDTagTile');
|
const DNDTagTile = sdk.getComponent('elements.DNDTagTile');
|
||||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||||
const TintableSvg = sdk.getComponent('elements.TintableSvg');
|
const TintableSvg = sdk.getComponent('elements.TintableSvg');
|
||||||
const GeminiScrollbarWrapper = sdk.getComponent("elements.GeminiScrollbarWrapper");
|
const GeminiScrollbarWrapper = sdk.getComponent("elements.GeminiScrollbarWrapper");
|
||||||
const ActionButton = sdk.getComponent("elements.ActionButton");
|
|
||||||
|
|
||||||
const tags = this.state.orderedTags.map((tag, index) => {
|
const tags = this.state.orderedTags.map((tag, index) => {
|
||||||
return <DNDTagTile
|
return <DNDTagTile
|
||||||
|
@ -174,13 +158,6 @@ const TagPanel = React.createClass({
|
||||||
) }
|
) }
|
||||||
</Droppable>
|
</Droppable>
|
||||||
</GeminiScrollbarWrapper>
|
</GeminiScrollbarWrapper>
|
||||||
<div className="mx_TagPanel_divider" />
|
|
||||||
<div className="mx_TagPanel_groupsButton">
|
|
||||||
<GroupsButton />
|
|
||||||
<ActionButton
|
|
||||||
className="mx_TagPanel_report" action="show_redesign_feedback_dialog"
|
|
||||||
label={_t("Report bugs & give feedback")} tooltip={true} />
|
|
||||||
</div>
|
|
||||||
</div>;
|
</div>;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
58
src/components/structures/TagPanelButtons.js
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import sdk from '../../index';
|
||||||
|
import dis from '../../dispatcher';
|
||||||
|
import Modal from '../../Modal';
|
||||||
|
import { _t } from '../../languageHandler';
|
||||||
|
|
||||||
|
const TagPanelButtons = React.createClass({
|
||||||
|
displayName: 'TagPanelButtons',
|
||||||
|
|
||||||
|
|
||||||
|
componentWillMount: function() {
|
||||||
|
this._dispatcherRef = dis.register(this._onAction);
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
if (this._dispatcherRef) {
|
||||||
|
dis.unregister(this._dispatcherRef);
|
||||||
|
this._dispatcherRef = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_onAction(payload) {
|
||||||
|
if (payload.action === "show_redesign_feedback_dialog") {
|
||||||
|
const RedesignFeedbackDialog =
|
||||||
|
sdk.getComponent("views.dialogs.RedesignFeedbackDialog");
|
||||||
|
Modal.createDialog(RedesignFeedbackDialog);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const GroupsButton = sdk.getComponent('elements.GroupsButton');
|
||||||
|
const ActionButton = sdk.getComponent("elements.ActionButton");
|
||||||
|
|
||||||
|
return (<div className="mx_TagPanelButtons">
|
||||||
|
<GroupsButton />
|
||||||
|
<ActionButton
|
||||||
|
className="mx_TagPanelButtons_report" action="show_redesign_feedback_dialog"
|
||||||
|
label={_t("Report bugs & give feedback")} tooltip={true} />
|
||||||
|
</div>);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
export default TagPanelButtons;
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
Copyright 2015, 2016 OpenMarket Ltd
|
||||||
Copyright 2017, 2018 New Vector Ltd
|
Copyright 2017, 2018, 2019 New Vector Ltd
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -25,6 +25,18 @@ import SdkConfig from "../../../SdkConfig";
|
||||||
|
|
||||||
import PasswordReset from "../../../PasswordReset";
|
import PasswordReset from "../../../PasswordReset";
|
||||||
|
|
||||||
|
// Phases
|
||||||
|
// Show controls to configure server details
|
||||||
|
const PHASE_SERVER_DETAILS = 0;
|
||||||
|
// Show the forgot password inputs
|
||||||
|
const PHASE_FORGOT = 1;
|
||||||
|
// Email is in the process of being sent
|
||||||
|
const PHASE_SENDING_EMAIL = 2;
|
||||||
|
// Email has been sent
|
||||||
|
const PHASE_EMAIL_SENT = 3;
|
||||||
|
// User has clicked the link in email and completed reset
|
||||||
|
const PHASE_DONE = 4;
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
displayName: 'ForgotPassword',
|
displayName: 'ForgotPassword',
|
||||||
|
|
||||||
|
@ -47,28 +59,29 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
return {
|
return {
|
||||||
enteredHomeserverUrl: this.props.customHsUrl || this.props.defaultHsUrl,
|
enteredHsUrl: this.props.customHsUrl || this.props.defaultHsUrl,
|
||||||
enteredIdentityServerUrl: this.props.customIsUrl || this.props.defaultIsUrl,
|
enteredIsUrl: this.props.customIsUrl || this.props.defaultIsUrl,
|
||||||
progress: null,
|
phase: PHASE_FORGOT,
|
||||||
password: null,
|
email: "",
|
||||||
password2: null,
|
password: "",
|
||||||
|
password2: "",
|
||||||
errorText: null,
|
errorText: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
submitPasswordReset: function(hsUrl, identityUrl, email, password) {
|
submitPasswordReset: function(hsUrl, identityUrl, email, password) {
|
||||||
this.setState({
|
this.setState({
|
||||||
progress: "sending_email",
|
phase: PHASE_SENDING_EMAIL,
|
||||||
});
|
});
|
||||||
this.reset = new PasswordReset(hsUrl, identityUrl);
|
this.reset = new PasswordReset(hsUrl, identityUrl);
|
||||||
this.reset.resetPassword(email, password).done(() => {
|
this.reset.resetPassword(email, password).done(() => {
|
||||||
this.setState({
|
this.setState({
|
||||||
progress: "sent_email",
|
phase: PHASE_EMAIL_SENT,
|
||||||
});
|
});
|
||||||
}, (err) => {
|
}, (err) => {
|
||||||
this.showErrorDialog(_t('Failed to send email') + ": " + err.message);
|
this.showErrorDialog(_t('Failed to send email') + ": " + err.message);
|
||||||
this.setState({
|
this.setState({
|
||||||
progress: null,
|
phase: PHASE_FORGOT,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -80,7 +93,7 @@ module.exports = React.createClass({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.reset.checkEmailLinkClicked().done((res) => {
|
this.reset.checkEmailLinkClicked().done((res) => {
|
||||||
this.setState({ progress: "complete" });
|
this.setState({ phase: PHASE_DONE });
|
||||||
}, (err) => {
|
}, (err) => {
|
||||||
this.showErrorDialog(err.message);
|
this.showErrorDialog(err.message);
|
||||||
});
|
});
|
||||||
|
@ -126,7 +139,7 @@ module.exports = React.createClass({
|
||||||
onFinished: (confirmed) => {
|
onFinished: (confirmed) => {
|
||||||
if (confirmed) {
|
if (confirmed) {
|
||||||
this.submitPasswordReset(
|
this.submitPasswordReset(
|
||||||
this.state.enteredHomeserverUrl, this.state.enteredIdentityServerUrl,
|
this.state.enteredHsUrl, this.state.enteredIsUrl,
|
||||||
this.state.email, this.state.password,
|
this.state.email, this.state.password,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -153,14 +166,29 @@ module.exports = React.createClass({
|
||||||
onServerConfigChange: function(config) {
|
onServerConfigChange: function(config) {
|
||||||
const newState = {};
|
const newState = {};
|
||||||
if (config.hsUrl !== undefined) {
|
if (config.hsUrl !== undefined) {
|
||||||
newState.enteredHomeserverUrl = config.hsUrl;
|
newState.enteredHsUrl = config.hsUrl;
|
||||||
}
|
}
|
||||||
if (config.isUrl !== undefined) {
|
if (config.isUrl !== undefined) {
|
||||||
newState.enteredIdentityServerUrl = config.isUrl;
|
newState.enteredIsUrl = config.isUrl;
|
||||||
}
|
}
|
||||||
this.setState(newState);
|
this.setState(newState);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onServerDetailsNextPhaseClick(ev) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
this.setState({
|
||||||
|
phase: PHASE_FORGOT,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onEditServerDetailsClick(ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
|
this.setState({
|
||||||
|
phase: PHASE_SERVER_DETAILS,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
onLoginClick: function(ev) {
|
onLoginClick: function(ev) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
|
@ -175,94 +203,151 @@ module.exports = React.createClass({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
renderServerDetails() {
|
||||||
const AuthPage = sdk.getComponent("auth.AuthPage");
|
|
||||||
const AuthHeader = sdk.getComponent("auth.AuthHeader");
|
|
||||||
const AuthBody = sdk.getComponent("auth.AuthBody");
|
|
||||||
const ServerConfig = sdk.getComponent("auth.ServerConfig");
|
const ServerConfig = sdk.getComponent("auth.ServerConfig");
|
||||||
const Spinner = sdk.getComponent("elements.Spinner");
|
const AccessibleButton = sdk.getComponent("elements.AccessibleButton");
|
||||||
|
|
||||||
let resetPasswordJsx;
|
if (SdkConfig.get()['disable_custom_urls']) {
|
||||||
|
return null;
|
||||||
if (this.state.progress === "sending_email") {
|
|
||||||
resetPasswordJsx = <Spinner />;
|
|
||||||
} else if (this.state.progress === "sent_email") {
|
|
||||||
resetPasswordJsx = (
|
|
||||||
<div>
|
|
||||||
{ _t("An email has been sent to %(emailAddress)s. Once you've followed the link it contains, " +
|
|
||||||
"click below.", { emailAddress: this.state.email }) }
|
|
||||||
<br />
|
|
||||||
<input className="mx_Login_submit" type="button" onClick={this.onVerify}
|
|
||||||
value={_t('I have verified my email address')} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
} else if (this.state.progress === "complete") {
|
|
||||||
resetPasswordJsx = (
|
|
||||||
<div>
|
|
||||||
<p>{ _t('Your password has been reset') }.</p>
|
|
||||||
<p>{ _t('You have been logged out of all devices and will no longer receive push notifications. ' +
|
|
||||||
'To re-enable notifications, sign in again on each device') }.</p>
|
|
||||||
<input className="mx_Login_submit" type="button" onClick={this.props.onComplete}
|
|
||||||
value={_t('Return to login screen')} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
let serverConfigSection;
|
|
||||||
if (!SdkConfig.get()['disable_custom_urls']) {
|
|
||||||
serverConfigSection = (
|
|
||||||
<ServerConfig ref="serverConfig"
|
|
||||||
defaultHsUrl={this.props.defaultHsUrl}
|
|
||||||
defaultIsUrl={this.props.defaultIsUrl}
|
|
||||||
customHsUrl={this.props.customHsUrl}
|
|
||||||
customIsUrl={this.props.customIsUrl}
|
|
||||||
onServerConfigChange={this.onServerConfigChange}
|
|
||||||
delayTimeMs={0} />
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return <div>
|
||||||
|
<ServerConfig
|
||||||
|
defaultHsUrl={this.props.defaultHsUrl}
|
||||||
|
defaultIsUrl={this.props.defaultIsUrl}
|
||||||
|
customHsUrl={this.state.enteredHsUrl}
|
||||||
|
customIsUrl={this.state.enteredIsUrl}
|
||||||
|
onServerConfigChange={this.onServerConfigChange}
|
||||||
|
delayTimeMs={0} />
|
||||||
|
<AccessibleButton className="mx_Login_submit"
|
||||||
|
onClick={this.onServerDetailsNextPhaseClick}
|
||||||
|
>
|
||||||
|
{_t("Next")}
|
||||||
|
</AccessibleButton>
|
||||||
|
</div>;
|
||||||
|
},
|
||||||
|
|
||||||
|
renderForgot() {
|
||||||
let errorText = null;
|
let errorText = null;
|
||||||
const err = this.state.errorText || this.props.defaultServerDiscoveryError;
|
const err = this.state.errorText || this.props.defaultServerDiscoveryError;
|
||||||
if (err) {
|
if (err) {
|
||||||
errorText = <div className="mx_Login_error">{ err }</div>;
|
errorText = <div className="mx_Login_error">{ err }</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
resetPasswordJsx = (
|
let yourMatrixAccountText = _t('Your account');
|
||||||
<div>
|
try {
|
||||||
<p>
|
const parsedHsUrl = new URL(this.state.enteredHsUrl);
|
||||||
{ _t('To reset your password, enter the email address linked to your account') }:
|
yourMatrixAccountText = _t('Your account on %(serverName)s', {
|
||||||
</p>
|
serverName: parsedHsUrl.hostname,
|
||||||
<div>
|
});
|
||||||
|
} catch (e) {
|
||||||
|
errorText = <div className="mx_Login_error">{_t(
|
||||||
|
"The homeserver URL %(hsUrl)s doesn't seem to be valid URL. Please " +
|
||||||
|
"enter a valid URL including the protocol prefix.",
|
||||||
|
{
|
||||||
|
hsUrl: this.state.enteredHsUrl,
|
||||||
|
})}</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If custom URLs are allowed, wire up the server details edit link.
|
||||||
|
let editLink = null;
|
||||||
|
if (!SdkConfig.get()['disable_custom_urls']) {
|
||||||
|
editLink = <a className="mx_AuthBody_editServerDetails"
|
||||||
|
href="#" onClick={this.onEditServerDetailsClick}
|
||||||
|
>
|
||||||
|
{_t('Change')}
|
||||||
|
</a>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div>
|
||||||
|
<h3>
|
||||||
|
{yourMatrixAccountText}
|
||||||
|
{editLink}
|
||||||
|
</h3>
|
||||||
|
{errorText}
|
||||||
<form onSubmit={this.onSubmitForm}>
|
<form onSubmit={this.onSubmitForm}>
|
||||||
<input className="mx_Login_field" ref="user" type="text"
|
<div className="mx_AuthBody_fieldRow">
|
||||||
|
<input className="mx_Login_field" type="text"
|
||||||
name="reset_email" // define a name so browser's password autofill gets less confused
|
name="reset_email" // define a name so browser's password autofill gets less confused
|
||||||
value={this.state.email}
|
value={this.state.email}
|
||||||
onChange={this.onInputChanged.bind(this, "email")}
|
onChange={this.onInputChanged.bind(this, "email")}
|
||||||
placeholder={_t('Email address')} autoFocus />
|
placeholder={_t('Email')} autoFocus />
|
||||||
<br />
|
</div>
|
||||||
<input className="mx_Login_field" ref="pass" type="password"
|
<div className="mx_AuthBody_fieldRow">
|
||||||
|
<input className="mx_Login_field" type="password"
|
||||||
name="reset_password"
|
name="reset_password"
|
||||||
value={this.state.password}
|
value={this.state.password}
|
||||||
onChange={this.onInputChanged.bind(this, "password")}
|
onChange={this.onInputChanged.bind(this, "password")}
|
||||||
placeholder={_t('New password')} />
|
placeholder={_t('Password')} />
|
||||||
<br />
|
<input className="mx_Login_field" type="password"
|
||||||
<input className="mx_Login_field" ref="pass" type="password"
|
|
||||||
name="reset_password_confirm"
|
name="reset_password_confirm"
|
||||||
value={this.state.password2}
|
value={this.state.password2}
|
||||||
onChange={this.onInputChanged.bind(this, "password2")}
|
onChange={this.onInputChanged.bind(this, "password2")}
|
||||||
placeholder={_t('Confirm your new password')} />
|
placeholder={_t('Confirm')} />
|
||||||
<br />
|
</div>
|
||||||
|
<span>{_t(
|
||||||
|
'A verification email will be sent to your inbox to confirm ' +
|
||||||
|
'setting your new password.',
|
||||||
|
)}</span>
|
||||||
<input className="mx_Login_submit" type="submit" value={_t('Send Reset Email')} />
|
<input className="mx_Login_submit" type="submit" value={_t('Send Reset Email')} />
|
||||||
</form>
|
</form>
|
||||||
{ serverConfigSection }
|
|
||||||
{ errorText }
|
|
||||||
<a className="mx_AuthBody_changeFlow" onClick={this.onLoginClick} href="#">
|
<a className="mx_AuthBody_changeFlow" onClick={this.onLoginClick} href="#">
|
||||||
{_t('Sign in instead')}
|
{_t('Sign in instead')}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>;
|
||||||
</div>
|
},
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
renderSendingEmail() {
|
||||||
|
const Spinner = sdk.getComponent("elements.Spinner");
|
||||||
|
return <Spinner />;
|
||||||
|
},
|
||||||
|
|
||||||
|
renderEmailSent() {
|
||||||
|
return <div>
|
||||||
|
{_t("An email has been sent to %(emailAddress)s. Once you've followed the " +
|
||||||
|
"link it contains, click below.", { emailAddress: this.state.email })}
|
||||||
|
<br />
|
||||||
|
<input className="mx_Login_submit" type="button" onClick={this.onVerify}
|
||||||
|
value={_t('I have verified my email address')} />
|
||||||
|
</div>;
|
||||||
|
},
|
||||||
|
|
||||||
|
renderDone() {
|
||||||
|
return <div>
|
||||||
|
<p>{_t("Your password has been reset.")}</p>
|
||||||
|
<p>{_t(
|
||||||
|
"You have been logged out of all devices and will no longer receive " +
|
||||||
|
"push notifications. To re-enable notifications, sign in again on each " +
|
||||||
|
"device.",
|
||||||
|
)}</p>
|
||||||
|
<input className="mx_Login_submit" type="button" onClick={this.props.onComplete}
|
||||||
|
value={_t('Return to login screen')} />
|
||||||
|
</div>;
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
const AuthPage = sdk.getComponent("auth.AuthPage");
|
||||||
|
const AuthHeader = sdk.getComponent("auth.AuthHeader");
|
||||||
|
const AuthBody = sdk.getComponent("auth.AuthBody");
|
||||||
|
|
||||||
|
let resetPasswordJsx;
|
||||||
|
switch (this.state.phase) {
|
||||||
|
case PHASE_SERVER_DETAILS:
|
||||||
|
resetPasswordJsx = this.renderServerDetails();
|
||||||
|
break;
|
||||||
|
case PHASE_FORGOT:
|
||||||
|
resetPasswordJsx = this.renderForgot();
|
||||||
|
break;
|
||||||
|
case PHASE_SENDING_EMAIL:
|
||||||
|
resetPasswordJsx = this.renderSendingEmail();
|
||||||
|
break;
|
||||||
|
case PHASE_EMAIL_SENT:
|
||||||
|
resetPasswordJsx = this.renderEmailSent();
|
||||||
|
break;
|
||||||
|
case PHASE_DONE:
|
||||||
|
resetPasswordJsx = this.renderDone();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AuthPage>
|
<AuthPage>
|
||||||
|
|
|
@ -26,7 +26,6 @@ import Login from '../../../Login';
|
||||||
import SdkConfig from '../../../SdkConfig';
|
import SdkConfig from '../../../SdkConfig';
|
||||||
import { messageForResourceLimitError } from '../../../utils/ErrorUtils';
|
import { messageForResourceLimitError } from '../../../utils/ErrorUtils';
|
||||||
import { AutoDiscovery } from "matrix-js-sdk";
|
import { AutoDiscovery } from "matrix-js-sdk";
|
||||||
import * as ServerType from '../../views/auth/ServerTypeSelector';
|
|
||||||
|
|
||||||
// For validating phone numbers without country codes
|
// For validating phone numbers without country codes
|
||||||
const PHONE_NUMBER_REGEX = /^[0-9()\-\s]*$/;
|
const PHONE_NUMBER_REGEX = /^[0-9()\-\s]*$/;
|
||||||
|
@ -37,8 +36,8 @@ const PHASE_SERVER_DETAILS = 0;
|
||||||
// Show the appropriate login flow(s) for the server
|
// Show the appropriate login flow(s) for the server
|
||||||
const PHASE_LOGIN = 1;
|
const PHASE_LOGIN = 1;
|
||||||
|
|
||||||
// Disable phases for now, pending UX discussion on WK discovery
|
// Enable phases for login
|
||||||
const PHASES_ENABLED = false;
|
const PHASES_ENABLED = true;
|
||||||
|
|
||||||
// These are used in several places, and come from the js-sdk's autodiscovery
|
// These are used in several places, and come from the js-sdk's autodiscovery
|
||||||
// stuff. We define them here so that they'll be picked up by i18n.
|
// stuff. We define them here so that they'll be picked up by i18n.
|
||||||
|
@ -77,7 +76,6 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
// login shouldn't care how password recovery is done.
|
// login shouldn't care how password recovery is done.
|
||||||
onForgotPasswordClick: PropTypes.func,
|
onForgotPasswordClick: PropTypes.func,
|
||||||
onCancelClick: PropTypes.func,
|
|
||||||
onServerConfigChange: PropTypes.func.isRequired,
|
onServerConfigChange: PropTypes.func.isRequired,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -87,9 +85,8 @@ module.exports = React.createClass({
|
||||||
errorText: null,
|
errorText: null,
|
||||||
loginIncorrect: false,
|
loginIncorrect: false,
|
||||||
|
|
||||||
serverType: null,
|
enteredHsUrl: this.props.customHsUrl || this.props.defaultHsUrl,
|
||||||
enteredHomeserverUrl: this.props.customHsUrl || this.props.defaultHsUrl,
|
enteredIsUrl: this.props.customIsUrl || this.props.defaultIsUrl,
|
||||||
enteredIdentityServerUrl: this.props.customIsUrl || this.props.defaultIsUrl,
|
|
||||||
|
|
||||||
// used for preserving form values when changing homeserver
|
// used for preserving form values when changing homeserver
|
||||||
username: "",
|
username: "",
|
||||||
|
@ -97,13 +94,11 @@ module.exports = React.createClass({
|
||||||
phoneNumber: "",
|
phoneNumber: "",
|
||||||
|
|
||||||
// Phase of the overall login dialog.
|
// Phase of the overall login dialog.
|
||||||
phase: PHASE_SERVER_DETAILS,
|
phase: PHASE_LOGIN,
|
||||||
// The current login flow, such as password, SSO, etc.
|
// The current login flow, such as password, SSO, etc.
|
||||||
currentFlow: "m.login.password",
|
currentFlow: "m.login.password",
|
||||||
|
|
||||||
// .well-known discovery
|
// .well-known discovery
|
||||||
discoveredHsUrl: "",
|
|
||||||
discoveredIsUrl: "",
|
|
||||||
discoveryError: "",
|
discoveryError: "",
|
||||||
findingHomeserver: false,
|
findingHomeserver: false,
|
||||||
};
|
};
|
||||||
|
@ -160,7 +155,7 @@ module.exports = React.createClass({
|
||||||
// Some error strings only apply for logging in
|
// Some error strings only apply for logging in
|
||||||
const usingEmail = username.indexOf("@") > 0;
|
const usingEmail = username.indexOf("@") > 0;
|
||||||
if (error.httpStatus === 400 && usingEmail) {
|
if (error.httpStatus === 400 && usingEmail) {
|
||||||
errorText = _t('This Home Server does not support login using email address.');
|
errorText = _t('This homeserver does not support login using email address.');
|
||||||
} else if (error.errcode == 'M_RESOURCE_LIMIT_EXCEEDED') {
|
} else if (error.errcode == 'M_RESOURCE_LIMIT_EXCEEDED') {
|
||||||
const errorTop = messageForResourceLimitError(
|
const errorTop = messageForResourceLimitError(
|
||||||
error.data.limit_type,
|
error.data.limit_type,
|
||||||
|
@ -241,7 +236,7 @@ module.exports = React.createClass({
|
||||||
}, function(error) {
|
}, function(error) {
|
||||||
let errorText;
|
let errorText;
|
||||||
if (error.httpStatus === 403) {
|
if (error.httpStatus === 403) {
|
||||||
errorText = _t("Guest access is disabled on this Home Server.");
|
errorText = _t("Guest access is disabled on this homeserver.");
|
||||||
} else {
|
} else {
|
||||||
errorText = self._errorTextFromError(error);
|
errorText = self._errorTextFromError(error);
|
||||||
}
|
}
|
||||||
|
@ -308,10 +303,10 @@ module.exports = React.createClass({
|
||||||
errorText: null, // reset err messages
|
errorText: null, // reset err messages
|
||||||
};
|
};
|
||||||
if (config.hsUrl !== undefined) {
|
if (config.hsUrl !== undefined) {
|
||||||
newState.enteredHomeserverUrl = config.hsUrl;
|
newState.enteredHsUrl = config.hsUrl;
|
||||||
}
|
}
|
||||||
if (config.isUrl !== undefined) {
|
if (config.isUrl !== undefined) {
|
||||||
newState.enteredIdentityServerUrl = config.isUrl;
|
newState.enteredIsUrl = config.isUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.props.onServerConfigChange(config);
|
this.props.onServerConfigChange(config);
|
||||||
|
@ -320,39 +315,6 @@ module.exports = React.createClass({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
onServerTypeChange(type) {
|
|
||||||
this.setState({
|
|
||||||
serverType: type,
|
|
||||||
});
|
|
||||||
|
|
||||||
// When changing server types, set the HS / IS URLs to reasonable defaults for the
|
|
||||||
// the new type.
|
|
||||||
switch (type) {
|
|
||||||
case ServerType.FREE: {
|
|
||||||
const { hsUrl, isUrl } = ServerType.TYPES.FREE;
|
|
||||||
this.onServerConfigChange({
|
|
||||||
hsUrl,
|
|
||||||
isUrl,
|
|
||||||
});
|
|
||||||
// Move directly to the login phase since the server details are fixed.
|
|
||||||
this.setState({
|
|
||||||
phase: PHASE_LOGIN,
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ServerType.PREMIUM:
|
|
||||||
case ServerType.ADVANCED:
|
|
||||||
this.onServerConfigChange({
|
|
||||||
hsUrl: this.props.defaultHsUrl,
|
|
||||||
isUrl: this.props.defaultIsUrl,
|
|
||||||
});
|
|
||||||
this.setState({
|
|
||||||
phase: PHASE_SERVER_DETAILS,
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
onRegisterClick: function(ev) {
|
onRegisterClick: function(ev) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
|
@ -377,43 +339,42 @@ module.exports = React.createClass({
|
||||||
_tryWellKnownDiscovery: async function(serverName) {
|
_tryWellKnownDiscovery: async function(serverName) {
|
||||||
if (!serverName.trim()) {
|
if (!serverName.trim()) {
|
||||||
// Nothing to discover
|
// Nothing to discover
|
||||||
this.setState({discoveryError: "", discoveredHsUrl: "", discoveredIsUrl: "", findingHomeserver: false});
|
this.setState({
|
||||||
|
discoveryError: "",
|
||||||
|
findingHomeserver: false,
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({findingHomeserver: true});
|
this.setState({findingHomeserver: true});
|
||||||
try {
|
try {
|
||||||
const discovery = await AutoDiscovery.findClientConfig(serverName);
|
const discovery = await AutoDiscovery.findClientConfig(serverName);
|
||||||
|
|
||||||
const state = discovery["m.homeserver"].state;
|
const state = discovery["m.homeserver"].state;
|
||||||
if (state !== AutoDiscovery.SUCCESS && state !== AutoDiscovery.PROMPT) {
|
if (state !== AutoDiscovery.SUCCESS && state !== AutoDiscovery.PROMPT) {
|
||||||
this.setState({
|
this.setState({
|
||||||
discoveredHsUrl: "",
|
|
||||||
discoveredIsUrl: "",
|
|
||||||
discoveryError: discovery["m.homeserver"].error,
|
discoveryError: discovery["m.homeserver"].error,
|
||||||
findingHomeserver: false,
|
findingHomeserver: false,
|
||||||
});
|
});
|
||||||
} else if (state === AutoDiscovery.PROMPT) {
|
} else if (state === AutoDiscovery.PROMPT) {
|
||||||
this.setState({
|
this.setState({
|
||||||
discoveredHsUrl: "",
|
|
||||||
discoveredIsUrl: "",
|
|
||||||
discoveryError: "",
|
discoveryError: "",
|
||||||
findingHomeserver: false,
|
findingHomeserver: false,
|
||||||
});
|
});
|
||||||
} else if (state === AutoDiscovery.SUCCESS) {
|
} else if (state === AutoDiscovery.SUCCESS) {
|
||||||
this.setState({
|
this.setState({
|
||||||
discoveredHsUrl: discovery["m.homeserver"].base_url,
|
|
||||||
discoveredIsUrl:
|
|
||||||
discovery["m.identity_server"].state === AutoDiscovery.SUCCESS
|
|
||||||
? discovery["m.identity_server"].base_url
|
|
||||||
: "",
|
|
||||||
discoveryError: "",
|
discoveryError: "",
|
||||||
findingHomeserver: false,
|
findingHomeserver: false,
|
||||||
});
|
});
|
||||||
|
this.onServerConfigChange({
|
||||||
|
hsUrl: discovery["m.homeserver"].base_url,
|
||||||
|
isUrl: discovery["m.identity_server"].state === AutoDiscovery.SUCCESS
|
||||||
|
? discovery["m.identity_server"].base_url
|
||||||
|
: "",
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
console.warn("Unknown state for m.homeserver in discovery response: ", discovery);
|
console.warn("Unknown state for m.homeserver in discovery response: ", discovery);
|
||||||
this.setState({
|
this.setState({
|
||||||
discoveredHsUrl: "",
|
|
||||||
discoveredIsUrl: "",
|
|
||||||
discoveryError: _t("Unknown failure discovering homeserver"),
|
discoveryError: _t("Unknown failure discovering homeserver"),
|
||||||
findingHomeserver: false,
|
findingHomeserver: false,
|
||||||
});
|
});
|
||||||
|
@ -429,8 +390,8 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
_initLoginLogic: function(hsUrl, isUrl) {
|
_initLoginLogic: function(hsUrl, isUrl) {
|
||||||
const self = this;
|
const self = this;
|
||||||
hsUrl = hsUrl || this.state.enteredHomeserverUrl;
|
hsUrl = hsUrl || this.state.enteredHsUrl;
|
||||||
isUrl = isUrl || this.state.enteredIdentityServerUrl;
|
isUrl = isUrl || this.state.enteredIsUrl;
|
||||||
|
|
||||||
const fallbackHsUrl = hsUrl === this.props.defaultHsUrl ? this.props.fallbackHsUrl : null;
|
const fallbackHsUrl = hsUrl === this.props.defaultHsUrl ? this.props.fallbackHsUrl : null;
|
||||||
|
|
||||||
|
@ -440,8 +401,8 @@ module.exports = React.createClass({
|
||||||
this._loginLogic = loginLogic;
|
this._loginLogic = loginLogic;
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
enteredHomeserverUrl: hsUrl,
|
enteredHsUrl: hsUrl,
|
||||||
enteredIdentityServerUrl: isUrl,
|
enteredIsUrl: isUrl,
|
||||||
busy: true,
|
busy: true,
|
||||||
loginIncorrect: false,
|
loginIncorrect: false,
|
||||||
});
|
});
|
||||||
|
@ -507,8 +468,8 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
if (err.cors === 'rejected') {
|
if (err.cors === 'rejected') {
|
||||||
if (window.location.protocol === 'https:' &&
|
if (window.location.protocol === 'https:' &&
|
||||||
(this.state.enteredHomeserverUrl.startsWith("http:") ||
|
(this.state.enteredHsUrl.startsWith("http:") ||
|
||||||
!this.state.enteredHomeserverUrl.startsWith("http"))
|
!this.state.enteredHsUrl.startsWith("http"))
|
||||||
) {
|
) {
|
||||||
errorText = <span>
|
errorText = <span>
|
||||||
{ _t("Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. " +
|
{ _t("Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. " +
|
||||||
|
@ -532,7 +493,7 @@ module.exports = React.createClass({
|
||||||
{
|
{
|
||||||
'a': (sub) => {
|
'a': (sub) => {
|
||||||
return <a target="_blank" rel="noopener"
|
return <a target="_blank" rel="noopener"
|
||||||
href={this.state.enteredHomeserverUrl}
|
href={this.state.enteredHsUrl}
|
||||||
>{ sub }</a>;
|
>{ sub }</a>;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -544,52 +505,26 @@ module.exports = React.createClass({
|
||||||
return errorText;
|
return errorText;
|
||||||
},
|
},
|
||||||
|
|
||||||
renderServerComponentForStep() {
|
renderServerComponent() {
|
||||||
const ServerTypeSelector = sdk.getComponent("auth.ServerTypeSelector");
|
|
||||||
const ServerConfig = sdk.getComponent("auth.ServerConfig");
|
const ServerConfig = sdk.getComponent("auth.ServerConfig");
|
||||||
const ModularServerConfig = sdk.getComponent("auth.ModularServerConfig");
|
|
||||||
const AccessibleButton = sdk.getComponent("elements.AccessibleButton");
|
const AccessibleButton = sdk.getComponent("elements.AccessibleButton");
|
||||||
|
|
||||||
// TODO: May need to adjust the behavior of this config option
|
|
||||||
if (SdkConfig.get()['disable_custom_urls']) {
|
if (SdkConfig.get()['disable_custom_urls']) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're on a different phase, we only show the server type selector,
|
|
||||||
// which is always shown if we allow custom URLs at all.
|
|
||||||
if (PHASES_ENABLED && this.state.phase !== PHASE_SERVER_DETAILS) {
|
if (PHASES_ENABLED && this.state.phase !== PHASE_SERVER_DETAILS) {
|
||||||
return <div>
|
return null;
|
||||||
<ServerTypeSelector
|
|
||||||
defaultHsUrl={this.props.defaultHsUrl}
|
|
||||||
onChange={this.onServerTypeChange}
|
|
||||||
/>
|
|
||||||
</div>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let serverDetails = null;
|
const serverDetails = <ServerConfig
|
||||||
switch (this.state.serverType) {
|
customHsUrl={this.state.enteredHsUrl}
|
||||||
case ServerType.FREE:
|
customIsUrl={this.state.enteredIsUrl}
|
||||||
break;
|
|
||||||
case ServerType.PREMIUM:
|
|
||||||
serverDetails = <ModularServerConfig
|
|
||||||
customHsUrl={this.state.discoveredHsUrl || this.props.customHsUrl}
|
|
||||||
defaultHsUrl={this.props.defaultHsUrl}
|
defaultHsUrl={this.props.defaultHsUrl}
|
||||||
defaultIsUrl={this.props.defaultIsUrl}
|
defaultIsUrl={this.props.defaultIsUrl}
|
||||||
onServerConfigChange={this.onServerConfigChange}
|
onServerConfigChange={this.onServerConfigChange}
|
||||||
delayTimeMs={250}
|
delayTimeMs={250}
|
||||||
/>;
|
/>;
|
||||||
break;
|
|
||||||
case ServerType.ADVANCED:
|
|
||||||
serverDetails = <ServerConfig
|
|
||||||
customHsUrl={this.state.discoveredHsUrl || this.props.customHsUrl}
|
|
||||||
customIsUrl={this.state.discoveredIsUrl || this.props.customIsUrl}
|
|
||||||
defaultHsUrl={this.props.defaultHsUrl}
|
|
||||||
defaultIsUrl={this.props.defaultIsUrl}
|
|
||||||
onServerConfigChange={this.onServerConfigChange}
|
|
||||||
delayTimeMs={250}
|
|
||||||
/>;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
let nextButton = null;
|
let nextButton = null;
|
||||||
if (PHASES_ENABLED) {
|
if (PHASES_ENABLED) {
|
||||||
|
@ -601,10 +536,6 @@ module.exports = React.createClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div>
|
return <div>
|
||||||
<ServerTypeSelector
|
|
||||||
defaultHsUrl={this.props.defaultHsUrl}
|
|
||||||
onChange={this.onServerTypeChange}
|
|
||||||
/>
|
|
||||||
{serverDetails}
|
{serverDetails}
|
||||||
{nextButton}
|
{nextButton}
|
||||||
</div>;
|
</div>;
|
||||||
|
@ -633,13 +564,8 @@ module.exports = React.createClass({
|
||||||
_renderPasswordStep: function() {
|
_renderPasswordStep: function() {
|
||||||
const PasswordLogin = sdk.getComponent('auth.PasswordLogin');
|
const PasswordLogin = sdk.getComponent('auth.PasswordLogin');
|
||||||
let onEditServerDetailsClick = null;
|
let onEditServerDetailsClick = null;
|
||||||
// If custom URLs are allowed and we haven't selected the Free server type, wire
|
// If custom URLs are allowed, wire up the server details edit link.
|
||||||
// up the server details edit link.
|
if (PHASES_ENABLED && !SdkConfig.get()['disable_custom_urls']) {
|
||||||
if (
|
|
||||||
PHASES_ENABLED &&
|
|
||||||
!SdkConfig.get()['disable_custom_urls'] &&
|
|
||||||
this.state.serverType !== ServerType.FREE
|
|
||||||
) {
|
|
||||||
onEditServerDetailsClick = this.onEditServerDetailsClick;
|
onEditServerDetailsClick = this.onEditServerDetailsClick;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
|
@ -657,7 +583,7 @@ module.exports = React.createClass({
|
||||||
onPhoneNumberBlur={this.onPhoneNumberBlur}
|
onPhoneNumberBlur={this.onPhoneNumberBlur}
|
||||||
onForgotPasswordClick={this.props.onForgotPasswordClick}
|
onForgotPasswordClick={this.props.onForgotPasswordClick}
|
||||||
loginIncorrect={this.state.loginIncorrect}
|
loginIncorrect={this.state.loginIncorrect}
|
||||||
hsUrl={this.state.enteredHomeserverUrl}
|
hsUrl={this.state.enteredHsUrl}
|
||||||
disableSubmit={this.state.findingHomeserver}
|
disableSubmit={this.state.findingHomeserver}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -708,11 +634,11 @@ module.exports = React.createClass({
|
||||||
<AuthHeader />
|
<AuthHeader />
|
||||||
<AuthBody>
|
<AuthBody>
|
||||||
<h2>
|
<h2>
|
||||||
{_t('Sign in to your account')}
|
{_t('Sign in')}
|
||||||
{loader}
|
{loader}
|
||||||
</h2>
|
</h2>
|
||||||
{ errorTextSection }
|
{ errorTextSection }
|
||||||
{ this.renderServerComponentForStep() }
|
{ this.renderServerComponent() }
|
||||||
{ this.renderLoginComponentForStep() }
|
{ this.renderLoginComponentForStep() }
|
||||||
<a className="mx_AuthBody_changeFlow" onClick={this.onRegisterClick} href="#">
|
<a className="mx_AuthBody_changeFlow" onClick={this.onRegisterClick} href="#">
|
||||||
{ _t('Create account') }
|
{ _t('Create account') }
|
||||||
|
|
|
@ -54,21 +54,17 @@ module.exports = React.createClass({
|
||||||
defaultIsUrl: PropTypes.string,
|
defaultIsUrl: PropTypes.string,
|
||||||
brand: PropTypes.string,
|
brand: PropTypes.string,
|
||||||
email: PropTypes.string,
|
email: PropTypes.string,
|
||||||
referrer: PropTypes.string,
|
|
||||||
|
|
||||||
// An error passed along from higher up explaining that something
|
// An error passed along from higher up explaining that something
|
||||||
// went wrong when finding the defaultHsUrl.
|
// went wrong when finding the defaultHsUrl.
|
||||||
defaultServerDiscoveryError: PropTypes.string,
|
defaultServerDiscoveryError: PropTypes.string,
|
||||||
|
|
||||||
defaultDeviceDisplayName: PropTypes.string,
|
|
||||||
|
|
||||||
// registration shouldn't know or care how login is done.
|
// registration shouldn't know or care how login is done.
|
||||||
onLoginClick: PropTypes.func.isRequired,
|
onLoginClick: PropTypes.func.isRequired,
|
||||||
onCancelClick: PropTypes.func,
|
|
||||||
onServerConfigChange: PropTypes.func.isRequired,
|
onServerConfigChange: PropTypes.func.isRequired,
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
|
const customURLsAllowed = !SdkConfig.get()['disable_custom_urls'];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
busy: false,
|
busy: false,
|
||||||
errorText: null,
|
errorText: null,
|
||||||
|
@ -90,6 +86,8 @@ module.exports = React.createClass({
|
||||||
serverType: null,
|
serverType: null,
|
||||||
hsUrl: this.props.customHsUrl,
|
hsUrl: this.props.customHsUrl,
|
||||||
isUrl: this.props.customIsUrl,
|
isUrl: this.props.customIsUrl,
|
||||||
|
// Phase of the overall registration dialog.
|
||||||
|
phase: customURLsAllowed ? PHASE_SERVER_DETAILS : PHASE_REGISTRATION,
|
||||||
flows: null,
|
flows: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
@ -164,9 +162,13 @@ module.exports = React.createClass({
|
||||||
this.setState({
|
this.setState({
|
||||||
flows: e.data.flows,
|
flows: e.data.flows,
|
||||||
});
|
});
|
||||||
|
} else if (e.httpStatus === 403 && e.errcode === "M_UNKNOWN") {
|
||||||
|
this.setState({
|
||||||
|
errorText: _t("Registration has been disabled on this homeserver."),
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
this.setState({
|
this.setState({
|
||||||
errorText: _t("Unable to query for supported registration methods"),
|
errorText: _t("Unable to query for supported registration methods."),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -363,7 +365,6 @@ module.exports = React.createClass({
|
||||||
const ModularServerConfig = sdk.getComponent("auth.ModularServerConfig");
|
const ModularServerConfig = sdk.getComponent("auth.ModularServerConfig");
|
||||||
const AccessibleButton = sdk.getComponent("elements.AccessibleButton");
|
const AccessibleButton = sdk.getComponent("elements.AccessibleButton");
|
||||||
|
|
||||||
// TODO: May need to adjust the behavior of this config option
|
|
||||||
if (SdkConfig.get()['disable_custom_urls']) {
|
if (SdkConfig.get()['disable_custom_urls']) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -141,7 +141,7 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref="recaptchaContainer">
|
<div ref="recaptchaContainer">
|
||||||
{ _t("This Home Server would like to make sure you are not a robot") }
|
{ _t("This homeserver would like to make sure you are not a robot.") }
|
||||||
<br />
|
<br />
|
||||||
<div id={DIV_ID}></div>
|
<div id={DIV_ID}></div>
|
||||||
{ error }
|
{ error }
|
||||||
|
|
|
@ -334,7 +334,7 @@ export const TermsAuthEntry = React.createClass({
|
||||||
let submitButton;
|
let submitButton;
|
||||||
if (this.props.showContinue !== false) {
|
if (this.props.showContinue !== false) {
|
||||||
// XXX: button classes
|
// XXX: button classes
|
||||||
submitButton = <button className="mx_InteractiveAuthEntryComponents_termsSubmit mx_UserSettings_button"
|
submitButton = <button className="mx_InteractiveAuthEntryComponents_termsSubmit mx_GeneralButton"
|
||||||
onClick={this._trySubmit} disabled={!allChecked}>{_t("Accept")}</button>;
|
onClick={this._trySubmit} disabled={!allChecked}>{_t("Accept")}</button>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -525,7 +525,7 @@ export const MsisdnAuthEntry = React.createClass({
|
||||||
const enableSubmit = Boolean(this.state.token);
|
const enableSubmit = Boolean(this.state.token);
|
||||||
const submitClasses = classnames({
|
const submitClasses = classnames({
|
||||||
mx_InteractiveAuthEntryComponents_msisdnSubmit: true,
|
mx_InteractiveAuthEntryComponents_msisdnSubmit: true,
|
||||||
mx_UserSettings_button: true, // XXX button classes
|
mx_GeneralButton: true,
|
||||||
});
|
});
|
||||||
let errorSection;
|
let errorSection;
|
||||||
if (this.state.errorText) {
|
if (this.state.errorText) {
|
||||||
|
|
|
@ -249,10 +249,10 @@ class PasswordLogin extends React.Component {
|
||||||
</span>;
|
</span>;
|
||||||
}
|
}
|
||||||
|
|
||||||
let yourMatrixAccountText = _t('Your account');
|
let signInToText = _t('Sign in');
|
||||||
try {
|
try {
|
||||||
const parsedHsUrl = new URL(this.props.hsUrl);
|
const parsedHsUrl = new URL(this.props.hsUrl);
|
||||||
yourMatrixAccountText = _t('Your %(serverName)s account', {
|
signInToText = _t('Sign in to %(serverName)s', {
|
||||||
serverName: parsedHsUrl.hostname,
|
serverName: parsedHsUrl.hostname,
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -264,7 +264,7 @@ class PasswordLogin extends React.Component {
|
||||||
editLink = <a className="mx_AuthBody_editServerDetails"
|
editLink = <a className="mx_AuthBody_editServerDetails"
|
||||||
href="#" onClick={this.props.onEditServerDetailsClick}
|
href="#" onClick={this.props.onEditServerDetailsClick}
|
||||||
>
|
>
|
||||||
{_t('Edit')}
|
{_t('Change')}
|
||||||
</a>;
|
</a>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -297,7 +297,7 @@ class PasswordLogin extends React.Component {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h3>
|
<h3>
|
||||||
{yourMatrixAccountText}
|
{signInToText}
|
||||||
{editLink}
|
{editLink}
|
||||||
</h3>
|
</h3>
|
||||||
<form onSubmit={this.onSubmitForm}>
|
<form onSubmit={this.onSubmitForm}>
|
||||||
|
|
|
@ -270,11 +270,27 @@ module.exports = React.createClass({
|
||||||
this.validateField(FIELD_USERNAME, ev.type);
|
this.validateField(FIELD_USERNAME, ev.type);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A step is required if all flows include that step.
|
||||||
|
*
|
||||||
|
* @param {string} step A stage name to check
|
||||||
|
* @returns {boolean} Whether it is required
|
||||||
|
*/
|
||||||
_authStepIsRequired(step) {
|
_authStepIsRequired(step) {
|
||||||
// A step is required if no flow exists which does not include that step
|
return this.props.flows.every((flow) => {
|
||||||
// (Notwithstanding setups like either email or msisdn being required)
|
return flow.stages.includes(step);
|
||||||
return !this.props.flows.some((flow) => {
|
});
|
||||||
return !flow.stages.includes(step);
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A step is used if any flows include that step.
|
||||||
|
*
|
||||||
|
* @param {string} step A stage name to check
|
||||||
|
* @returns {boolean} Whether it is used
|
||||||
|
*/
|
||||||
|
_authStepIsUsed(step) {
|
||||||
|
return this.props.flows.some((flow) => {
|
||||||
|
return flow.stages.includes(step);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -298,24 +314,28 @@ module.exports = React.createClass({
|
||||||
</a>;
|
</a>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let emailSection;
|
||||||
|
if (this._authStepIsUsed('m.login.email.identity')) {
|
||||||
const emailPlaceholder = this._authStepIsRequired('m.login.email.identity') ?
|
const emailPlaceholder = this._authStepIsRequired('m.login.email.identity') ?
|
||||||
_t("Email") :
|
_t("Email") :
|
||||||
_t("Email (optional)");
|
_t("Email (optional)");
|
||||||
|
|
||||||
const emailSection = (
|
emailSection = (
|
||||||
<div>
|
<div>
|
||||||
<input type="text" ref="email"
|
<input type="text" ref="email"
|
||||||
autoFocus={true} placeholder={emailPlaceholder}
|
placeholder={emailPlaceholder}
|
||||||
defaultValue={this.props.defaultEmail}
|
defaultValue={this.props.defaultEmail}
|
||||||
className={this._classForField(FIELD_EMAIL, 'mx_Login_field')}
|
className={this._classForField(FIELD_EMAIL, 'mx_Login_field')}
|
||||||
onBlur={this.onEmailBlur}
|
onBlur={this.onEmailBlur}
|
||||||
value={this.state.email} />
|
value={this.state.email} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const threePidLogin = !SdkConfig.get().disable_3pid_login;
|
||||||
const CountryDropdown = sdk.getComponent('views.auth.CountryDropdown');
|
const CountryDropdown = sdk.getComponent('views.auth.CountryDropdown');
|
||||||
let phoneSection;
|
let phoneSection;
|
||||||
if (!SdkConfig.get().disable_3pid_login) {
|
if (threePidLogin && this._authStepIsUsed('m.login.msisdn')) {
|
||||||
const phonePlaceholder = this._authStepIsRequired('m.login.msisdn') ?
|
const phonePlaceholder = this._authStepIsRequired('m.login.msisdn') ?
|
||||||
_t("Phone") :
|
_t("Phone") :
|
||||||
_t("Phone (optional)");
|
_t("Phone (optional)");
|
||||||
|
@ -359,6 +379,7 @@ module.exports = React.createClass({
|
||||||
<form onSubmit={this.onSubmit}>
|
<form onSubmit={this.onSubmit}>
|
||||||
<div className="mx_AuthBody_fieldRow">
|
<div className="mx_AuthBody_fieldRow">
|
||||||
<input type="text" ref="username"
|
<input type="text" ref="username"
|
||||||
|
autoFocus={true}
|
||||||
placeholder={placeholderUsername} defaultValue={this.props.defaultUsername}
|
placeholder={placeholderUsername} defaultValue={this.props.defaultUsername}
|
||||||
className={this._classForField(FIELD_USERNAME, 'mx_Login_field')}
|
className={this._classForField(FIELD_USERNAME, 'mx_Login_field')}
|
||||||
onBlur={this.onUsernameBlur} />
|
onBlur={this.onUsernameBlur} />
|
||||||
|
@ -379,7 +400,7 @@ module.exports = React.createClass({
|
||||||
{ phoneSection }
|
{ phoneSection }
|
||||||
</div>
|
</div>
|
||||||
{_t(
|
{_t(
|
||||||
"Use an email address to receover your account. Other users " +
|
"Use an email address to recover your account. Other users " +
|
||||||
"can invite you to rooms using your contact details.",
|
"can invite you to rooms using your contact details.",
|
||||||
)}
|
)}
|
||||||
{ registerButton }
|
{ registerButton }
|
||||||
|
|
47
src/components/views/auth/Welcome.js
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import sdk from '../../../index';
|
||||||
|
import SdkConfig from '../../../SdkConfig';
|
||||||
|
|
||||||
|
export default class Welcome extends React.PureComponent {
|
||||||
|
render() {
|
||||||
|
const AuthPage = sdk.getComponent("auth.AuthPage");
|
||||||
|
const EmbeddedPage = sdk.getComponent('structures.EmbeddedPage');
|
||||||
|
const LanguageSelector = sdk.getComponent('auth.LanguageSelector');
|
||||||
|
|
||||||
|
const pagesConfig = SdkConfig.get().embeddedPages;
|
||||||
|
let pageUrl = null;
|
||||||
|
if (pagesConfig) {
|
||||||
|
pageUrl = pagesConfig.welcomeUrl;
|
||||||
|
}
|
||||||
|
if (!pageUrl) {
|
||||||
|
pageUrl = 'welcome.html';
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AuthPage>
|
||||||
|
<div className="mx_Welcome">
|
||||||
|
<EmbeddedPage className="mx_WelcomePage"
|
||||||
|
url={pageUrl}
|
||||||
|
/>
|
||||||
|
<LanguageSelector />
|
||||||
|
</div>
|
||||||
|
</AuthPage>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -158,7 +158,8 @@ module.exports = React.createClass({
|
||||||
<BaseAvatar {...otherProps} name={roomName}
|
<BaseAvatar {...otherProps} name={roomName}
|
||||||
idName={room ? room.roomId : null}
|
idName={room ? room.roomId : null}
|
||||||
urls={this.state.urls}
|
urls={this.state.urls}
|
||||||
onClick={this.props.viewAvatarOnClick ? this.onRoomAvatarClick : null} />
|
onClick={this.props.viewAvatarOnClick ? this.onRoomAvatarClick : null}
|
||||||
|
disabled={!this.state.urls[0]} />
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|