Merge remote-tracking branch 'upstream/develop' into fix/bubble-bg-color/18081
119
CHANGELOG.md
@ -1,3 +1,122 @@
|
|||||||
|
Changes in [3.27.0](https://github.com/vector-im/element-desktop/releases/tag/v3.27.0) (2021-07-02)
|
||||||
|
===================================================================================================
|
||||||
|
|
||||||
|
## 🔒 SECURITY FIXES
|
||||||
|
* Sanitize untrusted variables from message previews before translation
|
||||||
|
Fixes vector-im/element-web#18314
|
||||||
|
|
||||||
|
## ✨ Features
|
||||||
|
* Fix editing of `<sub>` & `<sup`> & `<u>`
|
||||||
|
[\#6469](https://github.com/matrix-org/matrix-react-sdk/pull/6469)
|
||||||
|
Fixes vector-im/element-web#18211
|
||||||
|
* Zoom images in lightbox to where the cursor points
|
||||||
|
[\#6418](https://github.com/matrix-org/matrix-react-sdk/pull/6418)
|
||||||
|
Fixes vector-im/element-web#17870
|
||||||
|
* Avoid hitting the settings store from TextForEvent
|
||||||
|
[\#6205](https://github.com/matrix-org/matrix-react-sdk/pull/6205)
|
||||||
|
Fixes vector-im/element-web#17650
|
||||||
|
* Initial MSC3083 + MSC3244 support
|
||||||
|
[\#6212](https://github.com/matrix-org/matrix-react-sdk/pull/6212)
|
||||||
|
Fixes vector-im/element-web#17686 and vector-im/element-web#17661
|
||||||
|
* Navigate to the first room with notifications when clicked on space notification dot
|
||||||
|
[\#5974](https://github.com/matrix-org/matrix-react-sdk/pull/5974)
|
||||||
|
* Add matrix: to the list of permitted URL schemes
|
||||||
|
[\#6388](https://github.com/matrix-org/matrix-react-sdk/pull/6388)
|
||||||
|
* Add "Copy Link" to room context menu
|
||||||
|
[\#6374](https://github.com/matrix-org/matrix-react-sdk/pull/6374)
|
||||||
|
* 💭 Message bubble layout
|
||||||
|
[\#6291](https://github.com/matrix-org/matrix-react-sdk/pull/6291)
|
||||||
|
Fixes vector-im/element-web#4635, vector-im/element-web#17773 vector-im/element-web#16220 and vector-im/element-web#7687
|
||||||
|
* Play only one audio file at a time
|
||||||
|
[\#6417](https://github.com/matrix-org/matrix-react-sdk/pull/6417)
|
||||||
|
Fixes vector-im/element-web#17439
|
||||||
|
* Move download button for media to the action bar
|
||||||
|
[\#6386](https://github.com/matrix-org/matrix-react-sdk/pull/6386)
|
||||||
|
Fixes vector-im/element-web#17943
|
||||||
|
* Improved display of one-to-one call history with summary boxes for each call
|
||||||
|
[\#6121](https://github.com/matrix-org/matrix-react-sdk/pull/6121)
|
||||||
|
Fixes vector-im/element-web#16409
|
||||||
|
* Notification settings UI refresh
|
||||||
|
[\#6352](https://github.com/matrix-org/matrix-react-sdk/pull/6352)
|
||||||
|
Fixes vector-im/element-web#17782
|
||||||
|
* Fix EventIndex double handling events and erroring
|
||||||
|
[\#6385](https://github.com/matrix-org/matrix-react-sdk/pull/6385)
|
||||||
|
Fixes vector-im/element-web#18008
|
||||||
|
* Improve reply rendering
|
||||||
|
[\#3553](https://github.com/matrix-org/matrix-react-sdk/pull/3553)
|
||||||
|
Fixes vector-im/riot-web#9217, vector-im/riot-web#7633, vector-im/riot-web#7530, vector-im/riot-web#7169, vector-im/riot-web#7151, vector-im/riot-web#6692 vector-im/riot-web#6579 and vector-im/element-web#17440
|
||||||
|
|
||||||
|
## 🐛 Bug Fixes
|
||||||
|
* Fix CreateRoomDialog exploding when making public room outside of a space
|
||||||
|
[\#6493](https://github.com/matrix-org/matrix-react-sdk/pull/6493)
|
||||||
|
* Fix regression where registration would soft-crash on captcha
|
||||||
|
[\#6505](https://github.com/matrix-org/matrix-react-sdk/pull/6505)
|
||||||
|
Fixes vector-im/element-web#18284
|
||||||
|
* only send join rule event if we have a join rule to put in it
|
||||||
|
[\#6517](https://github.com/matrix-org/matrix-react-sdk/pull/6517)
|
||||||
|
* Improve the new download button's discoverability and interactions.
|
||||||
|
[\#6510](https://github.com/matrix-org/matrix-react-sdk/pull/6510)
|
||||||
|
* Fix voice recording UI looking broken while microphone permissions are being requested.
|
||||||
|
[\#6479](https://github.com/matrix-org/matrix-react-sdk/pull/6479)
|
||||||
|
Fixes vector-im/element-web#18223
|
||||||
|
* Match colors of room and user avatars in DMs
|
||||||
|
[\#6393](https://github.com/matrix-org/matrix-react-sdk/pull/6393)
|
||||||
|
Fixes vector-im/element-web#2449
|
||||||
|
* Fix onPaste handler to work with copying files from Finder
|
||||||
|
[\#5389](https://github.com/matrix-org/matrix-react-sdk/pull/5389)
|
||||||
|
Fixes vector-im/element-web#15536 and vector-im/element-web#16255
|
||||||
|
* Fix infinite pagination loop when offline
|
||||||
|
[\#6478](https://github.com/matrix-org/matrix-react-sdk/pull/6478)
|
||||||
|
Fixes vector-im/element-web#18242
|
||||||
|
* Fix blurhash rounded corners missing regression
|
||||||
|
[\#6467](https://github.com/matrix-org/matrix-react-sdk/pull/6467)
|
||||||
|
Fixes vector-im/element-web#18110
|
||||||
|
* Fix position of the space hierarchy spinner
|
||||||
|
[\#6462](https://github.com/matrix-org/matrix-react-sdk/pull/6462)
|
||||||
|
Fixes vector-im/element-web#18182
|
||||||
|
* Fix display of image messages that lack thumbnails
|
||||||
|
[\#6456](https://github.com/matrix-org/matrix-react-sdk/pull/6456)
|
||||||
|
Fixes vector-im/element-web#18175
|
||||||
|
* Fix crash with large audio files.
|
||||||
|
[\#6436](https://github.com/matrix-org/matrix-react-sdk/pull/6436)
|
||||||
|
Fixes vector-im/element-web#18149
|
||||||
|
* Make diff colors in codeblocks more pleasant
|
||||||
|
[\#6355](https://github.com/matrix-org/matrix-react-sdk/pull/6355)
|
||||||
|
Fixes vector-im/element-web#17939
|
||||||
|
* Show the correct audio file duration while loading the file.
|
||||||
|
[\#6435](https://github.com/matrix-org/matrix-react-sdk/pull/6435)
|
||||||
|
Fixes vector-im/element-web#18160
|
||||||
|
* Fix various timeline settings not applying immediately.
|
||||||
|
[\#6261](https://github.com/matrix-org/matrix-react-sdk/pull/6261)
|
||||||
|
Fixes vector-im/element-web#17748
|
||||||
|
* Fix issues with room list duplication
|
||||||
|
[\#6391](https://github.com/matrix-org/matrix-react-sdk/pull/6391)
|
||||||
|
Fixes vector-im/element-web#14508
|
||||||
|
* Fix grecaptcha throwing useless error sometimes
|
||||||
|
[\#6401](https://github.com/matrix-org/matrix-react-sdk/pull/6401)
|
||||||
|
Fixes vector-im/element-web#15142
|
||||||
|
* Update Emojibase and Twemoji and switch to IamCal (Slack-style) shortcodes
|
||||||
|
[\#6347](https://github.com/matrix-org/matrix-react-sdk/pull/6347)
|
||||||
|
Fixes vector-im/element-web#13857 and vector-im/element-web#13334
|
||||||
|
* Respect compound emojis in default avatar initial generation
|
||||||
|
[\#6397](https://github.com/matrix-org/matrix-react-sdk/pull/6397)
|
||||||
|
Fixes vector-im/element-web#18040
|
||||||
|
* Fix bug where the 'other homeserver' field in the server selection dialog would become briefly focus and then unfocus when clicked.
|
||||||
|
[\#6394](https://github.com/matrix-org/matrix-react-sdk/pull/6394)
|
||||||
|
Fixes vector-im/element-web#18031
|
||||||
|
* Standardise spelling and casing of homeserver, identity server, and integration manager
|
||||||
|
[\#6365](https://github.com/matrix-org/matrix-react-sdk/pull/6365)
|
||||||
|
* Fix widgets not receiving decrypted events when they have permission.
|
||||||
|
[\#6371](https://github.com/matrix-org/matrix-react-sdk/pull/6371)
|
||||||
|
Fixes vector-im/element-web#17615
|
||||||
|
* Prevent client hangs when calculating blurhashes
|
||||||
|
[\#6366](https://github.com/matrix-org/matrix-react-sdk/pull/6366)
|
||||||
|
Fixes vector-im/element-web#17945
|
||||||
|
* Exclude state events from widgets reading room events
|
||||||
|
[\#6378](https://github.com/matrix-org/matrix-react-sdk/pull/6378)
|
||||||
|
* Cache feature_spaces\* flags to improve performance
|
||||||
|
[\#6381](https://github.com/matrix-org/matrix-react-sdk/pull/6381)
|
||||||
|
|
||||||
Changes in [3.26.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.26.0) (2021-07-19)
|
Changes in [3.26.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.26.0) (2021-07-19)
|
||||||
=====================================================================================================
|
=====================================================================================================
|
||||||
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.26.0-rc.1...v3.26.0)
|
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.26.0-rc.1...v3.26.0)
|
||||||
|
@ -34,7 +34,7 @@ All code lands on the `develop` branch - `master` is only used for stable releas
|
|||||||
**Please file PRs against `develop`!!**
|
**Please file PRs against `develop`!!**
|
||||||
|
|
||||||
Please follow the standard Matrix contributor's guide:
|
Please follow the standard Matrix contributor's guide:
|
||||||
https://github.com/matrix-org/matrix-js-sdk/blob/develop/CONTRIBUTING.rst
|
https://github.com/matrix-org/matrix-js-sdk/blob/develop/CONTRIBUTING.md
|
||||||
|
|
||||||
Please follow the Matrix JS/React code style as per:
|
Please follow the Matrix JS/React code style as per:
|
||||||
https://github.com/matrix-org/matrix-react-sdk/blob/master/code_style.md
|
https://github.com/matrix-org/matrix-react-sdk/blob/master/code_style.md
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "matrix-react-sdk",
|
"name": "matrix-react-sdk",
|
||||||
"version": "3.26.0",
|
"version": "3.27.0",
|
||||||
"description": "SDK for matrix.org using React",
|
"description": "SDK for matrix.org using React",
|
||||||
"author": "matrix.org",
|
"author": "matrix.org",
|
||||||
"repository": {
|
"repository": {
|
||||||
@ -80,7 +80,7 @@
|
|||||||
"katex": "^0.12.0",
|
"katex": "^0.12.0",
|
||||||
"linkifyjs": "^2.1.9",
|
"linkifyjs": "^2.1.9",
|
||||||
"lodash": "^4.17.20",
|
"lodash": "^4.17.20",
|
||||||
"matrix-js-sdk": "12.1.0",
|
"matrix-js-sdk": "12.2.0",
|
||||||
"matrix-widget-api": "^0.1.0-beta.15",
|
"matrix-widget-api": "^0.1.0-beta.15",
|
||||||
"minimist": "^1.2.5",
|
"minimist": "^1.2.5",
|
||||||
"opus-recorder": "^8.0.3",
|
"opus-recorder": "^8.0.3",
|
||||||
@ -153,7 +153,7 @@
|
|||||||
"enzyme": "^3.11.0",
|
"enzyme": "^3.11.0",
|
||||||
"eslint": "7.18.0",
|
"eslint": "7.18.0",
|
||||||
"eslint-config-google": "^0.14.0",
|
"eslint-config-google": "^0.14.0",
|
||||||
"eslint-plugin-matrix-org": "github:matrix-org/eslint-plugin-matrix-org#main",
|
"eslint-plugin-matrix-org": "github:matrix-org/eslint-plugin-matrix-org#2306b3d4da4eba908b256014b979f1d3d43d2945",
|
||||||
"eslint-plugin-react": "^7.22.0",
|
"eslint-plugin-react": "^7.22.0",
|
||||||
"eslint-plugin-react-hooks": "^4.2.0",
|
"eslint-plugin-react-hooks": "^4.2.0",
|
||||||
"glob": "^7.1.6",
|
"glob": "^7.1.6",
|
||||||
|
@ -104,8 +104,8 @@ a:visited {
|
|||||||
input[type=text],
|
input[type=text],
|
||||||
input[type=search],
|
input[type=search],
|
||||||
input[type=password] {
|
input[type=password] {
|
||||||
|
font-family: inherit;
|
||||||
padding: 9px;
|
padding: 9px;
|
||||||
font-family: $font-family;
|
|
||||||
font-size: $font-14px;
|
font-size: $font-14px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
@ -146,7 +146,6 @@ input[type=text], input[type=password], textarea {
|
|||||||
|
|
||||||
/* Required by Firefox */
|
/* Required by Firefox */
|
||||||
textarea {
|
textarea {
|
||||||
font-family: $font-family;
|
|
||||||
color: $primary-fg-color;
|
color: $primary-fg-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,7 +67,6 @@
|
|||||||
@import "./views/dialogs/_AddExistingToSpaceDialog.scss";
|
@import "./views/dialogs/_AddExistingToSpaceDialog.scss";
|
||||||
@import "./views/dialogs/_AddressPickerDialog.scss";
|
@import "./views/dialogs/_AddressPickerDialog.scss";
|
||||||
@import "./views/dialogs/_Analytics.scss";
|
@import "./views/dialogs/_Analytics.scss";
|
||||||
@import "./views/dialogs/_BetaFeedbackDialog.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";
|
||||||
@ -76,16 +75,21 @@
|
|||||||
@import "./views/dialogs/_CreateCommunityPrototypeDialog.scss";
|
@import "./views/dialogs/_CreateCommunityPrototypeDialog.scss";
|
||||||
@import "./views/dialogs/_CreateGroupDialog.scss";
|
@import "./views/dialogs/_CreateGroupDialog.scss";
|
||||||
@import "./views/dialogs/_CreateRoomDialog.scss";
|
@import "./views/dialogs/_CreateRoomDialog.scss";
|
||||||
|
@import "./views/dialogs/_CreateSubspaceDialog.scss";
|
||||||
@import "./views/dialogs/_DeactivateAccountDialog.scss";
|
@import "./views/dialogs/_DeactivateAccountDialog.scss";
|
||||||
@import "./views/dialogs/_DevtoolsDialog.scss";
|
@import "./views/dialogs/_DevtoolsDialog.scss";
|
||||||
@import "./views/dialogs/_EditCommunityPrototypeDialog.scss";
|
@import "./views/dialogs/_EditCommunityPrototypeDialog.scss";
|
||||||
@import "./views/dialogs/_FeedbackDialog.scss";
|
@import "./views/dialogs/_FeedbackDialog.scss";
|
||||||
@import "./views/dialogs/_ForwardDialog.scss";
|
@import "./views/dialogs/_ForwardDialog.scss";
|
||||||
|
@import "./views/dialogs/_GenericFeatureFeedbackDialog.scss";
|
||||||
@import "./views/dialogs/_GroupAddressPicker.scss";
|
@import "./views/dialogs/_GroupAddressPicker.scss";
|
||||||
@import "./views/dialogs/_HostSignupDialog.scss";
|
@import "./views/dialogs/_HostSignupDialog.scss";
|
||||||
@import "./views/dialogs/_IncomingSasDialog.scss";
|
@import "./views/dialogs/_IncomingSasDialog.scss";
|
||||||
@import "./views/dialogs/_InviteDialog.scss";
|
@import "./views/dialogs/_InviteDialog.scss";
|
||||||
|
@import "./views/dialogs/_JoinRuleDropdown.scss";
|
||||||
@import "./views/dialogs/_KeyboardShortcutsDialog.scss";
|
@import "./views/dialogs/_KeyboardShortcutsDialog.scss";
|
||||||
|
@import "./views/dialogs/_LeaveSpaceDialog.scss";
|
||||||
|
@import "./views/dialogs/_ManageRestrictedJoinRuleDialog.scss";
|
||||||
@import "./views/dialogs/_MessageEditHistoryDialog.scss";
|
@import "./views/dialogs/_MessageEditHistoryDialog.scss";
|
||||||
@import "./views/dialogs/_ModalWidgetDialog.scss";
|
@import "./views/dialogs/_ModalWidgetDialog.scss";
|
||||||
@import "./views/dialogs/_NewSessionReviewDialog.scss";
|
@import "./views/dialogs/_NewSessionReviewDialog.scss";
|
||||||
@ -268,6 +272,7 @@
|
|||||||
@import "./views/voip/_CallPreview.scss";
|
@import "./views/voip/_CallPreview.scss";
|
||||||
@import "./views/voip/_CallView.scss";
|
@import "./views/voip/_CallView.scss";
|
||||||
@import "./views/voip/_CallViewForRoom.scss";
|
@import "./views/voip/_CallViewForRoom.scss";
|
||||||
|
@import "./views/voip/_CallViewSidebar.scss";
|
||||||
@import "./views/voip/_DialPad.scss";
|
@import "./views/voip/_DialPad.scss";
|
||||||
@import "./views/voip/_DialPadContextMenu.scss";
|
@import "./views/voip/_DialPadContextMenu.scss";
|
||||||
@import "./views/voip/_DialPadModal.scss";
|
@import "./views/voip/_DialPadModal.scss";
|
||||||
|
@ -297,7 +297,7 @@ $activeBorderColor: $secondary-fg-color;
|
|||||||
.mx_SpaceButton:hover,
|
.mx_SpaceButton:hover,
|
||||||
.mx_SpaceButton:focus-within,
|
.mx_SpaceButton:focus-within,
|
||||||
.mx_SpaceButton_hasMenuOpen {
|
.mx_SpaceButton_hasMenuOpen {
|
||||||
&:not(.mx_SpaceButton_home):not(.mx_SpaceButton_invite) {
|
&:not(.mx_SpaceButton_invite) {
|
||||||
// Hide the badge container on hover because it'll be a menu button
|
// Hide the badge container on hover because it'll be a menu button
|
||||||
.mx_SpacePanel_badgeContainer {
|
.mx_SpacePanel_badgeContainer {
|
||||||
width: 0;
|
width: 0;
|
||||||
@ -368,6 +368,14 @@ $activeBorderColor: $secondary-fg-color;
|
|||||||
.mx_SpacePanel_iconExplore::before {
|
.mx_SpacePanel_iconExplore::before {
|
||||||
mask-image: url('$(res)/img/element-icons/roomlist/browse.svg');
|
mask-image: url('$(res)/img/element-icons/roomlist/browse.svg');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_SpacePanel_noIcon {
|
||||||
|
display: none;
|
||||||
|
|
||||||
|
& + .mx_IconizedContextMenu_label {
|
||||||
|
padding-left: 5px !important; // override default iconized label style to align with header
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -61,6 +61,7 @@ limitations under the License.
|
|||||||
|
|
||||||
.mx_AccessibleButton_kind_link {
|
.mx_AccessibleButton_kind_link {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
font-size: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_SearchBox {
|
.mx_SearchBox {
|
||||||
@ -190,7 +191,6 @@ limitations under the License.
|
|||||||
position: relative;
|
position: relative;
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
min-height: 56px;
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
||||||
display: grid;
|
display: grid;
|
||||||
|
@ -234,6 +234,9 @@ $SpaceRoomViewInnerWidth: 428px;
|
|||||||
}
|
}
|
||||||
|
|
||||||
.mx_SpaceRoomView_landing {
|
.mx_SpaceRoomView_landing {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
> .mx_BaseAvatar_image,
|
> .mx_BaseAvatar_image,
|
||||||
> .mx_BaseAvatar > .mx_BaseAvatar_image {
|
> .mx_BaseAvatar > .mx_BaseAvatar_image {
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
@ -332,23 +335,22 @@ $SpaceRoomViewInnerWidth: 428px;
|
|||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
> hr {
|
|
||||||
border: none;
|
|
||||||
height: 1px;
|
|
||||||
background-color: $groupFilterPanel-bg-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_SearchBox {
|
.mx_SearchBox {
|
||||||
margin: 0 0 20px;
|
margin: 0 0 20px;
|
||||||
|
flex: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_SpaceFeedbackPrompt {
|
.mx_SpaceFeedbackPrompt {
|
||||||
margin-bottom: 16px;
|
padding: 7px; // 8px - 1px border
|
||||||
|
border: 1px solid $menu-border-color;
|
||||||
|
border-radius: 8px;
|
||||||
|
width: max-content;
|
||||||
|
margin: 0 0 -40px auto; // collapse its own height to not push other components down
|
||||||
|
}
|
||||||
|
|
||||||
// hide the HR as we have our own
|
.mx_SpaceRoomDirectory_list {
|
||||||
& + hr {
|
// we don't want this container to get forced into the flexbox layout
|
||||||
display: none;
|
display: contents;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -504,66 +506,3 @@ $SpaceRoomViewInnerWidth: 428px;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_SpaceFeedbackPrompt {
|
|
||||||
margin-top: 18px;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
|
|
||||||
> hr {
|
|
||||||
border: none;
|
|
||||||
border-top: 1px solid $input-border-color;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
> div {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
font-size: $font-15px;
|
|
||||||
line-height: $font-24px;
|
|
||||||
|
|
||||||
> span {
|
|
||||||
color: $secondary-fg-color;
|
|
||||||
position: relative;
|
|
||||||
padding-left: 32px;
|
|
||||||
font-size: inherit;
|
|
||||||
line-height: inherit;
|
|
||||||
margin-right: auto;
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
top: 2px;
|
|
||||||
height: 20px;
|
|
||||||
width: 20px;
|
|
||||||
background-color: $secondary-fg-color;
|
|
||||||
mask-repeat: no-repeat;
|
|
||||||
mask-size: contain;
|
|
||||||
mask-image: url('$(res)/img/element-icons/room/room-summary.svg');
|
|
||||||
mask-position: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_AccessibleButton_kind_link {
|
|
||||||
color: $accent-color;
|
|
||||||
position: relative;
|
|
||||||
padding: 0 0 0 24px;
|
|
||||||
margin-left: 8px;
|
|
||||||
font-size: inherit;
|
|
||||||
line-height: inherit;
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
height: 16px;
|
|
||||||
width: 16px;
|
|
||||||
background-color: $accent-color;
|
|
||||||
mask-repeat: no-repeat;
|
|
||||||
mask-size: contain;
|
|
||||||
mask-image: url('$(res)/img/element-icons/chat-bubbles.svg');
|
|
||||||
mask-position: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -27,7 +27,6 @@ limitations under the License.
|
|||||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=255139
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=255139
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
line-height: 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_BaseAvatar_initial {
|
.mx_BaseAvatar_initial {
|
||||||
|
@ -99,6 +99,10 @@ limitations under the License.
|
|||||||
.mx_IconizedContextMenu_icon + .mx_IconizedContextMenu_label {
|
.mx_IconizedContextMenu_icon + .mx_IconizedContextMenu_label {
|
||||||
padding-left: 14px;
|
padding-left: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_BetaCard_betaPill {
|
||||||
|
margin-left: 16px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,12 +149,17 @@ limitations under the License.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_IconizedContextMenu_checked {
|
.mx_IconizedContextMenu_checked,
|
||||||
|
.mx_IconizedContextMenu_unchecked {
|
||||||
margin-left: 16px;
|
margin-left: 16px;
|
||||||
margin-right: -5px;
|
margin-right: -5px;
|
||||||
|
}
|
||||||
|
|
||||||
&::before {
|
.mx_IconizedContextMenu_checked::before {
|
||||||
mask-image: url('$(res)/img/element-icons/roomlist/checkmark.svg');
|
mask-image: url('$(res)/img/element-icons/roomlist/checkmark.svg');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_IconizedContextMenu_unchecked::before {
|
||||||
|
content: unset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,64 +50,11 @@ limitations under the License.
|
|||||||
line-height: $font-15px;
|
line-height: $font-15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_AddExistingToSpace_entry {
|
.mx_AccessibleButton_kind_link {
|
||||||
display: flex;
|
font-size: $font-12px;
|
||||||
margin-top: 12px;
|
line-height: $font-15px;
|
||||||
|
margin-top: 8px;
|
||||||
// we can't target .mx_BaseAvatar here as it'll break the decorated avatar styling
|
padding: 0;
|
||||||
.mx_DecoratedRoomAvatar {
|
|
||||||
margin-right: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_AddExistingToSpace_entry_name {
|
|
||||||
font-size: $font-15px;
|
|
||||||
line-height: 30px;
|
|
||||||
flex-grow: 1;
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
margin-right: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_Checkbox {
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_AddExistingToSpace_section_spaces {
|
|
||||||
.mx_BaseAvatar {
|
|
||||||
margin-right: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_BaseAvatar_image {
|
|
||||||
border-radius: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_AddExistingToSpace_section_experimental {
|
|
||||||
position: relative;
|
|
||||||
border-radius: 8px;
|
|
||||||
margin: 12px 0;
|
|
||||||
padding: 8px 8px 8px 42px;
|
|
||||||
background-color: $header-panel-bg-color;
|
|
||||||
|
|
||||||
font-size: $font-12px;
|
|
||||||
line-height: $font-15px;
|
|
||||||
color: $secondary-fg-color;
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
left: 10px;
|
|
||||||
top: calc(50% - 8px); // vertical centering
|
|
||||||
height: 16px;
|
|
||||||
width: 16px;
|
|
||||||
background-color: $secondary-fg-color;
|
|
||||||
mask-repeat: no-repeat;
|
|
||||||
mask-size: contain;
|
|
||||||
mask-image: url('$(res)/img/element-icons/room/room-summary.svg');
|
|
||||||
mask-position: center;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,77 +152,106 @@ limitations under the License.
|
|||||||
min-height: 0;
|
min-height: 0;
|
||||||
height: 80vh;
|
height: 80vh;
|
||||||
|
|
||||||
.mx_Dialog_title {
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
.mx_BaseAvatar_image {
|
|
||||||
border-radius: 8px;
|
|
||||||
margin: 0;
|
|
||||||
vertical-align: unset;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_BaseAvatar {
|
|
||||||
display: inline-flex;
|
|
||||||
margin: auto 16px auto 5px;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
> div {
|
|
||||||
> h1 {
|
|
||||||
font-weight: $font-semi-bold;
|
|
||||||
font-size: $font-18px;
|
|
||||||
line-height: $font-22px;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_AddExistingToSpaceDialog_onlySpace {
|
|
||||||
color: $secondary-fg-color;
|
|
||||||
font-size: $font-15px;
|
|
||||||
line-height: $font-24px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_Dropdown_input {
|
|
||||||
border: none;
|
|
||||||
|
|
||||||
> .mx_Dropdown_option {
|
|
||||||
padding-left: 0;
|
|
||||||
flex: unset;
|
|
||||||
height: unset;
|
|
||||||
color: $secondary-fg-color;
|
|
||||||
font-size: $font-15px;
|
|
||||||
line-height: $font-24px;
|
|
||||||
|
|
||||||
.mx_BaseAvatar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_Dropdown_menu {
|
|
||||||
.mx_AddExistingToSpaceDialog_dropdownOptionActive {
|
|
||||||
color: $accent-color;
|
|
||||||
padding-right: 32px;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
content: '';
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
top: 8px;
|
|
||||||
right: 0;
|
|
||||||
position: absolute;
|
|
||||||
mask-position: center;
|
|
||||||
mask-size: contain;
|
|
||||||
mask-repeat: no-repeat;
|
|
||||||
background-color: $accent-color;
|
|
||||||
mask-image: url('$(res)/img/element-icons/roomlist/checkmark.svg');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_AddExistingToSpace {
|
.mx_AddExistingToSpace {
|
||||||
display: contents;
|
display: contents;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_SubspaceSelector {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.mx_BaseAvatar_image {
|
||||||
|
border-radius: 8px;
|
||||||
|
margin: 0;
|
||||||
|
vertical-align: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_BaseAvatar {
|
||||||
|
display: inline-flex;
|
||||||
|
margin: auto 16px auto 5px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
> div {
|
||||||
|
> h1 {
|
||||||
|
font-weight: $font-semi-bold;
|
||||||
|
font-size: $font-18px;
|
||||||
|
line-height: $font-22px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_Dropdown_input {
|
||||||
|
border: none;
|
||||||
|
|
||||||
|
> .mx_Dropdown_option {
|
||||||
|
padding-left: 0;
|
||||||
|
flex: unset;
|
||||||
|
height: unset;
|
||||||
|
color: $secondary-fg-color;
|
||||||
|
font-size: $font-15px;
|
||||||
|
line-height: $font-24px;
|
||||||
|
|
||||||
|
.mx_BaseAvatar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_Dropdown_menu {
|
||||||
|
.mx_SubspaceSelector_dropdownOptionActive {
|
||||||
|
color: $accent-color;
|
||||||
|
padding-right: 32px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
top: 8px;
|
||||||
|
right: 0;
|
||||||
|
position: absolute;
|
||||||
|
mask-position: center;
|
||||||
|
mask-size: contain;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
background-color: $accent-color;
|
||||||
|
mask-image: url('$(res)/img/element-icons/roomlist/checkmark.svg');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_SubspaceSelector_onlySpace {
|
||||||
|
color: $secondary-fg-color;
|
||||||
|
font-size: $font-15px;
|
||||||
|
line-height: $font-24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_AddExistingToSpace_entry {
|
||||||
|
display: flex;
|
||||||
|
margin-top: 12px;
|
||||||
|
|
||||||
|
.mx_DecoratedRoomAvatar, // we can't target .mx_BaseAvatar here as it'll break the decorated avatar styling
|
||||||
|
.mx_BaseAvatar.mx_RoomAvatar_isSpaceRoom {
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
img.mx_RoomAvatar_isSpaceRoom,
|
||||||
|
.mx_RoomAvatar_isSpaceRoom img {
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_AddExistingToSpace_entry_name {
|
||||||
|
font-size: $font-15px;
|
||||||
|
line-height: 30px;
|
||||||
|
flex-grow: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_Checkbox {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -29,7 +29,6 @@ limitations under the License.
|
|||||||
.mx_AddressPickerDialog_input:focus {
|
.mx_AddressPickerDialog_input:focus {
|
||||||
height: 26px;
|
height: 26px;
|
||||||
font-size: $font-14px;
|
font-size: $font-14px;
|
||||||
font-family: $font-family;
|
|
||||||
padding-left: 12px;
|
padding-left: 12px;
|
||||||
padding-right: 12px;
|
padding-right: 12px;
|
||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
|
@ -34,7 +34,6 @@ limitations under the License.
|
|||||||
}
|
}
|
||||||
|
|
||||||
.mx_ConfirmUserActionDialog_reasonField {
|
.mx_ConfirmUserActionDialog_reasonField {
|
||||||
font-family: $font-family;
|
|
||||||
font-size: $font-14px;
|
font-size: $font-14px;
|
||||||
color: $primary-fg-color;
|
color: $primary-fg-color;
|
||||||
background-color: $primary-bg-color;
|
background-color: $primary-bg-color;
|
||||||
|
@ -65,7 +65,7 @@ limitations under the License.
|
|||||||
.mx_CreateRoomDialog_aliasContainer {
|
.mx_CreateRoomDialog_aliasContainer {
|
||||||
display: flex;
|
display: flex;
|
||||||
// put margin on container so it can collapse with siblings
|
// put margin on container so it can collapse with siblings
|
||||||
margin: 10px 0;
|
margin: 24px 0 10px;
|
||||||
|
|
||||||
.mx_RoomAliasField {
|
.mx_RoomAliasField {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@ -101,10 +101,6 @@ limitations under the License.
|
|||||||
margin-left: 30px;
|
margin-left: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_CreateRoomDialog_topic {
|
|
||||||
margin-bottom: 36px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_Dialog_content > .mx_SettingsFlag {
|
.mx_Dialog_content > .mx_SettingsFlag {
|
||||||
margin-top: 24px;
|
margin-top: 24px;
|
||||||
}
|
}
|
||||||
@ -114,4 +110,3 @@ limitations under the License.
|
|||||||
font-size: $font-12px;
|
font-size: $font-12px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
81
res/css/views/dialogs/_CreateSubspaceDialog.scss
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.mx_CreateSubspaceDialog_wrapper {
|
||||||
|
.mx_Dialog {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_CreateSubspaceDialog {
|
||||||
|
width: 480px;
|
||||||
|
color: $primary-fg-color;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
min-height: 0;
|
||||||
|
|
||||||
|
.mx_CreateSubspaceDialog_content {
|
||||||
|
flex-grow: 1;
|
||||||
|
|
||||||
|
.mx_CreateSubspaceDialog_betaNotice {
|
||||||
|
padding: 12px 16px;
|
||||||
|
border-radius: 8px;
|
||||||
|
background-color: $header-panel-bg-color;
|
||||||
|
|
||||||
|
.mx_BetaCard_betaPill {
|
||||||
|
margin-right: 8px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_JoinRuleDropdown + p {
|
||||||
|
color: $muted-fg-color;
|
||||||
|
font-size: $font-12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_CreateSubspaceDialog_footer {
|
||||||
|
display: flex;
|
||||||
|
margin-top: 20px;
|
||||||
|
|
||||||
|
.mx_CreateSubspaceDialog_footer_prompt {
|
||||||
|
flex-grow: 1;
|
||||||
|
font-size: $font-12px;
|
||||||
|
line-height: $font-15px;
|
||||||
|
color: $secondary-fg-color;
|
||||||
|
|
||||||
|
> * {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_AccessibleButton {
|
||||||
|
display: inline-block;
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_AccessibleButton_kind_primary {
|
||||||
|
margin-left: 16px;
|
||||||
|
padding: 8px 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_AccessibleButton_kind_link {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -55,22 +55,6 @@ limitations under the License.
|
|||||||
padding-right: 24px;
|
padding-right: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_DevTools_inputCell {
|
|
||||||
display: table-cell;
|
|
||||||
width: 240px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_DevTools_inputCell input {
|
|
||||||
display: inline-block;
|
|
||||||
border: 0;
|
|
||||||
border-bottom: 1px solid $input-underline-color;
|
|
||||||
padding: 0;
|
|
||||||
width: 240px;
|
|
||||||
color: $input-fg-color;
|
|
||||||
font-family: $font-family;
|
|
||||||
font-size: $font-16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_DevTools_textarea {
|
.mx_DevTools_textarea {
|
||||||
font-size: $font-12px;
|
font-size: $font-12px;
|
||||||
max-width: 684px;
|
max-width: 684px;
|
||||||
@ -139,7 +123,6 @@ limitations under the License.
|
|||||||
+ .mx_DevTools_tgl-btn {
|
+ .mx_DevTools_tgl-btn {
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
transition: all .2s ease;
|
transition: all .2s ease;
|
||||||
font-family: sans-serif;
|
|
||||||
perspective: 100px;
|
perspective: 100px;
|
||||||
&::after,
|
&::after,
|
||||||
&::before {
|
&::before {
|
||||||
|
@ -14,8 +14,8 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.mx_BetaFeedbackDialog {
|
.mx_GenericFeatureFeedbackDialog {
|
||||||
.mx_BetaFeedbackDialog_subheading {
|
.mx_GenericFeatureFeedbackDialog_subheading {
|
||||||
color: $primary-fg-color;
|
color: $primary-fg-color;
|
||||||
font-size: $font-14px;
|
font-size: $font-14px;
|
||||||
line-height: $font-20px;
|
line-height: $font-20px;
|
67
res/css/views/dialogs/_JoinRuleDropdown.scss
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.mx_JoinRuleDropdown {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-weight: normal;
|
||||||
|
font-family: $font-family;
|
||||||
|
font-size: $font-14px;
|
||||||
|
color: $primary-fg-color;
|
||||||
|
|
||||||
|
.mx_Dropdown_input {
|
||||||
|
border: 1px solid $input-border-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_Dropdown_option {
|
||||||
|
font-size: $font-14px;
|
||||||
|
line-height: $font-32px;
|
||||||
|
height: 32px;
|
||||||
|
min-height: 32px;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
padding-left: 30px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
height: 16px;
|
||||||
|
width: 16px;
|
||||||
|
left: 6px;
|
||||||
|
top: 8px;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
mask-position: center;
|
||||||
|
background-color: $secondary-fg-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_JoinRuleDropdown_invite::before {
|
||||||
|
mask-image: url('$(res)/img/element-icons/lock.svg');
|
||||||
|
mask-size: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_JoinRuleDropdown_public::before {
|
||||||
|
mask-image: url('$(res)/img/globe.svg');
|
||||||
|
mask-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_JoinRuleDropdown_restricted::before {
|
||||||
|
mask-image: url('$(res)/img/element-icons/community-members.svg');
|
||||||
|
mask-size: contain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
96
res/css/views/dialogs/_LeaveSpaceDialog.scss
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.mx_LeaveSpaceDialog_wrapper {
|
||||||
|
.mx_Dialog {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 24px 32px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_LeaveSpaceDialog {
|
||||||
|
width: 440px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
max-height: 520px;
|
||||||
|
|
||||||
|
.mx_Dialog_content {
|
||||||
|
flex-grow: 1;
|
||||||
|
margin: 0;
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
.mx_RadioButton + .mx_RadioButton {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_SearchBox {
|
||||||
|
// To match the space around the title
|
||||||
|
margin: 0 0 15px 0;
|
||||||
|
flex-grow: 0;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_LeaveSpaceDialog_noResults {
|
||||||
|
display: block;
|
||||||
|
margin-top: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_LeaveSpaceDialog_section {
|
||||||
|
margin: 16px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_LeaveSpaceDialog_section_warning {
|
||||||
|
position: relative;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin: 12px 0 0;
|
||||||
|
padding: 12px 8px 12px 42px;
|
||||||
|
background-color: $header-panel-bg-color;
|
||||||
|
|
||||||
|
font-size: $font-12px;
|
||||||
|
line-height: $font-15px;
|
||||||
|
color: $secondary-fg-color;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: 10px;
|
||||||
|
top: calc(50% - 8px); // vertical centering
|
||||||
|
height: 16px;
|
||||||
|
width: 16px;
|
||||||
|
background-color: $secondary-fg-color;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
mask-size: contain;
|
||||||
|
mask-image: url('$(res)/img/element-icons/room/room-summary.svg');
|
||||||
|
mask-position: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> p {
|
||||||
|
color: $primary-fg-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_Dialog_buttons {
|
||||||
|
margin-top: 20px;
|
||||||
|
|
||||||
|
.mx_Dialog_primary {
|
||||||
|
background-color: $notice-primary-color !important; // override default colour
|
||||||
|
border-color: $notice-primary-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
150
res/css/views/dialogs/_ManageRestrictedJoinRuleDialog.scss
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.mx_ManageRestrictedJoinRuleDialog_wrapper {
|
||||||
|
.mx_Dialog {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_ManageRestrictedJoinRuleDialog {
|
||||||
|
width: 480px;
|
||||||
|
color: $primary-fg-color;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
min-height: 0;
|
||||||
|
height: 60vh;
|
||||||
|
|
||||||
|
.mx_SearchBox {
|
||||||
|
// To match the space around the title
|
||||||
|
margin: 0 0 15px 0;
|
||||||
|
flex-grow: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_ManageRestrictedJoinRuleDialog_content {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_ManageRestrictedJoinRuleDialog_noResults {
|
||||||
|
display: block;
|
||||||
|
margin-top: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_ManageRestrictedJoinRuleDialog_section {
|
||||||
|
&:not(:first-child) {
|
||||||
|
margin-top: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
> h3 {
|
||||||
|
margin: 0;
|
||||||
|
color: $secondary-fg-color;
|
||||||
|
font-size: $font-12px;
|
||||||
|
font-weight: $font-semi-bold;
|
||||||
|
line-height: $font-15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_ManageRestrictedJoinRuleDialog_entry {
|
||||||
|
display: flex;
|
||||||
|
margin-top: 12px;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
img.mx_RoomAvatar_isSpaceRoom,
|
||||||
|
.mx_RoomAvatar_isSpaceRoom img {
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_ManageRestrictedJoinRuleDialog_entry_name {
|
||||||
|
margin: 0 8px;
|
||||||
|
font-size: $font-15px;
|
||||||
|
line-height: 30px;
|
||||||
|
flex-grow: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_ManageRestrictedJoinRuleDialog_entry_description {
|
||||||
|
margin-top: 8px;
|
||||||
|
font-size: $font-12px;
|
||||||
|
line-height: $font-15px;
|
||||||
|
color: $tertiary-fg-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_Checkbox {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_ManageRestrictedJoinRuleDialog_section_spaces {
|
||||||
|
.mx_BaseAvatar {
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_BaseAvatar_image {
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_ManageRestrictedJoinRuleDialog_section_info {
|
||||||
|
position: relative;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin: 12px 0;
|
||||||
|
padding: 8px 8px 8px 42px;
|
||||||
|
background-color: $header-panel-bg-color;
|
||||||
|
|
||||||
|
font-size: $font-12px;
|
||||||
|
line-height: $font-15px;
|
||||||
|
color: $secondary-fg-color;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: 10px;
|
||||||
|
top: calc(50% - 8px); // vertical centering
|
||||||
|
height: 16px;
|
||||||
|
width: 16px;
|
||||||
|
background-color: $secondary-fg-color;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
mask-size: contain;
|
||||||
|
mask-image: url('$(res)/img/element-icons/room/room-summary.svg');
|
||||||
|
mask-position: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_ManageRestrictedJoinRuleDialog_footer {
|
||||||
|
margin-top: 20px;
|
||||||
|
|
||||||
|
.mx_ManageRestrictedJoinRuleDialog_footer_buttons {
|
||||||
|
display: flex;
|
||||||
|
width: max-content;
|
||||||
|
margin-left: auto;
|
||||||
|
|
||||||
|
.mx_AccessibleButton {
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
& + .mx_AccessibleButton {
|
||||||
|
margin-left: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -16,57 +16,43 @@ limitations under the License.
|
|||||||
|
|
||||||
.mx_desktopCapturerSourcePicker {
|
.mx_desktopCapturerSourcePicker {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
|
||||||
|
|
||||||
.mx_desktopCapturerSourcePicker_tabLabels {
|
.mx_desktopCapturerSourcePicker_tab {
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 0 0 8px 0;
|
flex-wrap: wrap;
|
||||||
}
|
justify-content: center;
|
||||||
|
align-items: flex-start;
|
||||||
|
height: 500px;
|
||||||
|
overflow: overlay;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_desktopCapturerSourcePicker_tabLabel,
|
.mx_desktopCapturerSourcePicker_source {
|
||||||
.mx_desktopCapturerSourcePicker_tabLabel_selected {
|
display: flex;
|
||||||
width: 100%;
|
flex-direction: column;
|
||||||
text-align: center;
|
margin: 8px;
|
||||||
border-radius: 8px;
|
}
|
||||||
padding: 8px 0;
|
|
||||||
font-size: $font-13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_desktopCapturerSourcePicker_tabLabel_selected {
|
.mx_desktopCapturerSourcePicker_source_thumbnail {
|
||||||
background-color: $tab-label-active-bg-color;
|
margin: 4px;
|
||||||
color: $tab-label-active-fg-color;
|
padding: 4px;
|
||||||
}
|
width: 312px;
|
||||||
|
border-width: 2px;
|
||||||
|
border-radius: 8px;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: transparent;
|
||||||
|
|
||||||
.mx_desktopCapturerSourcePicker_panel {
|
&.mx_desktopCapturerSourcePicker_source_thumbnail_selected,
|
||||||
display: flex;
|
&:hover,
|
||||||
flex-wrap: wrap;
|
&:focus {
|
||||||
justify-content: center;
|
border-color: $accent-color;
|
||||||
align-items: flex-start;
|
}
|
||||||
height: 500px;
|
}
|
||||||
overflow: overlay;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_desktopCapturerSourcePicker_stream_button {
|
.mx_desktopCapturerSourcePicker_source_name {
|
||||||
display: flex;
|
margin: 0 4px;
|
||||||
flex-direction: column;
|
white-space: nowrap;
|
||||||
margin: 8px;
|
text-overflow: ellipsis;
|
||||||
border-radius: 4px;
|
overflow: hidden;
|
||||||
}
|
width: 312px;
|
||||||
|
}
|
||||||
.mx_desktopCapturerSourcePicker_stream_button:hover,
|
|
||||||
.mx_desktopCapturerSourcePicker_stream_button:focus {
|
|
||||||
background: $roomtile-selected-bg-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_desktopCapturerSourcePicker_stream_thumbnail {
|
|
||||||
margin: 4px;
|
|
||||||
width: 312px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_desktopCapturerSourcePicker_stream_name {
|
|
||||||
margin: 0 4px;
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
overflow: hidden;
|
|
||||||
width: 312px;
|
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ limitations under the License.
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
position: relative;
|
position: relative;
|
||||||
border-radius: 3px;
|
border-radius: 4px;
|
||||||
border: 1px solid $strong-input-border-color;
|
border: 1px solid $strong-input-border-color;
|
||||||
font-size: $font-12px;
|
font-size: $font-12px;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
@ -109,7 +109,7 @@ input.mx_Dropdown_option:focus {
|
|||||||
z-index: 2;
|
z-index: 2;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
border-radius: 3px;
|
border-radius: 4px;
|
||||||
border: 1px solid $input-focused-border-color;
|
border: 1px solid $input-focused-border-color;
|
||||||
background-color: $primary-bg-color;
|
background-color: $primary-bg-color;
|
||||||
max-height: 200px;
|
max-height: 200px;
|
||||||
|
@ -39,7 +39,6 @@ limitations under the License.
|
|||||||
.mx_Field select,
|
.mx_Field select,
|
||||||
.mx_Field textarea {
|
.mx_Field textarea {
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-family: $font-family;
|
|
||||||
font-size: $font-14px;
|
font-size: $font-14px;
|
||||||
border: none;
|
border: none;
|
||||||
// Even without a border here, we still need this avoid overlapping the rounded
|
// Even without a border here, we still need this avoid overlapping the rounded
|
||||||
|
@ -23,7 +23,7 @@ limitations under the License.
|
|||||||
background-color: $dark-panel-bg-color;
|
background-color: $dark-panel-bg-color;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
margin: 10px auto;
|
margin: 10px auto;
|
||||||
max-width: 75%;
|
width: 75%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
height: 60px;
|
height: 60px;
|
||||||
|
|
||||||
@ -43,6 +43,14 @@ limitations under the License.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.mx_CallEvent_voice.mx_CallEvent_missed .mx_CallEvent_type_icon::before {
|
||||||
|
mask-image: url('$(res)/img/voip/missed-voice.svg');
|
||||||
|
}
|
||||||
|
|
||||||
|
&.mx_CallEvent_video.mx_CallEvent_missed .mx_CallEvent_type_icon::before {
|
||||||
|
mask-image: url('$(res)/img/voip/missed-video.svg');
|
||||||
|
}
|
||||||
|
|
||||||
.mx_CallEvent_info {
|
.mx_CallEvent_info {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2015, 2016, 2021 The Matrix.org Foundation C.I.C.
|
Copyright 2015 - 2021 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -60,6 +60,8 @@ limitations under the License.
|
|||||||
}
|
}
|
||||||
|
|
||||||
.mx_MFileBody_info {
|
.mx_MFileBody_info {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
.mx_MFileBody_info_icon {
|
.mx_MFileBody_info_icon {
|
||||||
background-color: $message-body-panel-icon-bg-color;
|
background-color: $message-body-panel-icon-bg-color;
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
|
@ -16,10 +16,6 @@ limitations under the License.
|
|||||||
|
|
||||||
$timelineImageBorderRadius: 4px;
|
$timelineImageBorderRadius: 4px;
|
||||||
|
|
||||||
.mx_MImageBody {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_MImageBody_thumbnail {
|
.mx_MImageBody_thumbnail {
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
border-radius: $timelineImageBorderRadius;
|
border-radius: $timelineImageBorderRadius;
|
||||||
@ -28,7 +24,7 @@ $timelineImageBorderRadius: 4px;
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
> canvas {
|
> div > canvas {
|
||||||
border-radius: $timelineImageBorderRadius;
|
border-radius: $timelineImageBorderRadius;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,8 +43,10 @@ limitations under the License.
|
|||||||
margin-bottom: 7px;
|
margin-bottom: 7px;
|
||||||
mask-image: url('$(res)/img/feather-customised/minimise.svg');
|
mask-image: url('$(res)/img/feather-customised/minimise.svg');
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&:hover .mx_ViewSourceEvent_toggle {
|
.mx_EventTile:hover {
|
||||||
|
.mx_ViewSourceEvent_toggle {
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ limitations under the License.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
.mx_EventTile[data-layout=bubble],
|
.mx_EventTile[data-layout=bubble],
|
||||||
.mx_EventTile[data-layout=bubble] ~ .mx_EventListSummary {
|
.mx_EventListSummary[data-layout=bubble] {
|
||||||
--avatarSize: 32px;
|
--avatarSize: 32px;
|
||||||
--gutterSize: 11px;
|
--gutterSize: 11px;
|
||||||
--cornerRadius: 12px;
|
--cornerRadius: 12px;
|
||||||
@ -38,17 +38,22 @@ limitations under the License.
|
|||||||
padding-top: 0;
|
padding-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: -1px;
|
||||||
|
bottom: -1px;
|
||||||
|
left: -60px;
|
||||||
|
right: -60px;
|
||||||
|
z-index: -1;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&.mx_EventTile_selected {
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
top: -1px;
|
|
||||||
bottom: -1px;
|
|
||||||
left: -60px;
|
|
||||||
right: -60px;
|
|
||||||
z-index: -1;
|
|
||||||
background: $eventbubble-bg-hover;
|
background: $eventbubble-bg-hover;
|
||||||
border-radius: 4px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_EventTile_avatar {
|
.mx_EventTile_avatar {
|
||||||
@ -155,12 +160,24 @@ limitations under the License.
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
|
z-index: 9;
|
||||||
img {
|
img {
|
||||||
box-shadow: 0 0 0 3px $eventbubble-avatar-outline;
|
box-shadow: 0 0 0 3px $eventbubble-avatar-outline;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.mx_EventTile_noSender {
|
||||||
|
.mx_EventTile_avatar {
|
||||||
|
top: -19px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_BaseAvatar,
|
||||||
|
.mx_EventTile_avatar {
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
&[data-has-reply=true] {
|
&[data-has-reply=true] {
|
||||||
> .mx_EventTile_line {
|
> .mx_EventTile_line {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@ -211,89 +228,6 @@ limitations under the License.
|
|||||||
border-left-color: $eventbubble-reply-color;
|
border-left-color: $eventbubble-reply-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.mx_EventTile_bubbleContainer,
|
|
||||||
&.mx_EventTile_info,
|
|
||||||
& ~ .mx_EventListSummary[data-expanded=false] {
|
|
||||||
--backgroundColor: transparent;
|
|
||||||
--gutterSize: 0;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
.mx_EventTile_avatar {
|
|
||||||
position: static;
|
|
||||||
order: -1;
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
& ~ .mx_EventListSummary {
|
|
||||||
--maxWidth: 80%;
|
|
||||||
margin-left: calc(var(--avatarSize) + var(--gutterSize));
|
|
||||||
margin-right: calc(var(--gutterSize) + var(--avatarSize));
|
|
||||||
.mx_EventListSummary_toggle {
|
|
||||||
float: none;
|
|
||||||
margin: 0;
|
|
||||||
order: 9;
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
.mx_EventListSummary_avatars {
|
|
||||||
padding-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
content: "";
|
|
||||||
clear: both;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_EventTile {
|
|
||||||
margin: 0 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_EventTile_line {
|
|
||||||
margin: 0 5px;
|
|
||||||
> a {
|
|
||||||
left: auto;
|
|
||||||
right: 0;
|
|
||||||
transform: translateX(calc(100% + 5px));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_MessageActionBar {
|
|
||||||
transform: translate3d(90%, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
& ~ .mx_EventListSummary[data-expanded=false] {
|
|
||||||
padding: 0 34px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* events that do not require bubble layout */
|
|
||||||
& ~ .mx_EventListSummary,
|
|
||||||
&.mx_EventTile_bad {
|
|
||||||
.mx_EventTile_line {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
&::before {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
& + .mx_EventListSummary {
|
|
||||||
.mx_EventTile {
|
|
||||||
margin-top: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_EventListSummary_toggle {
|
|
||||||
margin-right: 55px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Special layout scenario for "Unable To Decrypt (UTD)" events */
|
/* Special layout scenario for "Unable To Decrypt (UTD)" events */
|
||||||
&.mx_EventTile_bad > .mx_EventTile_line {
|
&.mx_EventTile_bad > .mx_EventTile_line {
|
||||||
display: grid;
|
display: grid;
|
||||||
@ -328,3 +262,93 @@ limitations under the License.
|
|||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_EventTile.mx_EventTile_bubbleContainer[data-layout=bubble],
|
||||||
|
.mx_EventTile.mx_EventTile_info[data-layout=bubble],
|
||||||
|
.mx_EventListSummary[data-layout=bubble][data-expanded=false] {
|
||||||
|
--backgroundColor: transparent;
|
||||||
|
--gutterSize: 0;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: start;
|
||||||
|
padding: 5px 0;
|
||||||
|
|
||||||
|
.mx_EventTile_avatar {
|
||||||
|
position: static;
|
||||||
|
order: -1;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_EventTile_line,
|
||||||
|
.mx_EventTile_info {
|
||||||
|
min-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_EventTile_e2eIcon {
|
||||||
|
margin-left: 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_EventTile_line > a {
|
||||||
|
right: auto;
|
||||||
|
top: -15px;
|
||||||
|
left: -68px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_EventListSummary[data-layout=bubble] {
|
||||||
|
--maxWidth: 70%;
|
||||||
|
margin-left: calc(var(--avatarSize) + var(--gutterSize));
|
||||||
|
margin-right: 94px;
|
||||||
|
.mx_EventListSummary_toggle {
|
||||||
|
float: none;
|
||||||
|
margin: 0;
|
||||||
|
order: 9;
|
||||||
|
margin-left: 5px;
|
||||||
|
margin-right: 55px;
|
||||||
|
}
|
||||||
|
.mx_EventListSummary_avatars {
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: "";
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_EventTile {
|
||||||
|
margin: 0 6px;
|
||||||
|
padding: 2px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_EventTile_line {
|
||||||
|
margin: 0 5px;
|
||||||
|
> a {
|
||||||
|
left: auto;
|
||||||
|
right: 0;
|
||||||
|
transform: translateX(calc(100% + 5px));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_MessageActionBar {
|
||||||
|
transform: translate3d(90%, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_EventListSummary[data-expanded=false][data-layout=bubble] {
|
||||||
|
padding: 0 34px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* events that do not require bubble layout */
|
||||||
|
.mx_EventListSummary[data-layout=bubble],
|
||||||
|
.mx_EventTile.mx_EventTile_bad[data-layout=bubble] {
|
||||||
|
.mx_EventTile_line {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
&::before {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -59,7 +59,6 @@ $hover-select-border: 4px;
|
|||||||
font-size: $font-14px;
|
font-size: $font-14px;
|
||||||
display: inline-block; /* anti-zalgo, with overflow hidden */
|
display: inline-block; /* anti-zalgo, with overflow hidden */
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
cursor: pointer;
|
|
||||||
padding-bottom: 0px;
|
padding-bottom: 0px;
|
||||||
padding-top: 0px;
|
padding-top: 0px;
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
@ -132,15 +131,6 @@ $hover-select-border: 4px;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.mx_EventTile_info .mx_EventTile_line,
|
|
||||||
& ~ .mx_EventListSummary .mx_EventTile_avatar ~ .mx_EventTile_line {
|
|
||||||
padding-left: calc($left-gutter + 18px);
|
|
||||||
}
|
|
||||||
|
|
||||||
& ~ .mx_EventListSummary .mx_EventTile_line {
|
|
||||||
padding-left: calc($left-gutter);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.mx_EventTile_selected.mx_EventTile_info .mx_EventTile_line {
|
&.mx_EventTile_selected.mx_EventTile_info .mx_EventTile_line {
|
||||||
padding-left: calc($left-gutter + 18px - $hover-select-border);
|
padding-left: calc($left-gutter + 18px - $hover-select-border);
|
||||||
}
|
}
|
||||||
@ -276,10 +266,19 @@ $hover-select-border: 4px;
|
|||||||
|
|
||||||
.mx_ReactionsRow {
|
.mx_ReactionsRow {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 6px 60px;
|
padding: 4px 64px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_EventTile:not([data-layout=bubble]).mx_EventTile_info .mx_EventTile_line,
|
||||||
|
.mx_EventListSummary:not([data-layout=bubble]) > :not(.mx_EventTile) .mx_EventTile_avatar ~ .mx_EventTile_line {
|
||||||
|
padding-left: calc($left-gutter + 18px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_EventListSummary:not([data-layout=bubble]) .mx_EventTile_line {
|
||||||
|
padding-left: calc($left-gutter);
|
||||||
|
}
|
||||||
|
|
||||||
/* all the overflow-y: hidden; are to trap Zalgos -
|
/* all the overflow-y: hidden; are to trap Zalgos -
|
||||||
but they introduce an implicit overflow-x: auto.
|
but they introduce an implicit overflow-x: auto.
|
||||||
so make that explicitly hidden too to avoid random
|
so make that explicitly hidden too to avoid random
|
||||||
@ -322,6 +321,10 @@ $hover-select-border: 4px;
|
|||||||
// on ELS we need the margin to allow interaction with the expand/collapse button which is normally in the RR gutter
|
// on ELS we need the margin to allow interaction with the expand/collapse button which is normally in the RR gutter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_SenderProfile {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_EventTile_bubbleContainer {
|
.mx_EventTile_bubbleContainer {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 100px;
|
grid-template-columns: 1fr 100px;
|
||||||
@ -456,8 +459,14 @@ $hover-select-border: 4px;
|
|||||||
|
|
||||||
/* Various markdown overrides */
|
/* Various markdown overrides */
|
||||||
|
|
||||||
.mx_EventTile_body pre {
|
.mx_EventTile_body {
|
||||||
border: 1px solid transparent;
|
a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
border: 1px solid transparent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_EventTile_content .markdown-body {
|
.mx_EventTile_content .markdown-body {
|
||||||
@ -573,6 +582,12 @@ $hover-select-border: 4px;
|
|||||||
color: $accent-color-alt;
|
color: $accent-color-alt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_EventTile_content .markdown-body blockquote {
|
||||||
|
border-left: 2px solid $blockquote-bar-color;
|
||||||
|
border-radius: 2px;
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_EventTile_content .markdown-body .hljs {
|
.mx_EventTile_content .markdown-body .hljs {
|
||||||
display: inline !important;
|
display: inline !important;
|
||||||
}
|
}
|
||||||
|
@ -116,6 +116,11 @@ $irc-line-height: $font-18px;
|
|||||||
.mx_EditMessageComposer_buttons {
|
.mx_EditMessageComposer_buttons {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_ReactionsRow {
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_EventTile_emote {
|
.mx_EventTile_emote {
|
||||||
|
@ -34,7 +34,7 @@ limitations under the License.
|
|||||||
.mx_LinkPreviewWidget_caption {
|
.mx_LinkPreviewWidget_caption {
|
||||||
margin-left: 15px;
|
margin-left: 15px;
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
overflow-x: hidden; // cause it to wrap rather than clip
|
overflow: hidden; // cause it to wrap rather than clip
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_LinkPreviewWidget_title {
|
.mx_LinkPreviewWidget_title {
|
||||||
|
@ -165,8 +165,6 @@ limitations under the License.
|
|||||||
font-size: $font-14px;
|
font-size: $font-14px;
|
||||||
max-height: 120px;
|
max-height: 120px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
/* needed for FF */
|
|
||||||
font-family: $font-family;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* hack for FF as vertical alignment of custom placeholder text is broken */
|
/* hack for FF as vertical alignment of custom placeholder text is broken */
|
||||||
|
@ -60,8 +60,6 @@ limitations under the License.
|
|||||||
$reply-lines: 2;
|
$reply-lines: 2;
|
||||||
$line-height: $font-22px;
|
$line-height: $font-22px;
|
||||||
|
|
||||||
pointer-events: none;
|
|
||||||
|
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
display: -webkit-box;
|
display: -webkit-box;
|
||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
|
@ -16,6 +16,7 @@ limitations under the License.
|
|||||||
|
|
||||||
.mx_ProfileSettings_controls_topic {
|
.mx_ProfileSettings_controls_topic {
|
||||||
& > textarea {
|
& > textarea {
|
||||||
|
font-family: inherit;
|
||||||
resize: vertical;
|
resize: vertical;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,6 @@ limitations under the License.
|
|||||||
.mx_SettingsTab_subheading {
|
.mx_SettingsTab_subheading {
|
||||||
font-size: $font-16px;
|
font-size: $font-16px;
|
||||||
display: block;
|
display: block;
|
||||||
font-family: $font-family;
|
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: $primary-fg-color;
|
color: $primary-fg-color;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
@ -47,14 +46,14 @@ limitations under the License.
|
|||||||
color: $settings-subsection-fg-color;
|
color: $settings-subsection-fg-color;
|
||||||
font-size: $font-14px;
|
font-size: $font-14px;
|
||||||
display: block;
|
display: block;
|
||||||
margin: 10px 100px 10px 0; // Align with the rest of the view
|
margin: 10px 80px 10px 0; // Align with the rest of the view
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_SettingsTab_section {
|
.mx_SettingsTab_section {
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
|
|
||||||
.mx_SettingsFlag {
|
.mx_SettingsFlag {
|
||||||
margin-right: 100px;
|
margin-right: 80px;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,6 +72,13 @@ limitations under the License.
|
|||||||
padding-right: 10px;
|
padding-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_SettingsTab_section .mx_SettingsFlag .mx_SettingsFlag_microcopy {
|
||||||
|
margin-top: 4px;
|
||||||
|
font-size: $font-12px;
|
||||||
|
line-height: $font-15px;
|
||||||
|
color: $secondary-fg-color;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_SettingsTab_section .mx_SettingsFlag .mx_ToggleSwitch {
|
.mx_SettingsTab_section .mx_SettingsFlag .mx_ToggleSwitch {
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,44 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
.mx_SecurityRoomSettingsTab {
|
||||||
|
.mx_SettingsTab_showAdvanced {
|
||||||
|
padding: 0;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_SecurityRoomSettingsTab_spacesWithAccess {
|
||||||
|
> h4 {
|
||||||
|
color: $secondary-fg-color;
|
||||||
|
font-weight: $font-semi-bold;
|
||||||
|
font-size: $font-12px;
|
||||||
|
line-height: $font-15px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
> span {
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: $font-14px;
|
||||||
|
line-height: 32px; // matches height of avatar for v-align
|
||||||
|
color: $secondary-fg-color;
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
img.mx_RoomAvatar_isSpaceRoom,
|
||||||
|
.mx_RoomAvatar_isSpaceRoom img {
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_BaseAvatar {
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
& + span {
|
||||||
|
margin-left: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.mx_SecurityRoomSettingsTab_warning {
|
.mx_SecurityRoomSettingsTab_warning {
|
||||||
display: block;
|
display: block;
|
||||||
|
|
||||||
@ -26,5 +64,51 @@ limitations under the License.
|
|||||||
}
|
}
|
||||||
|
|
||||||
.mx_SecurityRoomSettingsTab_encryptionSection {
|
.mx_SecurityRoomSettingsTab_encryptionSection {
|
||||||
margin-bottom: 25px;
|
padding-bottom: 24px;
|
||||||
|
border-bottom: 1px solid $menu-border-color;
|
||||||
|
margin-bottom: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_SecurityRoomSettingsTab_upgradeRequired {
|
||||||
|
margin-left: 16px;
|
||||||
|
padding: 4px 16px;
|
||||||
|
border: 1px solid $accent-color;
|
||||||
|
border-radius: 8px;
|
||||||
|
color: $accent-color;
|
||||||
|
font-size: $font-12px;
|
||||||
|
line-height: $font-15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_SecurityRoomSettingsTab_joinRule {
|
||||||
|
.mx_RadioButton {
|
||||||
|
padding-top: 16px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
|
||||||
|
.mx_RadioButton_content {
|
||||||
|
margin-left: 14px;
|
||||||
|
font-weight: $font-semi-bold;
|
||||||
|
font-size: $font-15px;
|
||||||
|
line-height: $font-24px;
|
||||||
|
color: $primary-fg-color;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> span {
|
||||||
|
display: inline-block;
|
||||||
|
margin-left: 34px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
font-size: $font-15px;
|
||||||
|
line-height: $font-24px;
|
||||||
|
color: $secondary-fg-color;
|
||||||
|
|
||||||
|
& + .mx_RadioButton {
|
||||||
|
border-top: 1px solid $menu-border-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_AccessibleButton_kind_link {
|
||||||
|
padding: 0;
|
||||||
|
font-size: inherit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,12 @@ $spacePanelWidth: 71px;
|
|||||||
color: $secondary-fg-color;
|
color: $secondary-fg-color;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_SpaceFeedbackPrompt {
|
||||||
|
border-top: 1px solid $input-border-color;
|
||||||
|
padding-top: 12px;
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX remove this when spaces leaves Beta
|
// XXX remove this when spaces leaves Beta
|
||||||
@ -99,3 +105,25 @@ $spacePanelWidth: 71px;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_SpaceFeedbackPrompt {
|
||||||
|
font-size: $font-15px;
|
||||||
|
line-height: $font-24px;
|
||||||
|
|
||||||
|
> span {
|
||||||
|
color: $secondary-fg-color;
|
||||||
|
position: relative;
|
||||||
|
font-size: inherit;
|
||||||
|
line-height: inherit;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_AccessibleButton_kind_link {
|
||||||
|
color: $accent-color;
|
||||||
|
position: relative;
|
||||||
|
padding: 0;
|
||||||
|
margin-left: 8px;
|
||||||
|
font-size: inherit;
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -67,7 +67,32 @@ limitations under the License.
|
|||||||
.mx_CallView_content {
|
.mx_CallView_content {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
|
||||||
|
> .mx_VideoFeed {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
&.mx_VideoFeed_voice {
|
||||||
|
// We don't want to collide with the call controls that have 52px of height
|
||||||
|
margin-bottom: 52px;
|
||||||
|
background-color: $inverted-bg-color;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_VideoFeed_video {
|
||||||
|
height: 100%;
|
||||||
|
background-color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_VideoFeed_mic {
|
||||||
|
left: 10px;
|
||||||
|
bottom: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_CallView_voice {
|
.mx_CallView_voice {
|
||||||
@ -260,7 +285,7 @@ limitations under the License.
|
|||||||
max-width: 240px;
|
max-width: 240px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_CallView_header_phoneIcon {
|
.mx_CallView_header_callTypeIcon {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-right: 6px;
|
margin-right: 6px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
@ -274,12 +299,19 @@ limitations under the License.
|
|||||||
|
|
||||||
height: 16px;
|
height: 16px;
|
||||||
width: 16px;
|
width: 16px;
|
||||||
background-color: $warning-color;
|
background-color: $secondary-fg-color;
|
||||||
mask-repeat: no-repeat;
|
mask-repeat: no-repeat;
|
||||||
mask-size: contain;
|
mask-size: contain;
|
||||||
mask-position: center;
|
mask-position: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.mx_CallView_header_callTypeIcon_voice::before {
|
||||||
mask-image: url('$(res)/img/element-icons/call/voice-call.svg');
|
mask-image: url('$(res)/img/element-icons/call/voice-call.svg');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.mx_CallView_header_callTypeIcon_video::before {
|
||||||
|
mask-image: url('$(res)/img/element-icons/call/video-call.svg');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_CallView_callControls {
|
.mx_CallView_callControls {
|
||||||
@ -287,9 +319,9 @@ limitations under the License.
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
bottom: 5px;
|
bottom: 5px;
|
||||||
width: 100%;
|
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transition: opacity 0.5s;
|
transition: opacity 0.5s;
|
||||||
|
z-index: 200; // To be above _all_ feeds
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_CallView_callControls_hidden {
|
.mx_CallView_callControls_hidden {
|
||||||
@ -297,10 +329,29 @@ limitations under the License.
|
|||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_CallView_presenting {
|
||||||
|
opacity: 1;
|
||||||
|
transition: opacity 0.5s;
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
margin-top: 18px;
|
||||||
|
padding: 4px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
// Same on both themes
|
||||||
|
color: white;
|
||||||
|
background-color: #17191c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_CallView_presenting_hidden {
|
||||||
|
opacity: 0.001; // opacity 0 can cause a re-layout
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_CallView_callControls_button {
|
.mx_CallView_callControls_button {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
margin-left: 8px;
|
margin-left: 2px;
|
||||||
margin-right: 8px;
|
margin-right: 2px;
|
||||||
|
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
@ -317,17 +368,11 @@ limitations under the License.
|
|||||||
}
|
}
|
||||||
|
|
||||||
.mx_CallView_callControls_dialpad {
|
.mx_CallView_callControls_dialpad {
|
||||||
margin-right: auto;
|
|
||||||
&::before {
|
&::before {
|
||||||
background-image: url('$(res)/img/voip/dialpad.svg');
|
background-image: url('$(res)/img/voip/dialpad.svg');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_CallView_callControls_button_dialpad_hidden {
|
|
||||||
margin-right: auto;
|
|
||||||
cursor: initial;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_CallView_callControls_button_micOn {
|
.mx_CallView_callControls_button_micOn {
|
||||||
&::before {
|
&::before {
|
||||||
background-image: url('$(res)/img/voip/mic-on.svg');
|
background-image: url('$(res)/img/voip/mic-on.svg');
|
||||||
@ -352,6 +397,30 @@ limitations under the License.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_CallView_callControls_button_screensharingOn {
|
||||||
|
&::before {
|
||||||
|
background-image: url('$(res)/img/voip/screensharing-on.svg');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_CallView_callControls_button_screensharingOff {
|
||||||
|
&::before {
|
||||||
|
background-image: url('$(res)/img/voip/screensharing-off.svg');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_CallView_callControls_button_sidebarOn {
|
||||||
|
&::before {
|
||||||
|
background-image: url('$(res)/img/voip/sidebar-on.svg');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_CallView_callControls_button_sidebarOff {
|
||||||
|
&::before {
|
||||||
|
background-image: url('$(res)/img/voip/sidebar-off.svg');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.mx_CallView_callControls_button_hangup {
|
.mx_CallView_callControls_button_hangup {
|
||||||
&::before {
|
&::before {
|
||||||
background-image: url('$(res)/img/voip/hangup.svg');
|
background-image: url('$(res)/img/voip/hangup.svg');
|
||||||
@ -359,17 +428,11 @@ limitations under the License.
|
|||||||
}
|
}
|
||||||
|
|
||||||
.mx_CallView_callControls_button_more {
|
.mx_CallView_callControls_button_more {
|
||||||
margin-left: auto;
|
|
||||||
&::before {
|
&::before {
|
||||||
background-image: url('$(res)/img/voip/more.svg');
|
background-image: url('$(res)/img/voip/more.svg');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_CallView_callControls_button_more_hidden {
|
|
||||||
margin-left: auto;
|
|
||||||
cursor: initial;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_CallView_callControls_button_invisible {
|
.mx_CallView_callControls_button_invisible {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
63
res/css/views/voip/_CallViewSidebar.scss
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 Šimon Brandner <simon.bra.ag@gmail.com>
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.mx_CallViewSidebar {
|
||||||
|
position: absolute;
|
||||||
|
right: 16px;
|
||||||
|
bottom: 16px;
|
||||||
|
z-index: 100; // To be above the primary feed
|
||||||
|
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
|
height: calc(100% - 32px); // Subtract the top and bottom padding
|
||||||
|
width: 20%;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column-reverse;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: flex-end;
|
||||||
|
gap: 12px;
|
||||||
|
|
||||||
|
> .mx_VideoFeed {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
&.mx_VideoFeed_voice {
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
aspect-ratio: 16 / 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_VideoFeed_video {
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_VideoFeed_mic {
|
||||||
|
left: 6px;
|
||||||
|
bottom: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.mx_CallViewSidebar_pipMode {
|
||||||
|
top: 16px;
|
||||||
|
bottom: unset;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
}
|
@ -69,7 +69,6 @@ limitations under the License.
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
max-width: 185px;
|
max-width: 185px;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
direction: rtl;
|
|
||||||
padding: 8px 0px;
|
padding: 8px 0px;
|
||||||
background-color: rgb(0, 0, 0, 0);
|
background-color: rgb(0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
@ -14,37 +14,53 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.mx_VideoFeed_voice {
|
.mx_VideoFeed {
|
||||||
background-color: $inverted-bg-color;
|
overflow: hidden;
|
||||||
}
|
position: relative;
|
||||||
|
|
||||||
|
&.mx_VideoFeed_voice {
|
||||||
.mx_VideoFeed_remote {
|
background-color: $inverted-bg-color;
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
&.mx_VideoFeed_video {
|
|
||||||
background-color: #000;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.mx_VideoFeed_local {
|
.mx_VideoFeed_video {
|
||||||
max-width: 25%;
|
width: 100%;
|
||||||
max-height: 25%;
|
|
||||||
position: absolute;
|
|
||||||
right: 10px;
|
|
||||||
top: 10px;
|
|
||||||
z-index: 100;
|
|
||||||
border-radius: 4px;
|
|
||||||
|
|
||||||
&.mx_VideoFeed_video {
|
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
|
|
||||||
|
&.mx_VideoFeed_video_mirror {
|
||||||
|
transform: scale(-1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_VideoFeed_mic {
|
||||||
|
position: absolute;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
|
||||||
|
background-color: rgba(0, 0, 0, 0.5); // Same on both themes
|
||||||
|
border-radius: 100%;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
position: absolute;
|
||||||
|
content: "";
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
mask-size: contain;
|
||||||
|
mask-position: center;
|
||||||
|
background-color: white; // Same on both themes
|
||||||
|
border-radius: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.mx_VideoFeed_mic_muted::before {
|
||||||
|
mask-image: url('$(res)/img/voip/mic-muted.svg');
|
||||||
|
}
|
||||||
|
|
||||||
|
&.mx_VideoFeed_mic_unmuted::before {
|
||||||
|
mask-image: url('$(res)/img/voip/mic-unmuted.svg');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_VideoFeed_mirror {
|
|
||||||
transform: scale(-1, 1);
|
|
||||||
}
|
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
<svg height="12" viewBox="0 0 12 12" width="12" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<g style="stroke:#454545;stroke-width:.8;fill:none;fill-rule:evenodd;stroke-linecap:round;stroke-linejoin:round" transform="translate(1 1)">
|
|
||||||
<circle cx="5" cy="5" r="5"/>
|
|
||||||
<path d="m0 5h10"/>
|
|
||||||
<path d="m5 0c1.25064019 1.36917645 1.96137638 3.14601693 2 5-.03862362 1.85398307-.74935981 3.63082355-2 5-1.25064019-1.36917645-1.96137638-3.14601693-2-5 .03862362-1.85398307.74935981-3.63082355 2-5z"/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 524 B |
5
res/img/voip/mic-muted.svg
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M1.9206 1.0544C1.68141 0.815201 1.29359 0.815201 1.0544 1.0544C0.815201 1.29359 0.815201 1.68141 1.0544 1.9206L4.55 5.41621V7C4.55 8.3531 5.6469 9.45 7 9.45C7.45436 9.45 7.87983 9.32632 8.24458 9.11079L9.12938 9.99558C8.52863 10.4234 7.7937 10.675 7 10.675C4.97035 10.675 3.325 9.02965 3.325 7C3.325 6.66173 3.05077 6.3875 2.7125 6.3875C2.37423 6.3875 2.1 6.66173 2.1 7C2.1 9.49877 3.97038 11.5607 6.3875 11.8621V12.5125C6.3875 12.8508 6.66173 13.125 7 13.125C7.33827 13.125 7.6125 12.8508 7.6125 12.5125V11.8621C8.50718 11.7505 9.32696 11.3978 10.0047 10.8709L12.0794 12.9456C12.3186 13.1848 12.7064 13.1848 12.9456 12.9456C13.1848 12.7064 13.1848 12.3186 12.9456 12.0794L1.9206 1.0544Z" fill="white"/>
|
||||||
|
<path d="M10.5474 7.96338L11.5073 8.92525C11.7601 8.33424 11.9 7.68346 11.9 7C11.9 6.66173 11.6258 6.3875 11.2875 6.3875C10.9492 6.3875 10.675 6.66173 10.675 7C10.675 7.33336 10.6306 7.65634 10.5474 7.96338Z" fill="white"/>
|
||||||
|
<path d="M4.81385 2.21784L9.45 6.86366V3.325C9.45 1.9719 8.3531 0.875 7 0.875C6.04532 0.875 5.21818 1.42104 4.81385 2.21784Z" fill="white"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
4
res/img/voip/mic-unmuted.svg
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M4.4645 3.29384C4.4645 1.95795 5.59973 0.875 7.0001 0.875C8.40048 0.875 9.53571 1.95795 9.53571 3.29384V6.91127C9.53571 8.24716 8.40048 9.33011 7.0001 9.33011C5.59973 9.33011 4.4645 8.24716 4.4645 6.91127V3.29384Z" fill="white"/>
|
||||||
|
<path d="M2.56269 6.1391C3.01153 6.1391 3.37539 6.4862 3.37539 6.91437C3.37539 8.81701 4.99198 10.3617 6.99032 10.3666C6.99359 10.3666 6.99686 10.3666 7.00014 10.3666C7.0034 10.3666 7.00665 10.3666 7.0099 10.3666C9.00814 10.3616 10.6246 8.81694 10.6246 6.91437C10.6246 6.4862 10.9885 6.1391 11.4373 6.1391C11.8861 6.1391 12.25 6.4862 12.25 6.91437C12.25 9.41469 10.3257 11.4854 7.81283 11.8576V12.3497C7.81283 12.7779 7.44898 13.125 7.00014 13.125C6.5513 13.125 6.18744 12.7779 6.18744 12.3497V11.8576C3.67448 11.4855 1.75 9.41478 1.75 6.91437C1.75 6.4862 2.11386 6.1391 2.56269 6.1391Z" fill="white"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 945 B |
3
res/img/voip/missed-video.svg
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 4.81815C0 3.76379 0.89543 2.90906 2 2.90906H9.33333C10.4379 2.90906 11.3333 3.76379 11.3333 4.81815V11.1818C11.3333 12.2361 10.4379 13.0909 9.33333 13.0909H2C0.895429 13.0909 0 12.2361 0 11.1818V4.81815ZM12.6667 6.09089L14.9169 4.37255C15.3534 4.03921 16 4.33587 16 4.86947V11.1305C16 11.6641 15.3534 11.9607 14.9169 11.6274L12.6667 9.90907V6.09089ZM3.68584 8.54792C3.68584 8.82819 3.45653 9.05751 3.17625 9.05751C2.89598 9.05751 2.66667 8.82819 2.66667 8.54792V6.50957C2.66667 6.22929 2.89598 5.99998 3.17625 5.99998H5.2146C5.49488 5.99998 5.72419 6.22929 5.72419 6.50957C5.72419 6.78984 5.49488 7.01916 5.2146 7.01916H4.39926L6.2083 8.82819L8.73076 6.30573C8.9295 6.10699 9.25054 6.10699 9.44928 6.30573C9.64802 6.50447 9.64802 6.82551 9.44928 7.02425L6.56501 9.90852C6.36627 10.1073 6.04523 10.1073 5.84649 9.90852L3.68584 7.74787V8.54792Z" fill="#8D97A5"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1016 B |
4
res/img/voip/missed-voice.svg
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M4.00016 6C4.36683 6 4.66683 5.7 4.66683 5.33333V4.28667L7.4935 7.11333C7.7535 7.37333 8.1735 7.37333 8.4335 7.11333L12.2068 3.34C12.4668 3.08 12.4668 2.66 12.2068 2.4C11.9468 2.14 11.5268 2.14 11.2668 2.4L7.96683 5.7L5.60016 3.33333H6.66683C7.0335 3.33333 7.3335 3.03333 7.3335 2.66667C7.3335 2.3 7.0335 2 6.66683 2H4.00016C3.6335 2 3.3335 2.3 3.3335 2.66667V5.33333C3.3335 5.7 3.6335 6 4.00016 6Z" fill="#8D97A5"/>
|
||||||
|
<path d="M8.00557 8.67107C6.88076 8.62784 4.56757 8.91974 4.0052 9.06763C3.97195 9.07638 3.93363 9.08616 3.89078 9.0971C3.02734 9.31746 0.321813 10.008 0.0294949 12.1958C-0.196977 13.8909 0.937169 14.4039 1.50412 14.3258C1.89653 14.2766 3.02006 14.0989 4.05816 13.9127C5.07753 13.7298 5.07701 13.0573 5.07666 12.6026C5.07665 12.5943 5.07664 12.586 5.07664 12.5778L5.07665 11.6636C5.07665 11.4308 5.29543 11.2962 5.5972 11.2598C6.66548 11.1147 7.5573 11.1143 8.00369 11.1143L8.00745 11.1143C8.45377 11.1143 9.33453 11.1147 10.4028 11.2598C10.7046 11.2962 10.9234 11.4308 10.9234 11.6636L10.9234 12.5778C10.9234 12.586 10.9233 12.5943 10.9233 12.6026C10.923 13.0573 10.9225 13.7298 11.9418 13.9127C12.9799 14.099 14.1035 14.2766 14.4959 14.3258C15.0628 14.4039 16.197 13.8909 15.9705 12.1958C15.6782 10.008 12.9727 9.31747 12.1092 9.0971C12.0664 9.08617 12.0281 9.07639 11.9948 9.06764C11.4324 8.91975 9.13037 8.62783 8.00557 8.67107Z" fill="#8D97A5"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
18
res/img/voip/screensharing-off.svg
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<svg width="50" height="49" viewBox="0 0 50 49" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g filter="url(#filter0_d)">
|
||||||
|
<circle cx="25" cy="20" r="20" fill="white"/>
|
||||||
|
</g>
|
||||||
|
<rect x="14.6008" y="12.8" width="20.8" height="14.4" rx="1.6" fill="white" stroke="#737D8C" stroke-width="1.6"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M24.3617 23.36C24.3617 23.7135 24.6483 24 25.0017 24C25.3552 24 25.6417 23.7135 25.6417 23.36L25.6417 18.1851L27.6692 20.2125C27.9191 20.4625 28.3243 20.4625 28.5743 20.2125C28.8242 19.9626 28.8242 19.5574 28.5743 19.3075L25.4543 16.1875C25.2043 15.9375 24.7991 15.9375 24.5492 16.1875L21.4292 19.3075C21.1792 19.5574 21.1792 19.9626 21.4292 20.2125C21.6791 20.4625 22.0843 20.4625 22.3343 20.2125L24.3617 18.1851L24.3617 23.36Z" fill="#737D8C"/>
|
||||||
|
<defs>
|
||||||
|
<filter id="filter0_d" x="0.947663" y="0" width="48.1047" height="48.1047" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||||
|
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||||
|
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
|
||||||
|
<feOffset dy="4.05234"/>
|
||||||
|
<feGaussianBlur stdDeviation="2.02617"/>
|
||||||
|
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0"/>
|
||||||
|
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/>
|
||||||
|
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
18
res/img/voip/screensharing-on.svg
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<svg width="50" height="49" viewBox="0 0 50 49" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g filter="url(#filter0_d)">
|
||||||
|
<circle cx="25" cy="20" r="20" fill="#0DBD8B"/>
|
||||||
|
</g>
|
||||||
|
<rect x="14.6008" y="12.8" width="20.8" height="14.4" rx="1.6" fill="#0DBD8B" stroke="white" stroke-width="1.6"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M24.3617 23.36C24.3617 23.7135 24.6483 24 25.0017 24C25.3552 24 25.6417 23.7135 25.6417 23.36L25.6417 18.1851L27.6692 20.2125C27.9191 20.4625 28.3243 20.4625 28.5743 20.2125C28.8242 19.9626 28.8242 19.5574 28.5743 19.3075L25.4543 16.1875C25.2043 15.9375 24.7991 15.9375 24.5492 16.1875L21.4292 19.3075C21.1792 19.5574 21.1792 19.9626 21.4292 20.2125C21.6791 20.4625 22.0843 20.4625 22.3343 20.2125L24.3617 18.1851L24.3617 23.36Z" fill="white"/>
|
||||||
|
<defs>
|
||||||
|
<filter id="filter0_d" x="0.947663" y="0" width="48.1047" height="48.1047" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||||
|
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||||
|
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
|
||||||
|
<feOffset dy="4.05234"/>
|
||||||
|
<feGaussianBlur stdDeviation="2.02617"/>
|
||||||
|
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0"/>
|
||||||
|
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/>
|
||||||
|
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
20
res/img/voip/sidebar-off.svg
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<svg width="48" height="47" viewBox="0 0 48 47" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g filter="url(#filter0_d)">
|
||||||
|
<circle cx="24" cy="20" r="20" fill="#737D8C"/>
|
||||||
|
</g>
|
||||||
|
<rect x="12.5618" y="12.8992" width="20.3525" height="14.4496" rx="2.43819" fill="white" stroke="#737D8C" stroke-width="1.12362"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M31.9132 20.5009C33.2675 20.5009 34.3655 19.4205 34.3655 18.0876C34.3655 16.7548 33.2675 15.6743 31.9132 15.6743C30.5589 15.6743 29.4609 16.7548 29.4609 18.0876C29.4609 19.4205 30.5589 20.5009 31.9132 20.5009ZM27.8242 26.132C27.8282 23.7187 28.976 21.3054 31.9113 21.3054C34.7818 21.3054 35.9984 23.7187 35.9984 26.132C35.9984 28.5453 32.7288 28.5453 31.9113 28.5453C31.0939 28.5453 27.8206 28.3403 27.8242 26.132Z" fill="white"/>
|
||||||
|
<path d="M27.8242 26.132L28.386 26.1329L27.8242 26.132ZM35.9984 26.132H35.4366H35.9984ZM33.8037 18.0876C33.8037 19.1017 32.9658 19.9391 31.9132 19.9391V21.0627C33.5693 21.0627 34.9273 19.7392 34.9273 18.0876H33.8037ZM31.9132 16.2361C32.9658 16.2361 33.8037 17.0735 33.8037 18.0876H34.9273C34.9273 16.4361 33.5693 15.1125 31.9132 15.1125V16.2361ZM30.0227 18.0876C30.0227 17.0735 30.8606 16.2361 31.9132 16.2361V15.1125C30.2571 15.1125 28.8991 16.4361 28.8991 18.0876H30.0227ZM31.9132 19.9391C30.8606 19.9391 30.0227 19.1017 30.0227 18.0876H28.8991C28.8991 19.7392 30.2571 21.0627 31.9132 21.0627V19.9391ZM31.9113 20.7436C30.2659 20.7436 29.0747 21.4314 28.3132 22.4845C27.5693 23.5133 27.2645 24.8471 27.2624 26.1311L28.386 26.1329C28.3879 25.0036 28.659 23.924 29.2238 23.1429C29.771 22.386 30.6214 21.8672 31.9113 21.8672V20.7436ZM36.5602 26.132C36.5602 24.8414 36.2364 23.5081 35.4845 22.4817C34.7168 21.4338 33.5275 20.7436 31.9113 20.7436V21.8672C33.1657 21.8672 34.0199 22.3836 34.5781 23.1457C35.1521 23.9293 35.4366 25.0093 35.4366 26.132H36.5602ZM31.9113 29.1071C32.3157 29.1071 33.4213 29.1105 34.4365 28.7775C34.9481 28.6096 35.4778 28.3438 35.8839 27.9122C36.3025 27.4673 36.5602 26.8767 36.5602 26.132H35.4366C35.4366 26.594 35.2857 26.9083 35.0656 27.1422C34.8331 27.3893 34.4943 27.576 34.0863 27.7098C33.2623 27.9801 32.3244 27.9835 31.9113 27.9835V29.1071ZM27.2624 26.1311C27.26 27.5996 28.3757 28.3418 29.3716 28.6961C30.3797 29.0547 31.4763 29.1071 31.9113 29.1071V27.9835C31.5289 27.9835 30.5802 27.9334 29.7482 27.6375C28.9039 27.3371 28.3848 26.8728 28.386 26.1329L27.2624 26.1311Z" fill="#737D8C"/>
|
||||||
|
<rect x="0.0339116" y="-0.787426" width="29.1443" height="3.36793" rx="1.68396" transform="matrix(0.681883 0.731461 -0.742244 0.670129 13.0943 8.71545)" fill="white" stroke="#737D8C" stroke-width="1.12362"/>
|
||||||
|
<defs>
|
||||||
|
<filter id="filter0_d" x="0.589744" y="0" width="46.8205" height="46.8205" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||||
|
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||||
|
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
|
||||||
|
<feOffset dy="3.41026"/>
|
||||||
|
<feGaussianBlur stdDeviation="1.70513"/>
|
||||||
|
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0"/>
|
||||||
|
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/>
|
||||||
|
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 3.2 KiB |
19
res/img/voip/sidebar-on.svg
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<svg width="48" height="47" viewBox="0 0 48 47" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g filter="url(#filter0_d)">
|
||||||
|
<circle cx="24" cy="20" r="20" fill="white"/>
|
||||||
|
</g>
|
||||||
|
<rect x="12.5" y="12.5" width="20.4763" height="15.3319" rx="2.5" fill="#737D8C" stroke="white"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M31.912 20.5618C33.2664 20.5618 34.3643 19.4287 34.3643 18.0309C34.3643 16.6331 33.2664 15.5 31.912 15.5C30.5577 15.5 29.4598 16.6331 29.4598 18.0309C29.4598 19.4287 30.5577 20.5618 31.912 20.5618ZM27.8242 26.467C27.8282 23.9361 28.976 21.4052 31.9113 21.4052C34.7818 21.4052 35.9985 23.9361 35.9985 26.467C35.9985 28.9978 32.7288 28.9978 31.9114 28.9978C31.0939 28.9978 27.8206 28.7829 27.8242 26.467Z" fill="#737D8C"/>
|
||||||
|
<path d="M27.8242 26.467L27.3242 26.4662L27.8242 26.467ZM35.9985 26.467H36.4985H35.9985ZM33.8643 18.0309C33.8643 19.1675 32.9755 20.0618 31.912 20.0618V21.0618C33.5573 21.0618 34.8643 19.6898 34.8643 18.0309H33.8643ZM31.912 16C32.9755 16 33.8643 16.8943 33.8643 18.0309H34.8643C34.8643 16.372 33.5573 15 31.912 15V16ZM29.9598 18.0309C29.9598 16.8943 30.8486 16 31.912 16V15C30.2668 15 28.9598 16.372 28.9598 18.0309H29.9598ZM31.912 20.0618C30.8486 20.0618 29.9598 19.1675 29.9598 18.0309H28.9598C28.9598 19.6898 30.2668 21.0618 31.912 21.0618V20.0618ZM31.9113 20.9052C30.2753 20.9052 29.1023 21.622 28.3569 22.7032C27.6274 23.7612 27.3263 25.1361 27.3242 26.4662L28.3242 26.4677C28.3261 25.2669 28.6009 24.1109 29.1802 23.2708C29.7434 22.4538 30.612 21.9052 31.9113 21.9052V20.9052ZM36.4985 26.467C36.4985 25.1313 36.1789 23.7567 35.4412 22.7007C34.6893 21.6242 33.5177 20.9052 31.9113 20.9052V21.9052C33.1755 21.9052 34.0475 22.4516 34.6214 23.2733C35.2097 24.1154 35.4985 25.2717 35.4985 26.467H36.4985ZM31.9114 29.4978C32.3162 29.4978 33.416 29.5011 34.4241 29.1543C34.9326 28.9794 35.4519 28.7044 35.847 28.264C36.2515 27.8131 36.4985 27.2184 36.4985 26.467H35.4985C35.4985 26.9809 35.3367 27.3354 35.1026 27.5962C34.8591 27.8677 34.5099 28.0673 34.0988 28.2087C33.2677 28.4946 32.3239 28.4978 31.9114 28.4978V29.4978ZM27.3242 26.4662C27.3219 27.9345 28.3854 28.6964 29.3851 29.0693C30.3864 29.4429 31.4779 29.4978 31.9114 29.4978V28.4978C31.5274 28.4978 30.5735 28.4453 29.7346 28.1324C28.8943 27.8189 28.3229 27.3153 28.3242 26.4677L27.3242 26.4662Z" fill="white"/>
|
||||||
|
<defs>
|
||||||
|
<filter id="filter0_d" x="0.589744" y="0" width="46.8205" height="46.8205" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||||
|
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||||
|
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
|
||||||
|
<feOffset dy="3.41026"/>
|
||||||
|
<feGaussianBlur stdDeviation="1.70513"/>
|
||||||
|
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.15 0"/>
|
||||||
|
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/>
|
||||||
|
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.9 KiB |
@ -56,7 +56,6 @@ limitations under the License.
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { MatrixClientPeg } from './MatrixClientPeg';
|
import { MatrixClientPeg } from './MatrixClientPeg';
|
||||||
import PlatformPeg from './PlatformPeg';
|
|
||||||
import Modal from './Modal';
|
import Modal from './Modal';
|
||||||
import { _t } from './languageHandler';
|
import { _t } from './languageHandler';
|
||||||
import dis from './dispatcher/dispatcher';
|
import dis from './dispatcher/dispatcher';
|
||||||
@ -80,7 +79,6 @@ import CountlyAnalytics from "./CountlyAnalytics";
|
|||||||
import { UIFeature } from "./settings/UIFeature";
|
import { UIFeature } from "./settings/UIFeature";
|
||||||
import { CallError } from "matrix-js-sdk/src/webrtc/call";
|
import { CallError } from "matrix-js-sdk/src/webrtc/call";
|
||||||
import { logger } from 'matrix-js-sdk/src/logger';
|
import { logger } from 'matrix-js-sdk/src/logger';
|
||||||
import DesktopCapturerSourcePicker from "./components/views/elements/DesktopCapturerSourcePicker";
|
|
||||||
import { Action } from './dispatcher/actions';
|
import { Action } from './dispatcher/actions';
|
||||||
import VoipUserMapper from './VoipUserMapper';
|
import VoipUserMapper from './VoipUserMapper';
|
||||||
import { addManagedHybridWidget, isManagedHybridWidgetEnabled } from './widgets/ManagedHybrid';
|
import { addManagedHybridWidget, isManagedHybridWidgetEnabled } from './widgets/ManagedHybrid';
|
||||||
@ -129,14 +127,9 @@ interface ThirdpartyLookupResponse {
|
|||||||
fields: ThirdpartyLookupResponseFields;
|
fields: ThirdpartyLookupResponseFields;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unlike 'CallType' in js-sdk, this one includes screen sharing
|
|
||||||
// (because a screen sharing call is only a screen sharing call to the caller,
|
|
||||||
// to the callee it's just a video call, at least as far as the current impl
|
|
||||||
// is concerned).
|
|
||||||
export enum PlaceCallType {
|
export enum PlaceCallType {
|
||||||
Voice = 'voice',
|
Voice = 'voice',
|
||||||
Video = 'video',
|
Video = 'video',
|
||||||
ScreenSharing = 'screensharing',
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum CallHandlerEvent {
|
export enum CallHandlerEvent {
|
||||||
@ -491,28 +484,18 @@ export default class CallHandler extends EventEmitter {
|
|||||||
break;
|
break;
|
||||||
case CallState.Ended:
|
case CallState.Ended:
|
||||||
{
|
{
|
||||||
Analytics.trackEvent('voip', 'callEnded', 'hangupReason', call.hangupReason);
|
const hangupReason = call.hangupReason;
|
||||||
|
Analytics.trackEvent('voip', 'callEnded', 'hangupReason', hangupReason);
|
||||||
this.removeCallForRoom(mappedRoomId);
|
this.removeCallForRoom(mappedRoomId);
|
||||||
if (oldState === CallState.InviteSent && (
|
if (oldState === CallState.InviteSent && call.hangupParty === CallParty.Remote) {
|
||||||
call.hangupParty === CallParty.Remote ||
|
|
||||||
(call.hangupParty === CallParty.Local && call.hangupReason === CallErrorCode.InviteTimeout)
|
|
||||||
)) {
|
|
||||||
this.play(AudioID.Busy);
|
this.play(AudioID.Busy);
|
||||||
let title;
|
let title;
|
||||||
let description;
|
let description;
|
||||||
if (call.hangupReason === CallErrorCode.UserHangup) {
|
// TODO: We should either do away with these or figure out a copy for each code (expect user_hangup...)
|
||||||
title = _t("Call Declined");
|
if (call.hangupReason === CallErrorCode.UserBusy) {
|
||||||
description = _t("The other party declined the call.");
|
|
||||||
} else if (call.hangupReason === CallErrorCode.UserBusy) {
|
|
||||||
title = _t("User Busy");
|
title = _t("User Busy");
|
||||||
description = _t("The user you called is busy.");
|
description = _t("The user you called is busy.");
|
||||||
} else if (call.hangupReason === CallErrorCode.InviteTimeout) {
|
} else if (hangupReason && ![CallErrorCode.UserHangup, "user hangup"].includes(hangupReason)) {
|
||||||
title = _t("Call Failed");
|
|
||||||
// XXX: full stop appended as some relic here, but these
|
|
||||||
// strings need proper input from design anyway, so let's
|
|
||||||
// not change this string until we have a proper one.
|
|
||||||
description = _t('The remote side failed to pick up') + '.';
|
|
||||||
} else {
|
|
||||||
title = _t("Call Failed");
|
title = _t("Call Failed");
|
||||||
description = _t("The call could not be established");
|
description = _t("The call could not be established");
|
||||||
}
|
}
|
||||||
@ -521,7 +504,7 @@ export default class CallHandler extends EventEmitter {
|
|||||||
title, description,
|
title, description,
|
||||||
});
|
});
|
||||||
} else if (
|
} else if (
|
||||||
call.hangupReason === CallErrorCode.AnsweredElsewhere && oldState === CallState.Connecting
|
hangupReason === CallErrorCode.AnsweredElsewhere && oldState === CallState.Connecting
|
||||||
) {
|
) {
|
||||||
Modal.createTrackedDialog('Call Handler', 'Call Failed', ErrorDialog, {
|
Modal.createTrackedDialog('Call Handler', 'Call Failed', ErrorDialog, {
|
||||||
title: _t("Answered Elsewhere"),
|
title: _t("Answered Elsewhere"),
|
||||||
@ -738,25 +721,6 @@ export default class CallHandler extends EventEmitter {
|
|||||||
call.placeVoiceCall();
|
call.placeVoiceCall();
|
||||||
} else if (type === 'video') {
|
} else if (type === 'video') {
|
||||||
call.placeVideoCall();
|
call.placeVideoCall();
|
||||||
} else if (type === PlaceCallType.ScreenSharing) {
|
|
||||||
const screenCapErrorString = PlatformPeg.get().screenCaptureErrorString();
|
|
||||||
if (screenCapErrorString) {
|
|
||||||
this.removeCallForRoom(roomId);
|
|
||||||
console.log("Can't capture screen: " + screenCapErrorString);
|
|
||||||
Modal.createTrackedDialog('Call Handler', 'Unable to capture screen', ErrorDialog, {
|
|
||||||
title: _t('Unable to capture screen'),
|
|
||||||
description: screenCapErrorString,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
call.placeScreenSharingCall(
|
|
||||||
async (): Promise<DesktopCapturerSource> => {
|
|
||||||
const { finished } = Modal.createDialog(DesktopCapturerSourcePicker);
|
|
||||||
const [source] = await finished;
|
|
||||||
return source;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
console.error("Unknown conf call type: " + type);
|
console.error("Unknown conf call type: " + type);
|
||||||
}
|
}
|
||||||
|
@ -146,23 +146,23 @@ export default class IdentityAuthClient {
|
|||||||
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||||
const { finished } = Modal.createTrackedDialog('Default identity server terms warning', '',
|
const { finished } = Modal.createTrackedDialog('Default identity server terms warning', '',
|
||||||
QuestionDialog, {
|
QuestionDialog, {
|
||||||
title: _t("Identity server has no terms of service"),
|
title: _t("Identity server has no terms of service"),
|
||||||
description: (
|
description: (
|
||||||
<div>
|
<div>
|
||||||
<p>{ _t(
|
<p>{ _t(
|
||||||
"This action requires accessing the default identity server " +
|
"This action requires accessing the default identity server " +
|
||||||
"<server /> to validate an email address or phone number, " +
|
"<server /> to validate an email address or phone number, " +
|
||||||
"but the server does not have any terms of service.", {},
|
"but the server does not have any terms of service.", {},
|
||||||
{
|
{
|
||||||
server: () => <b>{ abbreviateUrl(identityServerUrl) }</b>,
|
server: () => <b>{ abbreviateUrl(identityServerUrl) }</b>,
|
||||||
},
|
},
|
||||||
) }</p>
|
) }</p>
|
||||||
<p>{ _t(
|
<p>{ _t(
|
||||||
"Only continue if you trust the owner of the server.",
|
"Only continue if you trust the owner of the server.",
|
||||||
) }</p>
|
) }</p>
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
button: _t("Trust"),
|
button: _t("Trust"),
|
||||||
});
|
});
|
||||||
const [confirmed] = await finished;
|
const [confirmed] = await finished;
|
||||||
if (confirmed) {
|
if (confirmed) {
|
||||||
|
@ -51,10 +51,15 @@ export async function startAnyRegistrationFlow(options) {
|
|||||||
description: _t("Use your account or create a new one to continue."),
|
description: _t("Use your account or create a new one to continue."),
|
||||||
button: _t("Create Account"),
|
button: _t("Create Account"),
|
||||||
extraButtons: [
|
extraButtons: [
|
||||||
<button key="start_login" onClick={() => {
|
<button
|
||||||
modal.close();
|
key="start_login"
|
||||||
dis.dispatch({ action: 'start_login', screenAfterLogin: options.screen_after });
|
onClick={() => {
|
||||||
}}>{ _t('Sign In') }</button>,
|
modal.close();
|
||||||
|
dis.dispatch({ action: 'start_login', screenAfterLogin: options.screen_after });
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{ _t('Sign In') }
|
||||||
|
</button>,
|
||||||
],
|
],
|
||||||
onFinished: (proceed) => {
|
onFinished: (proceed) => {
|
||||||
if (proceed) {
|
if (proceed) {
|
||||||
|
@ -34,7 +34,6 @@ import { getAddressType } from './UserAddress';
|
|||||||
import { abbreviateUrl } from './utils/UrlUtils';
|
import { abbreviateUrl } from './utils/UrlUtils';
|
||||||
import { getDefaultIdentityServerUrl, useDefaultIdentityServer } from './utils/IdentityServerUtils';
|
import { getDefaultIdentityServerUrl, useDefaultIdentityServer } from './utils/IdentityServerUtils';
|
||||||
import { isPermalinkHost, parsePermalink } from "./utils/permalinks/Permalinks";
|
import { isPermalinkHost, parsePermalink } from "./utils/permalinks/Permalinks";
|
||||||
import { inviteUsersToRoom } from "./RoomInvite";
|
|
||||||
import { WidgetType } from "./widgets/WidgetType";
|
import { WidgetType } from "./widgets/WidgetType";
|
||||||
import { Jitsi } from "./widgets/Jitsi";
|
import { Jitsi } from "./widgets/Jitsi";
|
||||||
import { parseFragment as parseHtml, Element as ChildElement } from "parse5";
|
import { parseFragment as parseHtml, Element as ChildElement } from "parse5";
|
||||||
@ -49,6 +48,7 @@ import { UIFeature } from "./settings/UIFeature";
|
|||||||
import { CHAT_EFFECTS } from "./effects";
|
import { CHAT_EFFECTS } from "./effects";
|
||||||
import CallHandler from "./CallHandler";
|
import CallHandler from "./CallHandler";
|
||||||
import { guessAndSetDMRoom } from "./Rooms";
|
import { guessAndSetDMRoom } from "./Rooms";
|
||||||
|
import { upgradeRoom } from './utils/RoomUpgrade';
|
||||||
import UploadConfirmDialog from './components/views/dialogs/UploadConfirmDialog';
|
import UploadConfirmDialog from './components/views/dialogs/UploadConfirmDialog';
|
||||||
import ErrorDialog from './components/views/dialogs/ErrorDialog';
|
import ErrorDialog from './components/views/dialogs/ErrorDialog';
|
||||||
import DevtoolsDialog from './components/views/dialogs/DevtoolsDialog';
|
import DevtoolsDialog from './components/views/dialogs/DevtoolsDialog';
|
||||||
@ -277,50 +277,8 @@ export const Commands = [
|
|||||||
/*isPriority=*/false, /*isStatic=*/true);
|
/*isPriority=*/false, /*isStatic=*/true);
|
||||||
|
|
||||||
return success(finished.then(async ([resp]) => {
|
return success(finished.then(async ([resp]) => {
|
||||||
if (!resp.continue) return;
|
if (!resp?.continue) return;
|
||||||
|
await upgradeRoom(room, args, resp.invite);
|
||||||
let checkForUpgradeFn;
|
|
||||||
try {
|
|
||||||
const upgradePromise = cli.upgradeRoom(roomId, args);
|
|
||||||
|
|
||||||
// We have to wait for the js-sdk to give us the room back so
|
|
||||||
// we can more effectively abuse the MultiInviter behaviour
|
|
||||||
// which heavily relies on the Room object being available.
|
|
||||||
if (resp.invite) {
|
|
||||||
checkForUpgradeFn = async (newRoom) => {
|
|
||||||
// The upgradePromise should be done by the time we await it here.
|
|
||||||
const { replacement_room: newRoomId } = await upgradePromise;
|
|
||||||
if (newRoom.roomId !== newRoomId) return;
|
|
||||||
|
|
||||||
const toInvite = [
|
|
||||||
...room.getMembersWithMembership("join"),
|
|
||||||
...room.getMembersWithMembership("invite"),
|
|
||||||
].map(m => m.userId).filter(m => m !== cli.getUserId());
|
|
||||||
|
|
||||||
if (toInvite.length > 0) {
|
|
||||||
// Errors are handled internally to this function
|
|
||||||
await inviteUsersToRoom(newRoomId, toInvite);
|
|
||||||
}
|
|
||||||
|
|
||||||
cli.removeListener('Room', checkForUpgradeFn);
|
|
||||||
};
|
|
||||||
cli.on('Room', checkForUpgradeFn);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We have to await after so that the checkForUpgradesFn has a proper reference
|
|
||||||
// to the new room's ID.
|
|
||||||
await upgradePromise;
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
|
|
||||||
if (checkForUpgradeFn) cli.removeListener('Room', checkForUpgradeFn);
|
|
||||||
|
|
||||||
Modal.createTrackedDialog('Slash Commands', 'room upgrade error', ErrorDialog, {
|
|
||||||
title: _t('Error upgrading room'),
|
|
||||||
description: _t(
|
|
||||||
'Double check that your server supports the room version chosen and try again.'),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
return reject(this.getUsage());
|
return reject(this.getUsage());
|
||||||
|
@ -25,11 +25,44 @@ import { Action } from './dispatcher/actions';
|
|||||||
import defaultDispatcher from './dispatcher/dispatcher';
|
import defaultDispatcher from './dispatcher/dispatcher';
|
||||||
import { SetRightPanelPhasePayload } from './dispatcher/payloads/SetRightPanelPhasePayload';
|
import { SetRightPanelPhasePayload } from './dispatcher/payloads/SetRightPanelPhasePayload';
|
||||||
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||||
|
import { MatrixClientPeg } from "./MatrixClientPeg";
|
||||||
|
|
||||||
// These functions are frequently used just to check whether an event has
|
// These functions are frequently used just to check whether an event has
|
||||||
// any text to display at all. For this reason they return deferred values
|
// any text to display at all. For this reason they return deferred values
|
||||||
// to avoid the expense of looking up translations when they're not needed.
|
// to avoid the expense of looking up translations when they're not needed.
|
||||||
|
|
||||||
|
function textForCallInviteEvent(event: MatrixEvent): () => string | null {
|
||||||
|
const getSenderName = () => event.sender ? event.sender.name : _t('Someone');
|
||||||
|
// FIXME: Find a better way to determine this from the event?
|
||||||
|
let isVoice = true;
|
||||||
|
if (event.getContent().offer && event.getContent().offer.sdp &&
|
||||||
|
event.getContent().offer.sdp.indexOf('m=video') !== -1) {
|
||||||
|
isVoice = false;
|
||||||
|
}
|
||||||
|
const isSupported = MatrixClientPeg.get().supportsVoip();
|
||||||
|
|
||||||
|
// This ladder could be reduced down to a couple string variables, however other languages
|
||||||
|
// can have a hard time translating those strings. In an effort to make translations easier
|
||||||
|
// and more accurate, we break out the string-based variables to a couple booleans.
|
||||||
|
if (isVoice && isSupported) {
|
||||||
|
return () => _t("%(senderName)s placed a voice call.", {
|
||||||
|
senderName: getSenderName(),
|
||||||
|
});
|
||||||
|
} else if (isVoice && !isSupported) {
|
||||||
|
return () => _t("%(senderName)s placed a voice call. (not supported by this browser)", {
|
||||||
|
senderName: getSenderName(),
|
||||||
|
});
|
||||||
|
} else if (!isVoice && isSupported) {
|
||||||
|
return () => _t("%(senderName)s placed a video call.", {
|
||||||
|
senderName: getSenderName(),
|
||||||
|
});
|
||||||
|
} else if (!isVoice && !isSupported) {
|
||||||
|
return () => _t("%(senderName)s placed a video call. (not supported by this browser)", {
|
||||||
|
senderName: getSenderName(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function textForMemberEvent(ev: MatrixEvent, allowJSX: boolean, showHiddenEvents?: boolean): () => string | null {
|
function textForMemberEvent(ev: MatrixEvent, allowJSX: boolean, showHiddenEvents?: boolean): () => string | null {
|
||||||
// XXX: SYJS-16 "sender is sometimes null for join messages"
|
// XXX: SYJS-16 "sender is sometimes null for join messages"
|
||||||
const senderName = ev.sender ? ev.sender.name : ev.getSender();
|
const senderName = ev.sender ? ev.sender.name : ev.getSender();
|
||||||
@ -567,6 +600,7 @@ interface IHandlers {
|
|||||||
|
|
||||||
const handlers: IHandlers = {
|
const handlers: IHandlers = {
|
||||||
'm.room.message': textForMessageEvent,
|
'm.room.message': textForMessageEvent,
|
||||||
|
'm.call.invite': textForCallInviteEvent,
|
||||||
};
|
};
|
||||||
|
|
||||||
const stateHandlers: IHandlers = {
|
const stateHandlers: IHandlers = {
|
||||||
|
@ -163,7 +163,7 @@ const shortcuts: Record<Categories, IShortcut[]> = {
|
|||||||
modifiers: [Modifiers.SHIFT],
|
modifiers: [Modifiers.SHIFT],
|
||||||
key: Key.PAGE_UP,
|
key: Key.PAGE_UP,
|
||||||
}],
|
}],
|
||||||
description: _td("Jump to oldest unread message"),
|
description: _td("Jump to oldest unread message"),
|
||||||
}, {
|
}, {
|
||||||
keybinds: [{
|
keybinds: [{
|
||||||
modifiers: [CMD_OR_CTRL, Modifiers.SHIFT],
|
modifiers: [CMD_OR_CTRL, Modifiers.SHIFT],
|
||||||
|
@ -269,7 +269,7 @@ export default class CreateKeyBackupDialog extends React.PureComponent {
|
|||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>{ _t("Advanced") }</summary>
|
<summary>{ _t("Advanced") }</summary>
|
||||||
<AccessibleButton kind='primary' onClick={this._onSkipPassPhraseClick} >
|
<AccessibleButton kind='primary' onClick={this._onSkipPassPhraseClick}>
|
||||||
{ _t("Set up with a Security Key") }
|
{ _t("Set up with a Security Key") }
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
</details>
|
</details>
|
||||||
|
@ -474,7 +474,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
|
|||||||
outlined
|
outlined
|
||||||
>
|
>
|
||||||
<div className="mx_CreateSecretStorageDialog_optionTitle">
|
<div className="mx_CreateSecretStorageDialog_optionTitle">
|
||||||
<span className="mx_CreateSecretStorageDialog_optionIcon mx_CreateSecretStorageDialog_optionIcon_secureBackup"></span>
|
<span className="mx_CreateSecretStorageDialog_optionIcon mx_CreateSecretStorageDialog_optionIcon_secureBackup" />
|
||||||
{ _t("Generate a Security Key") }
|
{ _t("Generate a Security Key") }
|
||||||
</div>
|
</div>
|
||||||
<div>{ _t("We’ll generate a Security Key for you to store somewhere safe, like a password manager or a safe.") }</div>
|
<div>{ _t("We’ll generate a Security Key for you to store somewhere safe, like a password manager or a safe.") }</div>
|
||||||
@ -493,7 +493,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
|
|||||||
outlined
|
outlined
|
||||||
>
|
>
|
||||||
<div className="mx_CreateSecretStorageDialog_optionTitle">
|
<div className="mx_CreateSecretStorageDialog_optionTitle">
|
||||||
<span className="mx_CreateSecretStorageDialog_optionIcon mx_CreateSecretStorageDialog_optionIcon_securePhrase"></span>
|
<span className="mx_CreateSecretStorageDialog_optionIcon mx_CreateSecretStorageDialog_optionIcon_securePhrase" />
|
||||||
{ _t("Enter a Security Phrase") }
|
{ _t("Enter a Security Phrase") }
|
||||||
</div>
|
</div>
|
||||||
<div>{ _t("Use a secret phrase only you know, and optionally save a Security Key to use for backup.") }</div>
|
<div>{ _t("Use a secret phrase only you know, and optionally save a Security Key to use for backup.") }</div>
|
||||||
@ -701,7 +701,8 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
|
|||||||
<code ref={this._collectRecoveryKeyNode}>{ this._recoveryKey.encodedPrivateKey }</code>
|
<code ref={this._collectRecoveryKeyNode}>{ this._recoveryKey.encodedPrivateKey }</code>
|
||||||
</div>
|
</div>
|
||||||
<div className="mx_CreateSecretStorageDialog_recoveryKeyButtons">
|
<div className="mx_CreateSecretStorageDialog_recoveryKeyButtons">
|
||||||
<AccessibleButton kind='primary' className="mx_Dialog_primary"
|
<AccessibleButton kind='primary'
|
||||||
|
className="mx_Dialog_primary"
|
||||||
onClick={this._onDownloadClick}
|
onClick={this._onDownloadClick}
|
||||||
disabled={this.state.phase === PHASE_STORING}
|
disabled={this.state.phase === PHASE_STORING}
|
||||||
>
|
>
|
||||||
|
@ -148,8 +148,12 @@ export default class ExportE2eKeysDialog extends React.Component {
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className='mx_E2eKeysDialog_inputCell'>
|
<div className='mx_E2eKeysDialog_inputCell'>
|
||||||
<input ref={this._passphrase1} id='passphrase1'
|
<input
|
||||||
autoFocus={true} size='64' type='password'
|
ref={this._passphrase1}
|
||||||
|
id='passphrase1'
|
||||||
|
autoFocus={true}
|
||||||
|
size='64'
|
||||||
|
type='password'
|
||||||
disabled={disableForm}
|
disabled={disableForm}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -161,8 +165,10 @@ export default class ExportE2eKeysDialog extends React.Component {
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className='mx_E2eKeysDialog_inputCell'>
|
<div className='mx_E2eKeysDialog_inputCell'>
|
||||||
<input ref={this._passphrase2} id='passphrase2'
|
<input ref={this._passphrase2}
|
||||||
size='64' type='password'
|
id='passphrase2'
|
||||||
|
size='64'
|
||||||
|
type='password'
|
||||||
disabled={disableForm}
|
disabled={disableForm}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -174,7 +174,10 @@ export default class ImportE2eKeysDialog extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='mx_Dialog_buttons'>
|
<div className='mx_Dialog_buttons'>
|
||||||
<input className='mx_Dialog_primary' type='submit' value={_t('Import')}
|
<input
|
||||||
|
className='mx_Dialog_primary'
|
||||||
|
type='submit'
|
||||||
|
value={_t('Import')}
|
||||||
disabled={!this.state.enableSubmit || disableForm}
|
disabled={!this.state.enableSubmit || disableForm}
|
||||||
/>
|
/>
|
||||||
<button onClick={this._onCancelClick} disabled={disableForm}>
|
<button onClick={this._onCancelClick} disabled={disableForm}>
|
||||||
|
@ -74,6 +74,14 @@ export default class CallEventGrouper extends EventEmitter {
|
|||||||
return this.hangup?.getContent()?.reason;
|
return this.hangup?.getContent()?.reason;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get rejectParty(): string {
|
||||||
|
return this.reject?.getSender();
|
||||||
|
}
|
||||||
|
|
||||||
|
public get gotRejected(): boolean {
|
||||||
|
return Boolean(this.reject);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if there are only events from the other side - we missed the call
|
* Returns true if there are only events from the other side - we missed the call
|
||||||
*/
|
*/
|
||||||
|
@ -16,7 +16,7 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { CSSProperties, RefObject, useRef, useState } from "react";
|
import React, { CSSProperties, RefObject, SyntheticEvent, useRef, useState } from "react";
|
||||||
import ReactDOM from "react-dom";
|
import ReactDOM from "react-dom";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
|
|
||||||
@ -80,6 +80,10 @@ export interface IProps extends IPosition {
|
|||||||
managed?: boolean;
|
managed?: boolean;
|
||||||
wrapperClassName?: string;
|
wrapperClassName?: string;
|
||||||
|
|
||||||
|
// If true, this context menu will be mounted as a child to the parent container. Otherwise
|
||||||
|
// it will be mounted to a container at the root of the DOM.
|
||||||
|
mountAsChild?: boolean;
|
||||||
|
|
||||||
// Function to be called on menu close
|
// Function to be called on menu close
|
||||||
onFinished();
|
onFinished();
|
||||||
// on resize callback
|
// on resize callback
|
||||||
@ -390,7 +394,13 @@ export class ContextMenu extends React.PureComponent<IProps, IState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render(): React.ReactChild {
|
render(): React.ReactChild {
|
||||||
return ReactDOM.createPortal(this.renderMenu(), getOrCreateContainer());
|
if (this.props.mountAsChild) {
|
||||||
|
// Render as a child of the current parent
|
||||||
|
return this.renderMenu();
|
||||||
|
} else {
|
||||||
|
// Render as a child of a container at the root of the DOM
|
||||||
|
return ReactDOM.createPortal(this.renderMenu(), getOrCreateContainer());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -461,10 +471,14 @@ type ContextMenuTuple<T> = [boolean, RefObject<T>, () => void, () => void, (val:
|
|||||||
export const useContextMenu = <T extends any = HTMLElement>(): ContextMenuTuple<T> => {
|
export const useContextMenu = <T extends any = HTMLElement>(): ContextMenuTuple<T> => {
|
||||||
const button = useRef<T>(null);
|
const button = useRef<T>(null);
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const open = () => {
|
const open = (ev?: SyntheticEvent) => {
|
||||||
|
ev?.preventDefault();
|
||||||
|
ev?.stopPropagation();
|
||||||
setIsOpen(true);
|
setIsOpen(true);
|
||||||
};
|
};
|
||||||
const close = () => {
|
const close = (ev?: SyntheticEvent) => {
|
||||||
|
ev?.preventDefault();
|
||||||
|
ev?.stopPropagation();
|
||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -120,8 +120,7 @@ export default class EmbeddedPage extends React.PureComponent {
|
|||||||
|
|
||||||
const content = <div className={`${className}_body`}
|
const content = <div className={`${className}_body`}
|
||||||
dangerouslySetInnerHTML={{ __html: this.state.page }}
|
dangerouslySetInnerHTML={{ __html: this.state.page }}
|
||||||
>
|
/>;
|
||||||
</div>;
|
|
||||||
|
|
||||||
if (this.props.scrollbar) {
|
if (this.props.scrollbar) {
|
||||||
return <AutoHideScrollbar className={classes}>
|
return <AutoHideScrollbar className={classes}>
|
||||||
|
@ -222,7 +222,7 @@ class FeaturedRoom extends React.Component {
|
|||||||
|
|
||||||
let roomNameNode = null;
|
let roomNameNode = null;
|
||||||
if (permalink) {
|
if (permalink) {
|
||||||
roomNameNode = <a href={permalink} onClick={this.onClick} >{ roomName }</a>;
|
roomNameNode = <a href={permalink} onClick={this.onClick}>{ roomName }</a>;
|
||||||
} else {
|
} else {
|
||||||
roomNameNode = <span>{ roomName }</span>;
|
roomNameNode = <span>{ roomName }</span>;
|
||||||
}
|
}
|
||||||
@ -1185,10 +1185,13 @@ export default class GroupView extends React.Component {
|
|||||||
avatarImage = <Spinner />;
|
avatarImage = <Spinner />;
|
||||||
} else {
|
} else {
|
||||||
const GroupAvatar = sdk.getComponent('avatars.GroupAvatar');
|
const GroupAvatar = sdk.getComponent('avatars.GroupAvatar');
|
||||||
avatarImage = <GroupAvatar groupId={this.props.groupId}
|
avatarImage = <GroupAvatar
|
||||||
|
groupId={this.props.groupId}
|
||||||
groupName={this.state.profileForm.name}
|
groupName={this.state.profileForm.name}
|
||||||
groupAvatarUrl={this.state.profileForm.avatar_url}
|
groupAvatarUrl={this.state.profileForm.avatar_url}
|
||||||
width={28} height={28} resizeMethod='crop'
|
width={28}
|
||||||
|
height={28}
|
||||||
|
resizeMethod='crop'
|
||||||
/>;
|
/>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1199,9 +1202,12 @@ export default class GroupView extends React.Component {
|
|||||||
</label>
|
</label>
|
||||||
<div className="mx_GroupView_avatarPicker_edit">
|
<div className="mx_GroupView_avatarPicker_edit">
|
||||||
<label htmlFor="avatarInput" className="mx_GroupView_avatarPicker_label">
|
<label htmlFor="avatarInput" className="mx_GroupView_avatarPicker_label">
|
||||||
<img src={require("../../../res/img/camera.svg")}
|
<img
|
||||||
alt={_t("Upload avatar")} title={_t("Upload avatar")}
|
src={require("../../../res/img/camera.svg")}
|
||||||
width="17" height="15" />
|
alt={_t("Upload avatar")}
|
||||||
|
title={_t("Upload avatar")}
|
||||||
|
width="17"
|
||||||
|
height="15" />
|
||||||
</label>
|
</label>
|
||||||
<input id="avatarInput" className="mx_GroupView_uploadInput" type="file" onChange={this._onAvatarSelected} />
|
<input id="avatarInput" className="mx_GroupView_uploadInput" type="file" onChange={this._onAvatarSelected} />
|
||||||
</div>
|
</div>
|
||||||
@ -1238,7 +1244,8 @@ export default class GroupView extends React.Component {
|
|||||||
groupAvatarUrl={groupAvatarUrl}
|
groupAvatarUrl={groupAvatarUrl}
|
||||||
groupName={groupName}
|
groupName={groupName}
|
||||||
onClick={onGroupHeaderItemClick}
|
onClick={onGroupHeaderItemClick}
|
||||||
width={28} height={28}
|
width={28}
|
||||||
|
height={28}
|
||||||
/>;
|
/>;
|
||||||
if (summary.profile && summary.profile.name) {
|
if (summary.profile && summary.profile.name) {
|
||||||
nameNode = <div onClick={onGroupHeaderItemClick}>
|
nameNode = <div onClick={onGroupHeaderItemClick}>
|
||||||
@ -1269,28 +1276,32 @@ export default class GroupView extends React.Component {
|
|||||||
key="_cancelButton"
|
key="_cancelButton"
|
||||||
onClick={this._onCancelClick}
|
onClick={this._onCancelClick}
|
||||||
>
|
>
|
||||||
<img src={require("../../../res/img/cancel.svg")} className="mx_filterFlipColor"
|
<img
|
||||||
width="18" height="18" alt={_t("Cancel")} />
|
src={require("../../../res/img/cancel.svg")}
|
||||||
|
className="mx_filterFlipColor"
|
||||||
|
width="18"
|
||||||
|
height="18"
|
||||||
|
alt={_t("Cancel")} />
|
||||||
</AccessibleButton>,
|
</AccessibleButton>,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
if (summary.user && summary.user.membership === 'join') {
|
if (summary.user && summary.user.membership === 'join') {
|
||||||
rightButtons.push(
|
rightButtons.push(
|
||||||
<AccessibleButton className="mx_GroupHeader_button mx_GroupHeader_editButton"
|
<AccessibleButton
|
||||||
|
className="mx_GroupHeader_button mx_GroupHeader_editButton"
|
||||||
key="_editButton"
|
key="_editButton"
|
||||||
onClick={this._onEditClick}
|
onClick={this._onEditClick}
|
||||||
title={_t("Community Settings")}
|
title={_t("Community Settings")}
|
||||||
>
|
/>,
|
||||||
</AccessibleButton>,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
rightButtons.push(
|
rightButtons.push(
|
||||||
<AccessibleButton className="mx_GroupHeader_button mx_GroupHeader_shareButton"
|
<AccessibleButton
|
||||||
|
className="mx_GroupHeader_button mx_GroupHeader_shareButton"
|
||||||
key="_shareButton"
|
key="_shareButton"
|
||||||
onClick={this._onShareClick}
|
onClick={this._onShareClick}
|
||||||
title={_t('Share Community')}
|
title={_t('Share Community')}
|
||||||
>
|
/>,
|
||||||
</AccessibleButton>,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -618,7 +618,15 @@ export default class MessagePanel extends React.Component<IProps, IState> {
|
|||||||
|
|
||||||
for (const Grouper of groupers) {
|
for (const Grouper of groupers) {
|
||||||
if (Grouper.canStartGroup(this, mxEv)) {
|
if (Grouper.canStartGroup(this, mxEv)) {
|
||||||
grouper = new Grouper(this, mxEv, prevEvent, lastShownEvent, nextEvent, nextTile);
|
grouper = new Grouper(
|
||||||
|
this,
|
||||||
|
mxEv,
|
||||||
|
prevEvent,
|
||||||
|
lastShownEvent,
|
||||||
|
this.props.layout,
|
||||||
|
nextEvent,
|
||||||
|
nextTile,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!grouper) {
|
if (!grouper) {
|
||||||
@ -981,6 +989,7 @@ abstract class BaseGrouper {
|
|||||||
public readonly event: MatrixEvent,
|
public readonly event: MatrixEvent,
|
||||||
public readonly prevEvent: MatrixEvent,
|
public readonly prevEvent: MatrixEvent,
|
||||||
public readonly lastShownEvent: MatrixEvent,
|
public readonly lastShownEvent: MatrixEvent,
|
||||||
|
protected readonly layout: Layout,
|
||||||
public readonly nextEvent?: MatrixEvent,
|
public readonly nextEvent?: MatrixEvent,
|
||||||
public readonly nextEventTile?: MatrixEvent,
|
public readonly nextEventTile?: MatrixEvent,
|
||||||
) {
|
) {
|
||||||
@ -1107,6 +1116,7 @@ class CreationGrouper extends BaseGrouper {
|
|||||||
onToggle={panel.onHeightChanged} // Update scroll state
|
onToggle={panel.onHeightChanged} // Update scroll state
|
||||||
summaryMembers={[ev.sender]}
|
summaryMembers={[ev.sender]}
|
||||||
summaryText={summaryText}
|
summaryText={summaryText}
|
||||||
|
layout={this.layout}
|
||||||
>
|
>
|
||||||
{ eventTiles }
|
{ eventTiles }
|
||||||
</EventListSummary>,
|
</EventListSummary>,
|
||||||
@ -1134,10 +1144,11 @@ class RedactionGrouper extends BaseGrouper {
|
|||||||
ev: MatrixEvent,
|
ev: MatrixEvent,
|
||||||
prevEvent: MatrixEvent,
|
prevEvent: MatrixEvent,
|
||||||
lastShownEvent: MatrixEvent,
|
lastShownEvent: MatrixEvent,
|
||||||
|
layout: Layout,
|
||||||
nextEvent: MatrixEvent,
|
nextEvent: MatrixEvent,
|
||||||
nextEventTile: MatrixEvent,
|
nextEventTile: MatrixEvent,
|
||||||
) {
|
) {
|
||||||
super(panel, ev, prevEvent, lastShownEvent, nextEvent, nextEventTile);
|
super(panel, ev, prevEvent, lastShownEvent, layout, nextEvent, nextEventTile);
|
||||||
this.events = [ev];
|
this.events = [ev];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1202,6 +1213,7 @@ class RedactionGrouper extends BaseGrouper {
|
|||||||
onToggle={panel.onHeightChanged} // Update scroll state
|
onToggle={panel.onHeightChanged} // Update scroll state
|
||||||
summaryMembers={Array.from(senders)}
|
summaryMembers={Array.from(senders)}
|
||||||
summaryText={_t("%(count)s messages deleted.", { count: eventTiles.length })}
|
summaryText={_t("%(count)s messages deleted.", { count: eventTiles.length })}
|
||||||
|
layout={this.layout}
|
||||||
>
|
>
|
||||||
{ eventTiles }
|
{ eventTiles }
|
||||||
</EventListSummary>,
|
</EventListSummary>,
|
||||||
@ -1230,8 +1242,9 @@ class MemberGrouper extends BaseGrouper {
|
|||||||
public readonly event: MatrixEvent,
|
public readonly event: MatrixEvent,
|
||||||
public readonly prevEvent: MatrixEvent,
|
public readonly prevEvent: MatrixEvent,
|
||||||
public readonly lastShownEvent: MatrixEvent,
|
public readonly lastShownEvent: MatrixEvent,
|
||||||
|
protected readonly layout: Layout,
|
||||||
) {
|
) {
|
||||||
super(panel, event, prevEvent, lastShownEvent);
|
super(panel, event, prevEvent, lastShownEvent, layout);
|
||||||
this.events = [event];
|
this.events = [event];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1306,6 +1319,7 @@ class MemberGrouper extends BaseGrouper {
|
|||||||
events={this.events}
|
events={this.events}
|
||||||
onToggle={panel.onHeightChanged} // Update scroll state
|
onToggle={panel.onHeightChanged} // Update scroll state
|
||||||
startExpanded={highlightInMels}
|
startExpanded={highlightInMels}
|
||||||
|
layout={this.layout}
|
||||||
>
|
>
|
||||||
{ eventTiles }
|
{ eventTiles }
|
||||||
</MemberEventListSummary>,
|
</MemberEventListSummary>,
|
||||||
|
@ -109,8 +109,7 @@ export default class MyGroups extends React.Component {
|
|||||||
<SimpleRoomHeader title={_t("Communities")} icon={require("../../../res/img/icons-groups.svg")} />
|
<SimpleRoomHeader title={_t("Communities")} icon={require("../../../res/img/icons-groups.svg")} />
|
||||||
<div className='mx_MyGroups_header'>
|
<div className='mx_MyGroups_header'>
|
||||||
<div className="mx_MyGroups_headerCard">
|
<div className="mx_MyGroups_headerCard">
|
||||||
<AccessibleButton className='mx_MyGroups_headerCard_button' onClick={this._onCreateGroupClick}>
|
<AccessibleButton className='mx_MyGroups_headerCard_button' onClick={this._onCreateGroupClick} />
|
||||||
</AccessibleButton>
|
|
||||||
<div className="mx_MyGroups_headerCard_content">
|
<div className="mx_MyGroups_headerCard_content">
|
||||||
<div className="mx_MyGroups_headerCard_header">
|
<div className="mx_MyGroups_headerCard_header">
|
||||||
{ _t('Create a new community') }
|
{ _t('Create a new community') }
|
||||||
|
@ -266,8 +266,12 @@ export default class RoomStatusBar extends React.PureComponent {
|
|||||||
<div className="mx_RoomStatusBar">
|
<div className="mx_RoomStatusBar">
|
||||||
<div role="alert">
|
<div role="alert">
|
||||||
<div className="mx_RoomStatusBar_connectionLostBar">
|
<div className="mx_RoomStatusBar_connectionLostBar">
|
||||||
<img src={require("../../../res/img/feather-customised/warning-triangle.svg")} width="24"
|
<img
|
||||||
height="24" title="/!\ " alt="/!\ " />
|
src={require("../../../res/img/feather-customised/warning-triangle.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.') }
|
||||||
|
@ -1740,7 +1740,8 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||||||
onJoinClick={this.onJoinButtonClicked}
|
onJoinClick={this.onJoinButtonClicked}
|
||||||
onForgetClick={this.onForgetClick}
|
onForgetClick={this.onForgetClick}
|
||||||
onRejectClick={this.onRejectThreepidInviteButtonClicked}
|
onRejectClick={this.onRejectThreepidInviteButtonClicked}
|
||||||
canPreview={false} error={this.state.roomLoadError}
|
canPreview={false}
|
||||||
|
error={this.state.roomLoadError}
|
||||||
roomAlias={roomAlias}
|
roomAlias={roomAlias}
|
||||||
joining={this.state.joining}
|
joining={this.state.joining}
|
||||||
inviterName={inviterName}
|
inviterName={inviterName}
|
||||||
|
@ -183,8 +183,14 @@ export default class ScrollPanel extends React.Component<IProps> {
|
|||||||
private readonly itemlist = createRef<HTMLOListElement>();
|
private readonly itemlist = createRef<HTMLOListElement>();
|
||||||
private unmounted = false;
|
private unmounted = false;
|
||||||
private scrollTimeout: Timer;
|
private scrollTimeout: Timer;
|
||||||
|
// Are we currently trying to backfill?
|
||||||
private isFilling: boolean;
|
private isFilling: boolean;
|
||||||
|
// Is the current fill request caused by a props update?
|
||||||
|
private isFillingDueToPropsUpdate = false;
|
||||||
|
// Did another request to check the fill state arrive while we were trying to backfill?
|
||||||
private fillRequestWhileRunning: boolean;
|
private fillRequestWhileRunning: boolean;
|
||||||
|
// Is that next fill request scheduled because of a props update?
|
||||||
|
private pendingFillDueToPropsUpdate: boolean;
|
||||||
private scrollState: IScrollState;
|
private scrollState: IScrollState;
|
||||||
private preventShrinkingState: IPreventShrinkingState;
|
private preventShrinkingState: IPreventShrinkingState;
|
||||||
private unfillDebouncer: number;
|
private unfillDebouncer: number;
|
||||||
@ -213,7 +219,7 @@ export default class ScrollPanel extends React.Component<IProps> {
|
|||||||
// adding events to the top).
|
// adding events to the top).
|
||||||
//
|
//
|
||||||
// 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(true);
|
||||||
this.updatePreventShrinking();
|
this.updatePreventShrinking();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,12 +257,12 @@ export default class ScrollPanel extends React.Component<IProps> {
|
|||||||
|
|
||||||
// after an update to the contents of the panel, check that the scroll is
|
// after an update to the contents of the panel, check that the scroll is
|
||||||
// where it ought to be, and set off pagination requests if necessary.
|
// where it ought to be, and set off pagination requests if necessary.
|
||||||
public checkScroll = () => {
|
public checkScroll = (isFromPropsUpdate = false) => {
|
||||||
if (this.unmounted) {
|
if (this.unmounted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.restoreSavedScrollState();
|
this.restoreSavedScrollState();
|
||||||
this.checkFillState();
|
this.checkFillState(0, isFromPropsUpdate);
|
||||||
};
|
};
|
||||||
|
|
||||||
// return true if the content is fully scrolled down right now; else false.
|
// return true if the content is fully scrolled down right now; else false.
|
||||||
@ -319,7 +325,7 @@ export default class ScrollPanel extends React.Component<IProps> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check the scroll state and send out backfill requests if necessary.
|
// check the scroll state and send out backfill requests if necessary.
|
||||||
public checkFillState = async (depth = 0): Promise<void> => {
|
public checkFillState = async (depth = 0, isFromPropsUpdate = false): Promise<void> => {
|
||||||
if (this.unmounted) {
|
if (this.unmounted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -355,14 +361,20 @@ export default class ScrollPanel extends React.Component<IProps> {
|
|||||||
// don't allow more than 1 chain of calls concurrently
|
// don't allow more than 1 chain of calls concurrently
|
||||||
// do make a note when a new request comes in while already running one,
|
// do make a note when a new request comes in while already running one,
|
||||||
// so we can trigger a new chain of calls once done.
|
// so we can trigger a new chain of calls once done.
|
||||||
|
// However, we make an exception for when we're already filling due to a
|
||||||
|
// props (or children) update, because very often the children include
|
||||||
|
// spinners to say whether we're paginating or not, so this would cause
|
||||||
|
// infinite paginating.
|
||||||
if (isFirstCall) {
|
if (isFirstCall) {
|
||||||
if (this.isFilling) {
|
if (this.isFilling && !this.isFillingDueToPropsUpdate) {
|
||||||
debuglog("isFilling: not entering while request is ongoing, marking for a subsequent request");
|
debuglog("isFilling: not entering while request is ongoing, marking for a subsequent request");
|
||||||
this.fillRequestWhileRunning = true;
|
this.fillRequestWhileRunning = true;
|
||||||
|
this.pendingFillDueToPropsUpdate = isFromPropsUpdate;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
debuglog("isFilling: setting");
|
debuglog("isFilling: setting");
|
||||||
this.isFilling = true;
|
this.isFilling = true;
|
||||||
|
this.isFillingDueToPropsUpdate = isFromPropsUpdate;
|
||||||
}
|
}
|
||||||
|
|
||||||
const itemlist = this.itemlist.current;
|
const itemlist = this.itemlist.current;
|
||||||
@ -393,11 +405,14 @@ export default class ScrollPanel extends React.Component<IProps> {
|
|||||||
if (isFirstCall) {
|
if (isFirstCall) {
|
||||||
debuglog("isFilling: clearing");
|
debuglog("isFilling: clearing");
|
||||||
this.isFilling = false;
|
this.isFilling = false;
|
||||||
|
this.isFillingDueToPropsUpdate = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.fillRequestWhileRunning) {
|
if (this.fillRequestWhileRunning) {
|
||||||
|
const refillDueToPropsUpdate = this.pendingFillDueToPropsUpdate;
|
||||||
this.fillRequestWhileRunning = false;
|
this.fillRequestWhileRunning = false;
|
||||||
this.checkFillState();
|
this.pendingFillDueToPropsUpdate = false;
|
||||||
|
this.checkFillState(0, refillDueToPropsUpdate);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -136,8 +136,8 @@ export default class SearchBox extends React.Component {
|
|||||||
key="button"
|
key="button"
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
className="mx_SearchBox_closeButton"
|
className="mx_SearchBox_closeButton"
|
||||||
onClick={() => {this._clearSearch("button"); }}>
|
onClick={() => {this._clearSearch("button"); }}
|
||||||
</AccessibleButton>) : undefined;
|
/>) : undefined;
|
||||||
|
|
||||||
// show a shorter placeholder when blurred, if requested
|
// show a shorter placeholder when blurred, if requested
|
||||||
// this is used for the room filter field that has
|
// this is used for the room filter field that has
|
||||||
|
@ -16,7 +16,6 @@ limitations under the License.
|
|||||||
|
|
||||||
import React, { ReactNode, useMemo, useState } from "react";
|
import React, { ReactNode, useMemo, useState } from "react";
|
||||||
import { Room } from "matrix-js-sdk/src/models/room";
|
import { Room } from "matrix-js-sdk/src/models/room";
|
||||||
import { MatrixClient } from "matrix-js-sdk/src/client";
|
|
||||||
import { EventType, RoomType } from "matrix-js-sdk/src/@types/event";
|
import { EventType, RoomType } from "matrix-js-sdk/src/@types/event";
|
||||||
import { ISpaceSummaryRoom, ISpaceSummaryEvent } from "matrix-js-sdk/src/@types/spaces";
|
import { ISpaceSummaryRoom, ISpaceSummaryEvent } from "matrix-js-sdk/src/@types/spaces";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
@ -44,11 +43,13 @@ import { getChildOrder } from "../../stores/SpaceStore";
|
|||||||
import AccessibleTooltipButton from "../views/elements/AccessibleTooltipButton";
|
import AccessibleTooltipButton from "../views/elements/AccessibleTooltipButton";
|
||||||
import { linkifyElement } from "../../HtmlUtils";
|
import { linkifyElement } from "../../HtmlUtils";
|
||||||
import { getDisplayAliasForAliasSet } from "../../Rooms";
|
import { getDisplayAliasForAliasSet } from "../../Rooms";
|
||||||
|
import { useDispatcher } from "../../hooks/useDispatcher";
|
||||||
|
import defaultDispatcher from "../../dispatcher/dispatcher";
|
||||||
|
import { Action } from "../../dispatcher/actions";
|
||||||
|
|
||||||
interface IHierarchyProps {
|
interface IHierarchyProps {
|
||||||
space: Room;
|
space: Room;
|
||||||
initialText?: string;
|
initialText?: string;
|
||||||
refreshToken?: any;
|
|
||||||
additionalButtons?: ReactNode;
|
additionalButtons?: ReactNode;
|
||||||
showRoom(room: ISpaceSummaryRoom, viaServers?: string[], autoJoin?: boolean): void;
|
showRoom(room: ISpaceSummaryRoom, viaServers?: string[], autoJoin?: boolean): void;
|
||||||
}
|
}
|
||||||
@ -315,18 +316,25 @@ export const HierarchyLevel = ({
|
|||||||
</React.Fragment>;
|
</React.Fragment>;
|
||||||
};
|
};
|
||||||
|
|
||||||
// mutate argument refreshToken to force a reload
|
export const useSpaceSummary = (space: Room): [
|
||||||
export const useSpaceSummary = (cli: MatrixClient, space: Room, refreshToken?: any): [
|
|
||||||
null,
|
null,
|
||||||
ISpaceSummaryRoom[],
|
ISpaceSummaryRoom[],
|
||||||
Map<string, Map<string, ISpaceSummaryEvent>>?,
|
Map<string, Map<string, ISpaceSummaryEvent>>?,
|
||||||
Map<string, Set<string>>?,
|
Map<string, Set<string>>?,
|
||||||
Map<string, Set<string>>?,
|
Map<string, Set<string>>?,
|
||||||
] | [Error] => {
|
] | [Error] => {
|
||||||
|
// crude temporary refresh token approach until we have pagination and rework the data flow here
|
||||||
|
const [refreshToken, setRefreshToken] = useState(0);
|
||||||
|
useDispatcher(defaultDispatcher, (payload => {
|
||||||
|
if (payload.action === Action.UpdateSpaceHierarchy) {
|
||||||
|
setRefreshToken(t => t + 1);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
// TODO pagination
|
// TODO pagination
|
||||||
return useAsyncMemo(async () => {
|
return useAsyncMemo(async () => {
|
||||||
try {
|
try {
|
||||||
const data = await cli.getSpaceSummary(space.roomId);
|
const data = await space.client.getSpaceSummary(space.roomId);
|
||||||
|
|
||||||
const parentChildRelations = new EnhancedMap<string, Map<string, ISpaceSummaryEvent>>();
|
const parentChildRelations = new EnhancedMap<string, Map<string, ISpaceSummaryEvent>>();
|
||||||
const childParentRelations = new EnhancedMap<string, Set<string>>();
|
const childParentRelations = new EnhancedMap<string, Set<string>>();
|
||||||
@ -354,7 +362,6 @@ export const SpaceHierarchy: React.FC<IHierarchyProps> = ({
|
|||||||
space,
|
space,
|
||||||
initialText = "",
|
initialText = "",
|
||||||
showRoom,
|
showRoom,
|
||||||
refreshToken,
|
|
||||||
additionalButtons,
|
additionalButtons,
|
||||||
children,
|
children,
|
||||||
}) => {
|
}) => {
|
||||||
@ -364,7 +371,7 @@ export const SpaceHierarchy: React.FC<IHierarchyProps> = ({
|
|||||||
|
|
||||||
const [selected, setSelected] = useState(new Map<string, Set<string>>()); // Map<parentId, Set<childId>>
|
const [selected, setSelected] = useState(new Map<string, Set<string>>()); // Map<parentId, Set<childId>>
|
||||||
|
|
||||||
const [summaryError, rooms, parentChildMap, viaMap, childParentMap] = useSpaceSummary(cli, space, refreshToken);
|
const [summaryError, rooms, parentChildMap, viaMap, childParentMap] = useSpaceSummary(space);
|
||||||
|
|
||||||
const roomsMap = useMemo(() => {
|
const roomsMap = useMemo(() => {
|
||||||
if (!rooms) return null;
|
if (!rooms) return null;
|
||||||
|
@ -16,7 +16,7 @@ limitations under the License.
|
|||||||
|
|
||||||
import React, { RefObject, useContext, useRef, useState } from "react";
|
import React, { RefObject, useContext, useRef, useState } from "react";
|
||||||
import { EventType } from "matrix-js-sdk/src/@types/event";
|
import { EventType } from "matrix-js-sdk/src/@types/event";
|
||||||
import { Preset } from "matrix-js-sdk/src/@types/partials";
|
import { Preset, JoinRule } from "matrix-js-sdk/src/@types/partials";
|
||||||
import { Room } from "matrix-js-sdk/src/models/room";
|
import { Room } from "matrix-js-sdk/src/models/room";
|
||||||
import { EventSubscription } from "fbemitter";
|
import { EventSubscription } from "fbemitter";
|
||||||
|
|
||||||
@ -47,13 +47,23 @@ import { RightPanelPhases } from "../../stores/RightPanelStorePhases";
|
|||||||
import { SetRightPanelPhasePayload } from "../../dispatcher/payloads/SetRightPanelPhasePayload";
|
import { SetRightPanelPhasePayload } from "../../dispatcher/payloads/SetRightPanelPhasePayload";
|
||||||
import { useStateArray } from "../../hooks/useStateArray";
|
import { useStateArray } from "../../hooks/useStateArray";
|
||||||
import SpacePublicShare from "../views/spaces/SpacePublicShare";
|
import SpacePublicShare from "../views/spaces/SpacePublicShare";
|
||||||
import { shouldShowSpaceSettings, showAddExistingRooms, showCreateNewRoom, showSpaceSettings } from "../../utils/space";
|
import {
|
||||||
|
shouldShowSpaceSettings,
|
||||||
|
showAddExistingRooms,
|
||||||
|
showCreateNewRoom,
|
||||||
|
showCreateNewSubspace,
|
||||||
|
showSpaceSettings,
|
||||||
|
} from "../../utils/space";
|
||||||
import { showRoom, SpaceHierarchy } from "./SpaceRoomDirectory";
|
import { showRoom, SpaceHierarchy } from "./SpaceRoomDirectory";
|
||||||
import MemberAvatar from "../views/avatars/MemberAvatar";
|
import MemberAvatar from "../views/avatars/MemberAvatar";
|
||||||
import { useStateToggle } from "../../hooks/useStateToggle";
|
|
||||||
import SpaceStore from "../../stores/SpaceStore";
|
import SpaceStore from "../../stores/SpaceStore";
|
||||||
import FacePile from "../views/elements/FacePile";
|
import FacePile from "../views/elements/FacePile";
|
||||||
import { AddExistingToSpace } from "../views/dialogs/AddExistingToSpaceDialog";
|
import {
|
||||||
|
AddExistingToSpace,
|
||||||
|
defaultDmsRenderer,
|
||||||
|
defaultRoomsRenderer,
|
||||||
|
defaultSpacesRenderer,
|
||||||
|
} from "../views/dialogs/AddExistingToSpaceDialog";
|
||||||
import { ChevronFace, ContextMenuButton, useContextMenu } from "./ContextMenu";
|
import { ChevronFace, ContextMenuButton, useContextMenu } from "./ContextMenu";
|
||||||
import IconizedContextMenu, {
|
import IconizedContextMenu, {
|
||||||
IconizedContextMenuOption,
|
IconizedContextMenuOption,
|
||||||
@ -62,11 +72,8 @@ import IconizedContextMenu, {
|
|||||||
import AccessibleTooltipButton from "../views/elements/AccessibleTooltipButton";
|
import AccessibleTooltipButton from "../views/elements/AccessibleTooltipButton";
|
||||||
import { BetaPill } from "../views/beta/BetaCard";
|
import { BetaPill } from "../views/beta/BetaCard";
|
||||||
import { UserTab } from "../views/dialogs/UserSettingsDialog";
|
import { UserTab } from "../views/dialogs/UserSettingsDialog";
|
||||||
import Modal from "../../Modal";
|
|
||||||
import BetaFeedbackDialog from "../views/dialogs/BetaFeedbackDialog";
|
|
||||||
import SdkConfig from "../../SdkConfig";
|
|
||||||
import { EffectiveMembership, getEffectiveMembership } from "../../utils/membership";
|
import { EffectiveMembership, getEffectiveMembership } from "../../utils/membership";
|
||||||
import { JoinRule } from "../views/settings/tabs/room/SecurityRoomSettingsTab";
|
import { SpaceFeedbackPrompt } from "../views/spaces/SpaceCreateMenu";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
space: Room;
|
space: Room;
|
||||||
@ -93,26 +100,6 @@ enum Phase {
|
|||||||
PrivateExistingRooms,
|
PrivateExistingRooms,
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX: Temporary for the Spaces Beta only
|
|
||||||
export const SpaceFeedbackPrompt = ({ onClick }: { onClick?: () => void }) => {
|
|
||||||
if (!SdkConfig.get().bug_report_endpoint_url) return null;
|
|
||||||
|
|
||||||
return <div className="mx_SpaceFeedbackPrompt">
|
|
||||||
<hr />
|
|
||||||
<div>
|
|
||||||
<span className="mx_SpaceFeedbackPrompt_text">{ _t("Spaces are a beta feature.") }</span>
|
|
||||||
<AccessibleButton kind="link" onClick={() => {
|
|
||||||
if (onClick) onClick();
|
|
||||||
Modal.createTrackedDialog("Beta Feedback", "feature_spaces", BetaFeedbackDialog, {
|
|
||||||
featureId: "feature_spaces",
|
|
||||||
});
|
|
||||||
}}>
|
|
||||||
{ _t("Feedback") }
|
|
||||||
</AccessibleButton>
|
|
||||||
</div>
|
|
||||||
</div>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const RoomMemberCount = ({ room, children }) => {
|
const RoomMemberCount = ({ room, children }) => {
|
||||||
const members = useRoomMembers(room);
|
const members = useRoomMembers(room);
|
||||||
const count = members.length;
|
const count = members.length;
|
||||||
@ -306,8 +293,7 @@ const SpacePreview = ({ space, onJoinButtonClicked, onRejectButtonClicked }) =>
|
|||||||
</div>;
|
</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SpaceLandingAddButton = ({ space, onNewRoomAdded }) => {
|
const SpaceLandingAddButton = ({ space }) => {
|
||||||
const cli = useContext(MatrixClientContext);
|
|
||||||
const [menuDisplayed, handle, openMenu, closeMenu] = useContextMenu();
|
const [menuDisplayed, handle, openMenu, closeMenu] = useContextMenu();
|
||||||
|
|
||||||
let contextMenu;
|
let contextMenu;
|
||||||
@ -330,25 +316,33 @@ const SpaceLandingAddButton = ({ space, onNewRoomAdded }) => {
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
closeMenu();
|
closeMenu();
|
||||||
|
|
||||||
if (await showCreateNewRoom(cli, space)) {
|
if (await showCreateNewRoom(space)) {
|
||||||
onNewRoomAdded();
|
defaultDispatcher.fire(Action.UpdateSpaceHierarchy);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<IconizedContextMenuOption
|
<IconizedContextMenuOption
|
||||||
label={_t("Add existing room")}
|
label={_t("Add existing room")}
|
||||||
iconClassName="mx_RoomList_iconHash"
|
iconClassName="mx_RoomList_iconHash"
|
||||||
onClick={async (e) => {
|
onClick={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
closeMenu();
|
closeMenu();
|
||||||
|
showAddExistingRooms(space);
|
||||||
const [added] = await showAddExistingRooms(cli, space);
|
|
||||||
if (added) {
|
|
||||||
onNewRoomAdded();
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<IconizedContextMenuOption
|
||||||
|
label={_t("Add space")}
|
||||||
|
iconClassName="mx_RoomList_iconPlus"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
closeMenu();
|
||||||
|
showCreateNewSubspace(space);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<BetaPill />
|
||||||
|
</IconizedContextMenuOption>
|
||||||
</IconizedContextMenuOptionList>
|
</IconizedContextMenuOptionList>
|
||||||
</IconizedContextMenu>;
|
</IconizedContextMenu>;
|
||||||
}
|
}
|
||||||
@ -389,19 +383,17 @@ const SpaceLanding = ({ space }) => {
|
|||||||
|
|
||||||
const canAddRooms = myMembership === "join" && space.currentState.maySendStateEvent(EventType.SpaceChild, userId);
|
const canAddRooms = myMembership === "join" && space.currentState.maySendStateEvent(EventType.SpaceChild, userId);
|
||||||
|
|
||||||
const [refreshToken, forceUpdate] = useStateToggle(false);
|
|
||||||
|
|
||||||
let addRoomButton;
|
let addRoomButton;
|
||||||
if (canAddRooms) {
|
if (canAddRooms) {
|
||||||
addRoomButton = <SpaceLandingAddButton space={space} onNewRoomAdded={forceUpdate} />;
|
addRoomButton = <SpaceLandingAddButton space={space} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
let settingsButton;
|
let settingsButton;
|
||||||
if (shouldShowSpaceSettings(cli, space)) {
|
if (shouldShowSpaceSettings(space)) {
|
||||||
settingsButton = <AccessibleTooltipButton
|
settingsButton = <AccessibleTooltipButton
|
||||||
className="mx_SpaceRoomView_landing_settingsButton"
|
className="mx_SpaceRoomView_landing_settingsButton"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
showSpaceSettings(cli, space);
|
showSpaceSettings(space);
|
||||||
}}
|
}}
|
||||||
title={_t("Settings")}
|
title={_t("Settings")}
|
||||||
/>;
|
/>;
|
||||||
@ -416,6 +408,7 @@ const SpaceLanding = ({ space }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return <div className="mx_SpaceRoomView_landing">
|
return <div className="mx_SpaceRoomView_landing">
|
||||||
|
<SpaceFeedbackPrompt />
|
||||||
<RoomAvatar room={space} height={80} width={80} viewAvatarOnClick={true} />
|
<RoomAvatar room={space} height={80} width={80} viewAvatarOnClick={true} />
|
||||||
<div className="mx_SpaceRoomView_landing_name">
|
<div className="mx_SpaceRoomView_landing_name">
|
||||||
<RoomName room={space}>
|
<RoomName room={space}>
|
||||||
@ -440,15 +433,8 @@ const SpaceLanding = ({ space }) => {
|
|||||||
</div>
|
</div>
|
||||||
) }
|
) }
|
||||||
</RoomTopic>
|
</RoomTopic>
|
||||||
<SpaceFeedbackPrompt />
|
|
||||||
<hr />
|
|
||||||
|
|
||||||
<SpaceHierarchy
|
<SpaceHierarchy space={space} showRoom={showRoom} additionalButtons={addRoomButton} />
|
||||||
space={space}
|
|
||||||
showRoom={showRoom}
|
|
||||||
refreshToken={refreshToken}
|
|
||||||
additionalButtons={addRoomButton}
|
|
||||||
/>
|
|
||||||
</div>;
|
</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -531,7 +517,6 @@ const SpaceSetupFirstRooms = ({ space, title, description, onFinished }) => {
|
|||||||
value={buttonLabel}
|
value={buttonLabel}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<SpaceFeedbackPrompt />
|
|
||||||
</div>;
|
</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -550,13 +535,12 @@ const SpaceAddExistingRooms = ({ space, onFinished }) => {
|
|||||||
{ _t("Skip for now") }
|
{ _t("Skip for now") }
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
}
|
}
|
||||||
|
filterPlaceholder={_t("Search for rooms or spaces")}
|
||||||
onFinished={onFinished}
|
onFinished={onFinished}
|
||||||
|
roomsRenderer={defaultRoomsRenderer}
|
||||||
|
spacesRenderer={defaultSpacesRenderer}
|
||||||
|
dmsRenderer={defaultDmsRenderer}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="mx_SpaceRoomView_buttons">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<SpaceFeedbackPrompt />
|
|
||||||
</div>;
|
</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -576,7 +560,6 @@ const SpaceSetupPublicShare = ({ justCreatedOpts, space, onFinished, createdRoom
|
|||||||
{ createdRooms ? _t("Go to my first room") : _t("Go to my space") }
|
{ createdRooms ? _t("Go to my first room") : _t("Go to my space") }
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
</div>
|
</div>
|
||||||
<SpaceFeedbackPrompt />
|
|
||||||
</div>;
|
</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -605,9 +588,8 @@ const SpaceSetupPrivateScope = ({ space, justCreatedOpts, onFinished }) => {
|
|||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
<div className="mx_SpaceRoomView_betaWarning">
|
<div className="mx_SpaceRoomView_betaWarning">
|
||||||
<h3>{ _t("Teammates might not be able to view or join any private rooms you make.") }</h3>
|
<h3>{ _t("Teammates might not be able to view or join any private rooms you make.") }</h3>
|
||||||
<p>{ _t("We're working on this as part of the beta, but just want to let you know.") }</p>
|
<p>{ _t("We're working on this, but just want to let you know.") }</p>
|
||||||
</div>
|
</div>
|
||||||
<SpaceFeedbackPrompt />
|
|
||||||
</div>;
|
</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -730,7 +712,6 @@ const SpaceSetupPrivateInvite = ({ space, onFinished }) => {
|
|||||||
value={buttonLabel}
|
value={buttonLabel}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<SpaceFeedbackPrompt />
|
|
||||||
</div>;
|
</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -315,7 +315,10 @@ export default class ForgotPassword extends React.Component<IProps, IState> {
|
|||||||
{ _t("An email has been sent to %(emailAddress)s. Once you've followed the " +
|
{ _t("An email has been sent to %(emailAddress)s. Once you've followed the " +
|
||||||
"link it contains, click below.", { emailAddress: this.state.email }) }
|
"link it contains, click below.", { emailAddress: this.state.email }) }
|
||||||
<br />
|
<br />
|
||||||
<input className="mx_Login_submit" type="button" onClick={this.onVerify}
|
<input
|
||||||
|
className="mx_Login_submit"
|
||||||
|
type="button"
|
||||||
|
onClick={this.onVerify}
|
||||||
value={_t('I have verified my email address')} />
|
value={_t('I have verified my email address')} />
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
@ -328,7 +331,10 @@ export default class ForgotPassword extends React.Component<IProps, IState> {
|
|||||||
"push notifications. To re-enable notifications, sign in again on each " +
|
"push notifications. To re-enable notifications, sign in again on each " +
|
||||||
"device.",
|
"device.",
|
||||||
) }</p>
|
) }</p>
|
||||||
<input className="mx_Login_submit" type="button" onClick={this.props.onComplete}
|
<input
|
||||||
|
className="mx_Login_submit"
|
||||||
|
type="button"
|
||||||
|
onClick={this.props.onComplete}
|
||||||
value={_t('Return to login screen')} />
|
value={_t('Return to login screen')} />
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
@ -463,7 +463,9 @@ export default class LoginComponent extends React.PureComponent<IProps, IState>
|
|||||||
"Either use HTTPS or <a>enable unsafe scripts</a>.", {},
|
"Either use HTTPS or <a>enable unsafe scripts</a>.", {},
|
||||||
{
|
{
|
||||||
'a': (sub) => {
|
'a': (sub) => {
|
||||||
return <a target="_blank" rel="noreferrer noopener"
|
return <a
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer noopener"
|
||||||
href="https://www.google.com/search?&q=enable%20unsafe%20scripts"
|
href="https://www.google.com/search?&q=enable%20unsafe%20scripts"
|
||||||
>
|
>
|
||||||
{ sub }
|
{ sub }
|
||||||
|
@ -557,12 +557,16 @@ export default class Registration extends React.Component<IProps, IState> {
|
|||||||
loggedInUserId: this.state.differentLoggedInUserId,
|
loggedInUserId: this.state.differentLoggedInUserId,
|
||||||
},
|
},
|
||||||
) }</p>
|
) }</p>
|
||||||
<p><AccessibleButton element="span" className="mx_linkButton" onClick={async event => {
|
<p><AccessibleButton
|
||||||
const sessionLoaded = await this.onLoginClickWithCheck(event);
|
element="span"
|
||||||
if (sessionLoaded) {
|
className="mx_linkButton"
|
||||||
dis.dispatch({ action: "view_welcome_page" });
|
onClick={async event => {
|
||||||
}
|
const sessionLoaded = await this.onLoginClickWithCheck(event);
|
||||||
}}>
|
if (sessionLoaded) {
|
||||||
|
dis.dispatch({ action: "view_welcome_page" });
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
{ _t("Continue with previous account") }
|
{ _t("Continue with previous account") }
|
||||||
</AccessibleButton></p>
|
</AccessibleButton></p>
|
||||||
</div>;
|
</div>;
|
||||||
|
@ -14,9 +14,7 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Playback, PlaybackState } from "../../../voice/Playback";
|
|
||||||
import React, { createRef, ReactNode, RefObject } from "react";
|
import React, { createRef, ReactNode, RefObject } from "react";
|
||||||
import { UPDATE_EVENT } from "../../../stores/AsyncStore";
|
|
||||||
import PlayPauseButton from "./PlayPauseButton";
|
import PlayPauseButton from "./PlayPauseButton";
|
||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
import { formatBytes } from "../../../utils/FormattingUtils";
|
import { formatBytes } from "../../../utils/FormattingUtils";
|
||||||
@ -25,47 +23,13 @@ import { Key } from "../../../Keyboard";
|
|||||||
import { _t } from "../../../languageHandler";
|
import { _t } from "../../../languageHandler";
|
||||||
import SeekBar from "./SeekBar";
|
import SeekBar from "./SeekBar";
|
||||||
import PlaybackClock from "./PlaybackClock";
|
import PlaybackClock from "./PlaybackClock";
|
||||||
|
import AudioPlayerBase from "./AudioPlayerBase";
|
||||||
interface IProps {
|
|
||||||
// Playback instance to render. Cannot change during component lifecycle: create
|
|
||||||
// an all-new component instead.
|
|
||||||
playback: Playback;
|
|
||||||
|
|
||||||
mediaName: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IState {
|
|
||||||
playbackPhase: PlaybackState;
|
|
||||||
error?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
@replaceableComponent("views.audio_messages.AudioPlayer")
|
@replaceableComponent("views.audio_messages.AudioPlayer")
|
||||||
export default class AudioPlayer extends React.PureComponent<IProps, IState> {
|
export default class AudioPlayer extends AudioPlayerBase {
|
||||||
private playPauseRef: RefObject<PlayPauseButton> = createRef();
|
private playPauseRef: RefObject<PlayPauseButton> = createRef();
|
||||||
private seekRef: RefObject<SeekBar> = createRef();
|
private seekRef: RefObject<SeekBar> = createRef();
|
||||||
|
|
||||||
constructor(props: IProps) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
playbackPhase: PlaybackState.Decoding, // default assumption
|
|
||||||
};
|
|
||||||
|
|
||||||
// We don't need to de-register: the class handles this for us internally
|
|
||||||
this.props.playback.on(UPDATE_EVENT, this.onPlaybackUpdate);
|
|
||||||
|
|
||||||
// Don't wait for the promise to complete - it will emit a progress update when it
|
|
||||||
// is done, and it's not meant to take long anyhow.
|
|
||||||
this.props.playback.prepare().catch(e => {
|
|
||||||
console.error("Error processing audio file:", e);
|
|
||||||
this.setState({ error: true });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private onPlaybackUpdate = (ev: PlaybackState) => {
|
|
||||||
this.setState({ playbackPhase: ev });
|
|
||||||
};
|
|
||||||
|
|
||||||
private onKeyDown = (ev: React.KeyboardEvent) => {
|
private onKeyDown = (ev: React.KeyboardEvent) => {
|
||||||
// stopPropagation() prevents the FocusComposer catch-all from triggering,
|
// stopPropagation() prevents the FocusComposer catch-all from triggering,
|
||||||
// but we need to do it on key down instead of press (even though the user
|
// but we need to do it on key down instead of press (even though the user
|
||||||
@ -91,10 +55,10 @@ export default class AudioPlayer extends React.PureComponent<IProps, IState> {
|
|||||||
return `(${formatBytes(bytes)})`;
|
return `(${formatBytes(bytes)})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
public render(): ReactNode {
|
protected renderComponent(): ReactNode {
|
||||||
// tabIndex=0 to ensure that the whole component becomes a tab stop, where we handle keyboard
|
// tabIndex=0 to ensure that the whole component becomes a tab stop, where we handle keyboard
|
||||||
// events for accessibility
|
// events for accessibility
|
||||||
return <>
|
return (
|
||||||
<div className='mx_MediaBody mx_AudioPlayer_container' tabIndex={0} onKeyDown={this.onKeyDown}>
|
<div className='mx_MediaBody mx_AudioPlayer_container' tabIndex={0} onKeyDown={this.onKeyDown}>
|
||||||
<div className='mx_AudioPlayer_primaryContainer'>
|
<div className='mx_AudioPlayer_primaryContainer'>
|
||||||
<PlayPauseButton
|
<PlayPauseButton
|
||||||
@ -124,7 +88,6 @@ export default class AudioPlayer extends React.PureComponent<IProps, IState> {
|
|||||||
<PlaybackClock playback={this.props.playback} defaultDisplaySeconds={0} />
|
<PlaybackClock playback={this.props.playback} defaultDisplaySeconds={0} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{ this.state.error && <div className="text-warning">{ _t("Error downloading audio") }</div> }
|
);
|
||||||
</>;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
70
src/components/views/audio_messages/AudioPlayerBase.tsx
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Playback, PlaybackState } from "../../../audio/Playback";
|
||||||
|
import { TileShape } from "../rooms/EventTile";
|
||||||
|
import React, { ReactNode } from "react";
|
||||||
|
import { UPDATE_EVENT } from "../../../stores/AsyncStore";
|
||||||
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
|
import { _t } from "../../../languageHandler";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
// Playback instance to render. Cannot change during component lifecycle: create
|
||||||
|
// an all-new component instead.
|
||||||
|
playback: Playback;
|
||||||
|
|
||||||
|
mediaName?: string;
|
||||||
|
tileShape?: TileShape;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IState {
|
||||||
|
playbackPhase: PlaybackState;
|
||||||
|
error?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
@replaceableComponent("views.audio_messages.AudioPlayerBase")
|
||||||
|
export default abstract class AudioPlayerBase extends React.PureComponent<IProps, IState> {
|
||||||
|
constructor(props: IProps) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
playbackPhase: PlaybackState.Decoding, // default assumption
|
||||||
|
};
|
||||||
|
|
||||||
|
// We don't need to de-register: the class handles this for us internally
|
||||||
|
this.props.playback.on(UPDATE_EVENT, this.onPlaybackUpdate);
|
||||||
|
|
||||||
|
// Don't wait for the promise to complete - it will emit a progress update when it
|
||||||
|
// is done, and it's not meant to take long anyhow.
|
||||||
|
this.props.playback.prepare().catch(e => {
|
||||||
|
console.error("Error processing audio file:", e);
|
||||||
|
this.setState({ error: true });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private onPlaybackUpdate = (ev: PlaybackState) => {
|
||||||
|
this.setState({ playbackPhase: ev });
|
||||||
|
};
|
||||||
|
|
||||||
|
protected abstract renderComponent(): ReactNode;
|
||||||
|
|
||||||
|
public render(): ReactNode {
|
||||||
|
return <>
|
||||||
|
{ this.renderComponent() }
|
||||||
|
{ this.state.error && <div className="text-warning">{ _t("Error downloading audio") }</div> }
|
||||||
|
</>;
|
||||||
|
}
|
||||||
|
}
|
@ -17,7 +17,7 @@ limitations under the License.
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
import Clock from "./Clock";
|
import Clock from "./Clock";
|
||||||
import { Playback } from "../../../voice/Playback";
|
import { Playback } from "../../../audio/Playback";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
playback: Playback;
|
playback: Playback;
|
||||||
|
@ -15,7 +15,7 @@ limitations under the License.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { IRecordingUpdate, VoiceRecording } from "../../../voice/VoiceRecording";
|
import { IRecordingUpdate, VoiceRecording } from "../../../audio/VoiceRecording";
|
||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
import Clock from "./Clock";
|
import Clock from "./Clock";
|
||||||
import { MarkedExecution } from "../../../utils/MarkedExecution";
|
import { MarkedExecution } from "../../../utils/MarkedExecution";
|
||||||
|
@ -15,7 +15,7 @@ limitations under the License.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { IRecordingUpdate, RECORDING_PLAYBACK_SAMPLES, VoiceRecording } from "../../../voice/VoiceRecording";
|
import { IRecordingUpdate, RECORDING_PLAYBACK_SAMPLES, VoiceRecording } from "../../../audio/VoiceRecording";
|
||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
import { arrayFastResample } from "../../../utils/arrays";
|
import { arrayFastResample } from "../../../utils/arrays";
|
||||||
import { percentageOf } from "../../../utils/numbers";
|
import { percentageOf } from "../../../utils/numbers";
|
||||||
|
@ -18,7 +18,7 @@ import React, { ReactNode } from "react";
|
|||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
||||||
import { _t } from "../../../languageHandler";
|
import { _t } from "../../../languageHandler";
|
||||||
import { Playback, PlaybackState } from "../../../voice/Playback";
|
import { Playback, PlaybackState } from "../../../audio/Playback";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
|
|
||||||
// omitted props are handled by render function
|
// omitted props are handled by render function
|
||||||
|
@ -17,7 +17,7 @@ limitations under the License.
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
import Clock from "./Clock";
|
import Clock from "./Clock";
|
||||||
import { Playback, PlaybackState } from "../../../voice/Playback";
|
import { Playback, PlaybackState } from "../../../audio/Playback";
|
||||||
import { UPDATE_EVENT } from "../../../stores/AsyncStore";
|
import { UPDATE_EVENT } from "../../../stores/AsyncStore";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
|
@ -18,7 +18,7 @@ import React from "react";
|
|||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
import { arraySeed, arrayTrimFill } from "../../../utils/arrays";
|
import { arraySeed, arrayTrimFill } from "../../../utils/arrays";
|
||||||
import Waveform from "./Waveform";
|
import Waveform from "./Waveform";
|
||||||
import { Playback, PLAYBACK_WAVEFORM_SAMPLES } from "../../../voice/Playback";
|
import { Playback, PLAYBACK_WAVEFORM_SAMPLES } from "../../../audio/Playback";
|
||||||
import { percentageOf } from "../../../utils/numbers";
|
import { percentageOf } from "../../../utils/numbers";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
|
@ -14,68 +14,30 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Playback, PlaybackState } from "../../../voice/Playback";
|
|
||||||
import React, { ReactNode } from "react";
|
import React, { ReactNode } from "react";
|
||||||
import { UPDATE_EVENT } from "../../../stores/AsyncStore";
|
|
||||||
import PlayPauseButton from "./PlayPauseButton";
|
import PlayPauseButton from "./PlayPauseButton";
|
||||||
import PlaybackClock from "./PlaybackClock";
|
import PlaybackClock from "./PlaybackClock";
|
||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
import { TileShape } from "../rooms/EventTile";
|
import { TileShape } from "../rooms/EventTile";
|
||||||
import PlaybackWaveform from "./PlaybackWaveform";
|
import PlaybackWaveform from "./PlaybackWaveform";
|
||||||
import { _t } from "../../../languageHandler";
|
import AudioPlayerBase from "./AudioPlayerBase";
|
||||||
|
|
||||||
interface IProps {
|
|
||||||
// Playback instance to render. Cannot change during component lifecycle: create
|
|
||||||
// an all-new component instead.
|
|
||||||
playback: Playback;
|
|
||||||
|
|
||||||
tileShape?: TileShape;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IState {
|
|
||||||
playbackPhase: PlaybackState;
|
|
||||||
error?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
@replaceableComponent("views.audio_messages.RecordingPlayback")
|
@replaceableComponent("views.audio_messages.RecordingPlayback")
|
||||||
export default class RecordingPlayback extends React.PureComponent<IProps, IState> {
|
export default class RecordingPlayback extends AudioPlayerBase {
|
||||||
constructor(props: IProps) {
|
|
||||||
super(props);
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
playbackPhase: PlaybackState.Decoding, // default assumption
|
|
||||||
};
|
|
||||||
|
|
||||||
// We don't need to de-register: the class handles this for us internally
|
|
||||||
this.props.playback.on(UPDATE_EVENT, this.onPlaybackUpdate);
|
|
||||||
|
|
||||||
// Don't wait for the promise to complete - it will emit a progress update when it
|
|
||||||
// is done, and it's not meant to take long anyhow.
|
|
||||||
this.props.playback.prepare().catch(e => {
|
|
||||||
console.error("Error processing audio file:", e);
|
|
||||||
this.setState({ error: true });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private get isWaveformable(): boolean {
|
private get isWaveformable(): boolean {
|
||||||
return this.props.tileShape !== TileShape.Notif
|
return this.props.tileShape !== TileShape.Notif
|
||||||
&& this.props.tileShape !== TileShape.FileGrid
|
&& this.props.tileShape !== TileShape.FileGrid
|
||||||
&& this.props.tileShape !== TileShape.Pinned;
|
&& this.props.tileShape !== TileShape.Pinned;
|
||||||
}
|
}
|
||||||
|
|
||||||
private onPlaybackUpdate = (ev: PlaybackState) => {
|
protected renderComponent(): ReactNode {
|
||||||
this.setState({ playbackPhase: ev });
|
|
||||||
};
|
|
||||||
|
|
||||||
public render(): ReactNode {
|
|
||||||
const shapeClass = !this.isWaveformable ? 'mx_VoiceMessagePrimaryContainer_noWaveform' : '';
|
const shapeClass = !this.isWaveformable ? 'mx_VoiceMessagePrimaryContainer_noWaveform' : '';
|
||||||
return <>
|
return (
|
||||||
<div className={'mx_MediaBody mx_VoiceMessagePrimaryContainer ' + shapeClass}>
|
<div className={'mx_MediaBody mx_VoiceMessagePrimaryContainer ' + shapeClass}>
|
||||||
<PlayPauseButton playback={this.props.playback} playbackPhase={this.state.playbackPhase} />
|
<PlayPauseButton playback={this.props.playback} playbackPhase={this.state.playbackPhase} />
|
||||||
<PlaybackClock playback={this.props.playback} />
|
<PlaybackClock playback={this.props.playback} />
|
||||||
{ this.isWaveformable && <PlaybackWaveform playback={this.props.playback} /> }
|
{ this.isWaveformable && <PlaybackWaveform playback={this.props.playback} /> }
|
||||||
</div>
|
</div>
|
||||||
{ this.state.error && <div className="text-warning">{ _t("Error downloading audio") }</div> }
|
);
|
||||||
</>;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Playback, PlaybackState } from "../../../voice/Playback";
|
import { Playback, PlaybackState } from "../../../audio/Playback";
|
||||||
import React, { ChangeEvent, CSSProperties, ReactNode } from "react";
|
import React, { ChangeEvent, CSSProperties, ReactNode } from "react";
|
||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
import { MarkedExecution } from "../../../utils/MarkedExecution";
|
import { MarkedExecution } from "../../../utils/MarkedExecution";
|
||||||
|
@ -54,9 +54,13 @@ export default class Waveform extends React.PureComponent<IProps, IState> {
|
|||||||
'mx_Waveform_bar': true,
|
'mx_Waveform_bar': true,
|
||||||
'mx_Waveform_bar_100pct': isCompleteBar,
|
'mx_Waveform_bar_100pct': isCompleteBar,
|
||||||
});
|
});
|
||||||
return <span key={i} style={{
|
return <span
|
||||||
"--barHeight": h,
|
key={i}
|
||||||
} as WaveformCSSProperties} className={classes} />;
|
style={{
|
||||||
|
"--barHeight": h,
|
||||||
|
} as WaveformCSSProperties}
|
||||||
|
className={classes}
|
||||||
|
/>;
|
||||||
}) }
|
}) }
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
@ -103,8 +103,8 @@ export default class CaptchaForm extends React.Component<ICaptchaFormProps, ICap
|
|||||||
}
|
}
|
||||||
|
|
||||||
private resetRecaptcha() {
|
private resetRecaptcha() {
|
||||||
if (this.captchaWidgetId !== null) {
|
if (this.captchaWidgetId) {
|
||||||
global.grecaptcha.reset(this.captchaWidgetId);
|
global?.grecaptcha?.reset(this.captchaWidgetId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -416,8 +416,10 @@ export class TermsAuthEntry extends React.Component<ITermsAuthEntryProps, ITerms
|
|||||||
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_GeneralButton"
|
submitButton = <button
|
||||||
onClick={this.trySubmit} disabled={!allChecked}>{ _t("Accept") }</button>;
|
className="mx_InteractiveAuthEntryComponents_termsSubmit mx_GeneralButton"
|
||||||
|
onClick={this.trySubmit}
|
||||||
|
disabled={!allChecked}>{ _t("Accept") }</button>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -616,7 +618,9 @@ export class MsisdnAuthEntry extends React.Component<IMsisdnAuthEntryProps, IMsi
|
|||||||
aria-label={_t("Code")}
|
aria-label={_t("Code")}
|
||||||
/>
|
/>
|
||||||
<br />
|
<br />
|
||||||
<input type="submit" value={_t("Submit")}
|
<input
|
||||||
|
type="submit"
|
||||||
|
value={_t("Submit")}
|
||||||
className={submitClasses}
|
className={submitClasses}
|
||||||
disabled={!enableSubmit}
|
disabled={!enableSubmit}
|
||||||
/>
|
/>
|
||||||
|
@ -187,7 +187,8 @@ const BaseAvatar = (props: IProps) => {
|
|||||||
width: toPx(width),
|
width: toPx(width),
|
||||||
height: toPx(height),
|
height: toPx(height),
|
||||||
}}
|
}}
|
||||||
title={title} alt={_t("Avatar")}
|
title={title}
|
||||||
|
alt={_t("Avatar")}
|
||||||
inputRef={inputRef}
|
inputRef={inputRef}
|
||||||
{...otherProps} />
|
{...otherProps} />
|
||||||
);
|
);
|
||||||
@ -201,7 +202,8 @@ const BaseAvatar = (props: IProps) => {
|
|||||||
width: toPx(width),
|
width: toPx(width),
|
||||||
height: toPx(height),
|
height: toPx(height),
|
||||||
}}
|
}}
|
||||||
title={title} alt=""
|
title={title}
|
||||||
|
alt=""
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
{...otherProps} />
|
{...otherProps} />
|
||||||
);
|
);
|
||||||
|
@ -102,8 +102,12 @@ export default class MemberAvatar extends React.Component<IProps, IState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseAvatar {...otherProps} name={this.state.name} title={this.state.title}
|
<BaseAvatar {...otherProps}
|
||||||
idName={userId} url={this.state.imageUrl} onClick={onClick} />
|
name={this.state.name}
|
||||||
|
title={this.state.title}
|
||||||
|
idName={userId}
|
||||||
|
url={this.state.imageUrl}
|
||||||
|
onClick={onClick} />
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,9 +13,11 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { ComponentProps } from 'react';
|
import React, { ComponentProps } from 'react';
|
||||||
import { Room } from 'matrix-js-sdk/src/models/room';
|
import { Room } from 'matrix-js-sdk/src/models/room';
|
||||||
import { ResizeMethod } from 'matrix-js-sdk/src/@types/partials';
|
import { ResizeMethod } from 'matrix-js-sdk/src/@types/partials';
|
||||||
|
import classNames from "classnames";
|
||||||
|
|
||||||
import BaseAvatar from './BaseAvatar';
|
import BaseAvatar from './BaseAvatar';
|
||||||
import ImageView from '../elements/ImageView';
|
import ImageView from '../elements/ImageView';
|
||||||
@ -32,11 +34,14 @@ interface IProps extends Omit<ComponentProps<typeof BaseAvatar>, "name" | "idNam
|
|||||||
// oobData.avatarUrl should be set (else there
|
// oobData.avatarUrl should be set (else there
|
||||||
// would be nowhere to get the avatar from)
|
// would be nowhere to get the avatar from)
|
||||||
room?: Room;
|
room?: Room;
|
||||||
oobData?: IOOBData;
|
oobData?: IOOBData & {
|
||||||
|
roomId?: string;
|
||||||
|
};
|
||||||
width?: number;
|
width?: number;
|
||||||
height?: number;
|
height?: number;
|
||||||
resizeMethod?: ResizeMethod;
|
resizeMethod?: ResizeMethod;
|
||||||
viewAvatarOnClick?: boolean;
|
viewAvatarOnClick?: boolean;
|
||||||
|
className?: string;
|
||||||
onClick?(): void;
|
onClick?(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,15 +134,19 @@ export default class RoomAvatar extends React.Component<IProps, IState> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const { room, oobData, viewAvatarOnClick, onClick, ...otherProps } = this.props;
|
const { room, oobData, viewAvatarOnClick, onClick, className, ...otherProps } = this.props;
|
||||||
|
|
||||||
const roomName = room ? room.name : oobData.name;
|
const roomName = room ? room.name : oobData.name;
|
||||||
// If the room is a DM, we use the other user's ID for the color hash
|
// If the room is a DM, we use the other user's ID for the color hash
|
||||||
// in order to match the room avatar with their avatar
|
// in order to match the room avatar with their avatar
|
||||||
const idName = room ? (DMRoomMap.shared().getUserIdForRoomId(room.roomId) ?? room.roomId) : null;
|
const idName = room ? (DMRoomMap.shared().getUserIdForRoomId(room.roomId) ?? room.roomId) : oobData.roomId;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseAvatar {...otherProps}
|
<BaseAvatar
|
||||||
|
{...otherProps}
|
||||||
|
className={classNames(className, {
|
||||||
|
mx_RoomAvatar_isSpaceRoom: room?.isSpaceRoom(),
|
||||||
|
})}
|
||||||
name={roomName}
|
name={roomName}
|
||||||
idName={idName}
|
idName={idName}
|
||||||
urls={this.state.urls}
|
urls={this.state.urls}
|
||||||
|
@ -27,6 +27,8 @@ import BetaFeedbackDialog from "../dialogs/BetaFeedbackDialog";
|
|||||||
import SdkConfig from "../../../SdkConfig";
|
import SdkConfig from "../../../SdkConfig";
|
||||||
import SettingsFlag from "../elements/SettingsFlag";
|
import SettingsFlag from "../elements/SettingsFlag";
|
||||||
|
|
||||||
|
// XXX: Keep this around for re-use in future Betas
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
title?: string;
|
title?: string;
|
||||||
featureId: string;
|
featureId: string;
|
||||||
|