Merge remote-tracking branch 'upstream/develop' into burn-sdk-get-comp-with-fire
Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com>
This commit is contained in:
commit
f91b35a0a0
29 changed files with 599 additions and 342 deletions
171
CHANGELOG.md
171
CHANGELOG.md
|
@ -1,3 +1,174 @@
|
||||||
|
Changes in [3.25.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.25.0) (2021-07-05)
|
||||||
|
=====================================================================================================
|
||||||
|
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.25.0-rc.1...v3.25.0)
|
||||||
|
|
||||||
|
* Remove reminescent references to the tinter
|
||||||
|
[\#6316](https://github.com/matrix-org/matrix-react-sdk/pull/6316)
|
||||||
|
* Update to released version of js-sdk
|
||||||
|
|
||||||
|
Changes in [3.25.0-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.25.0-rc.1) (2021-06-29)
|
||||||
|
===============================================================================================================
|
||||||
|
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.24.0...v3.25.0-rc.1)
|
||||||
|
|
||||||
|
* Update to js-sdk v12.0.1-rc.1
|
||||||
|
* Translations update from Weblate
|
||||||
|
[\#6286](https://github.com/matrix-org/matrix-react-sdk/pull/6286)
|
||||||
|
* Fix back button on user info card after clicking a permalink
|
||||||
|
[\#6277](https://github.com/matrix-org/matrix-react-sdk/pull/6277)
|
||||||
|
* Group ACLs with MELS
|
||||||
|
[\#6280](https://github.com/matrix-org/matrix-react-sdk/pull/6280)
|
||||||
|
* Fix editState not getting passed through
|
||||||
|
[\#6282](https://github.com/matrix-org/matrix-react-sdk/pull/6282)
|
||||||
|
* Migrate message context menu to IconizedContextMenu
|
||||||
|
[\#5671](https://github.com/matrix-org/matrix-react-sdk/pull/5671)
|
||||||
|
* Improve audio recording performance
|
||||||
|
[\#6240](https://github.com/matrix-org/matrix-react-sdk/pull/6240)
|
||||||
|
* Fix multiple timeline panels handling composer and edit events
|
||||||
|
[\#6278](https://github.com/matrix-org/matrix-react-sdk/pull/6278)
|
||||||
|
* Let m.notice messages mark a room as unread
|
||||||
|
[\#6281](https://github.com/matrix-org/matrix-react-sdk/pull/6281)
|
||||||
|
* Removes the override on the Bubble Container
|
||||||
|
[\#5953](https://github.com/matrix-org/matrix-react-sdk/pull/5953)
|
||||||
|
* Fix IRC layout regressions
|
||||||
|
[\#6193](https://github.com/matrix-org/matrix-react-sdk/pull/6193)
|
||||||
|
* Fix trashcan.svg by exporting it with its viewbox
|
||||||
|
[\#6248](https://github.com/matrix-org/matrix-react-sdk/pull/6248)
|
||||||
|
* Fix tiny scrollbar dot on chrome/electron in Forward Dialog
|
||||||
|
[\#6276](https://github.com/matrix-org/matrix-react-sdk/pull/6276)
|
||||||
|
* Upgrade puppeteer to use newer version of Chrome
|
||||||
|
[\#6268](https://github.com/matrix-org/matrix-react-sdk/pull/6268)
|
||||||
|
* Make toast dismiss button less prominent
|
||||||
|
[\#6275](https://github.com/matrix-org/matrix-react-sdk/pull/6275)
|
||||||
|
* Encrypt the voice message file if needed
|
||||||
|
[\#6269](https://github.com/matrix-org/matrix-react-sdk/pull/6269)
|
||||||
|
* Fix hyper-precise presence
|
||||||
|
[\#6270](https://github.com/matrix-org/matrix-react-sdk/pull/6270)
|
||||||
|
* Fix issues around private spaces, including previewable
|
||||||
|
[\#6265](https://github.com/matrix-org/matrix-react-sdk/pull/6265)
|
||||||
|
* Make _pinned messages_ in `m.room.pinned_events` event clickable
|
||||||
|
[\#6257](https://github.com/matrix-org/matrix-react-sdk/pull/6257)
|
||||||
|
* Fix space avatar management layout being broken
|
||||||
|
[\#6266](https://github.com/matrix-org/matrix-react-sdk/pull/6266)
|
||||||
|
* Convert EntityTile, MemberTile and PresenceLabel to TS
|
||||||
|
[\#6251](https://github.com/matrix-org/matrix-react-sdk/pull/6251)
|
||||||
|
* Fix UserInfo not working when rendered without a room
|
||||||
|
[\#6260](https://github.com/matrix-org/matrix-react-sdk/pull/6260)
|
||||||
|
* Update membership reason handling, including leave reason displaying
|
||||||
|
[\#6253](https://github.com/matrix-org/matrix-react-sdk/pull/6253)
|
||||||
|
* Consolidate types with js-sdk changes
|
||||||
|
[\#6220](https://github.com/matrix-org/matrix-react-sdk/pull/6220)
|
||||||
|
* Fix edit history modal
|
||||||
|
[\#6258](https://github.com/matrix-org/matrix-react-sdk/pull/6258)
|
||||||
|
* Convert MemberList to TS
|
||||||
|
[\#6249](https://github.com/matrix-org/matrix-react-sdk/pull/6249)
|
||||||
|
* Fix two PRs duplicating the css attribute
|
||||||
|
[\#6259](https://github.com/matrix-org/matrix-react-sdk/pull/6259)
|
||||||
|
* Improve invite error messages in InviteDialog for room invites
|
||||||
|
[\#6201](https://github.com/matrix-org/matrix-react-sdk/pull/6201)
|
||||||
|
* Fix invite dialog being cut off when it has limited results
|
||||||
|
[\#6256](https://github.com/matrix-org/matrix-react-sdk/pull/6256)
|
||||||
|
* Fix pinning event in a room which hasn't had events pinned in before
|
||||||
|
[\#6255](https://github.com/matrix-org/matrix-react-sdk/pull/6255)
|
||||||
|
* Allow modal widget buttons to be disabled when the modal opens
|
||||||
|
[\#6178](https://github.com/matrix-org/matrix-react-sdk/pull/6178)
|
||||||
|
* Decrease e2e shield fill mask size so that it doesn't overlap
|
||||||
|
[\#6250](https://github.com/matrix-org/matrix-react-sdk/pull/6250)
|
||||||
|
* Dial Pad UI bug fixes
|
||||||
|
[\#5786](https://github.com/matrix-org/matrix-react-sdk/pull/5786)
|
||||||
|
* Simple handling of mid-call output changes
|
||||||
|
[\#6247](https://github.com/matrix-org/matrix-react-sdk/pull/6247)
|
||||||
|
* Improve ForwardDialog performance by using TruncatedList
|
||||||
|
[\#6228](https://github.com/matrix-org/matrix-react-sdk/pull/6228)
|
||||||
|
* Fix dependency and lockfile mismatch
|
||||||
|
[\#6246](https://github.com/matrix-org/matrix-react-sdk/pull/6246)
|
||||||
|
* Improve room directory click behaviour
|
||||||
|
[\#6234](https://github.com/matrix-org/matrix-react-sdk/pull/6234)
|
||||||
|
* Fix keyboard accessibility of the space panel
|
||||||
|
[\#6239](https://github.com/matrix-org/matrix-react-sdk/pull/6239)
|
||||||
|
* Add ways to manage addresses for Spaces
|
||||||
|
[\#6151](https://github.com/matrix-org/matrix-react-sdk/pull/6151)
|
||||||
|
* Hide communities invites and the community autocompleter when Spaces on
|
||||||
|
[\#6244](https://github.com/matrix-org/matrix-react-sdk/pull/6244)
|
||||||
|
* Convert bunch of files to TS
|
||||||
|
[\#6241](https://github.com/matrix-org/matrix-react-sdk/pull/6241)
|
||||||
|
* Open local addresses section by default when there are no existing local
|
||||||
|
addresses
|
||||||
|
[\#6179](https://github.com/matrix-org/matrix-react-sdk/pull/6179)
|
||||||
|
* Allow reordering of the space panel via Drag and Drop
|
||||||
|
[\#6137](https://github.com/matrix-org/matrix-react-sdk/pull/6137)
|
||||||
|
* Replace drag and drop mechanism in communities with something simpler
|
||||||
|
[\#6134](https://github.com/matrix-org/matrix-react-sdk/pull/6134)
|
||||||
|
* EventTilePreview fixes
|
||||||
|
[\#6000](https://github.com/matrix-org/matrix-react-sdk/pull/6000)
|
||||||
|
* Upgrade @types/react and @types/react-dom
|
||||||
|
[\#6233](https://github.com/matrix-org/matrix-react-sdk/pull/6233)
|
||||||
|
* Fix type error in the SpaceStore
|
||||||
|
[\#6242](https://github.com/matrix-org/matrix-react-sdk/pull/6242)
|
||||||
|
* Add experimental options to the Spaces beta
|
||||||
|
[\#6199](https://github.com/matrix-org/matrix-react-sdk/pull/6199)
|
||||||
|
* Consolidate types with js-sdk changes
|
||||||
|
[\#6215](https://github.com/matrix-org/matrix-react-sdk/pull/6215)
|
||||||
|
* Fix branch matching for Buildkite
|
||||||
|
[\#6236](https://github.com/matrix-org/matrix-react-sdk/pull/6236)
|
||||||
|
* Migrate SearchBar to TypeScript
|
||||||
|
[\#6230](https://github.com/matrix-org/matrix-react-sdk/pull/6230)
|
||||||
|
* Add support to keyboard shortcuts dialog for [digits]
|
||||||
|
[\#6088](https://github.com/matrix-org/matrix-react-sdk/pull/6088)
|
||||||
|
* Fix modal opening race condition
|
||||||
|
[\#6238](https://github.com/matrix-org/matrix-react-sdk/pull/6238)
|
||||||
|
* Deprecate FormButton in favour of AccessibleButton
|
||||||
|
[\#6229](https://github.com/matrix-org/matrix-react-sdk/pull/6229)
|
||||||
|
* Add PR template
|
||||||
|
[\#6216](https://github.com/matrix-org/matrix-react-sdk/pull/6216)
|
||||||
|
* Prefer canonical aliases while autocompleting rooms
|
||||||
|
[\#6222](https://github.com/matrix-org/matrix-react-sdk/pull/6222)
|
||||||
|
* Fix quote button
|
||||||
|
[\#6232](https://github.com/matrix-org/matrix-react-sdk/pull/6232)
|
||||||
|
* Restore branch matching support for GitHub Actions e2e tests
|
||||||
|
[\#6224](https://github.com/matrix-org/matrix-react-sdk/pull/6224)
|
||||||
|
* Fix View Source accessing renamed private field on MatrixEvent
|
||||||
|
[\#6225](https://github.com/matrix-org/matrix-react-sdk/pull/6225)
|
||||||
|
* Fix ConfirmUserActionDialog returning an input field rather than text
|
||||||
|
[\#6219](https://github.com/matrix-org/matrix-react-sdk/pull/6219)
|
||||||
|
* Revert "Partially restore immutable event objects at the rendering layer"
|
||||||
|
[\#6221](https://github.com/matrix-org/matrix-react-sdk/pull/6221)
|
||||||
|
* Add jq to e2e tests Dockerfile
|
||||||
|
[\#6218](https://github.com/matrix-org/matrix-react-sdk/pull/6218)
|
||||||
|
* Partially restore immutable event objects at the rendering layer
|
||||||
|
[\#6196](https://github.com/matrix-org/matrix-react-sdk/pull/6196)
|
||||||
|
* Update MSC number references for voice messages
|
||||||
|
[\#6197](https://github.com/matrix-org/matrix-react-sdk/pull/6197)
|
||||||
|
* Fix phase enum usage in JS modules as well
|
||||||
|
[\#6214](https://github.com/matrix-org/matrix-react-sdk/pull/6214)
|
||||||
|
* Migrate some dialogs to TypeScript
|
||||||
|
[\#6185](https://github.com/matrix-org/matrix-react-sdk/pull/6185)
|
||||||
|
* Typescript fixes due to MatrixEvent being TSified
|
||||||
|
[\#6208](https://github.com/matrix-org/matrix-react-sdk/pull/6208)
|
||||||
|
* Allow click-to-ping, quote & emoji picker for edit composer too
|
||||||
|
[\#5858](https://github.com/matrix-org/matrix-react-sdk/pull/5858)
|
||||||
|
* Add call silencing
|
||||||
|
[\#6082](https://github.com/matrix-org/matrix-react-sdk/pull/6082)
|
||||||
|
* Fix types in SlashCommands
|
||||||
|
[\#6207](https://github.com/matrix-org/matrix-react-sdk/pull/6207)
|
||||||
|
* Benchmark multiple common user scenario
|
||||||
|
[\#6190](https://github.com/matrix-org/matrix-react-sdk/pull/6190)
|
||||||
|
* Fix forward dialog message preview display names
|
||||||
|
[\#6204](https://github.com/matrix-org/matrix-react-sdk/pull/6204)
|
||||||
|
* Remove stray bullet point in reply preview
|
||||||
|
[\#6206](https://github.com/matrix-org/matrix-react-sdk/pull/6206)
|
||||||
|
* Stop requesting null next replies from the server
|
||||||
|
[\#6203](https://github.com/matrix-org/matrix-react-sdk/pull/6203)
|
||||||
|
* Fix soft crash caused by a broken shouldComponentUpdate
|
||||||
|
[\#6202](https://github.com/matrix-org/matrix-react-sdk/pull/6202)
|
||||||
|
* Keep composer reply when scrolling away from a highlighted event
|
||||||
|
[\#6200](https://github.com/matrix-org/matrix-react-sdk/pull/6200)
|
||||||
|
* Cache virtual/native room mappings when they're created
|
||||||
|
[\#6194](https://github.com/matrix-org/matrix-react-sdk/pull/6194)
|
||||||
|
* Disable comment-on-alert
|
||||||
|
[\#6191](https://github.com/matrix-org/matrix-react-sdk/pull/6191)
|
||||||
|
* Bump postcss from 7.0.35 to 7.0.36
|
||||||
|
[\#6195](https://github.com/matrix-org/matrix-react-sdk/pull/6195)
|
||||||
|
|
||||||
Changes in [3.24.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.24.0) (2021-06-21)
|
Changes in [3.24.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.24.0) (2021-06-21)
|
||||||
=====================================================================================================
|
=====================================================================================================
|
||||||
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.24.0-rc.1...v3.24.0)
|
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.24.0-rc.1...v3.24.0)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "matrix-react-sdk",
|
"name": "matrix-react-sdk",
|
||||||
"version": "3.24.0",
|
"version": "3.25.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.0.0",
|
"matrix-js-sdk": "12.0.1",
|
||||||
"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",
|
||||||
|
|
|
@ -57,7 +57,6 @@
|
||||||
@import "./views/avatars/_BaseAvatar.scss";
|
@import "./views/avatars/_BaseAvatar.scss";
|
||||||
@import "./views/avatars/_DecoratedRoomAvatar.scss";
|
@import "./views/avatars/_DecoratedRoomAvatar.scss";
|
||||||
@import "./views/avatars/_MemberStatusMessageAvatar.scss";
|
@import "./views/avatars/_MemberStatusMessageAvatar.scss";
|
||||||
@import "./views/avatars/_PulsedAvatar.scss";
|
|
||||||
@import "./views/avatars/_WidgetAvatar.scss";
|
@import "./views/avatars/_WidgetAvatar.scss";
|
||||||
@import "./views/beta/_BetaCard.scss";
|
@import "./views/beta/_BetaCard.scss";
|
||||||
@import "./views/context_menus/_CallContextMenu.scss";
|
@import "./views/context_menus/_CallContextMenu.scss";
|
||||||
|
|
|
@ -121,23 +121,51 @@ $pulse-color: $pinned-unread-color;
|
||||||
box-shadow: 0 0 0 0 rgba($pulse-color, 1);
|
box-shadow: 0 0 0 0 rgba($pulse-color, 1);
|
||||||
animation: mx_RightPanel_indicator_pulse 2s infinite;
|
animation: mx_RightPanel_indicator_pulse 2s infinite;
|
||||||
animation-iteration-count: 1;
|
animation-iteration-count: 1;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
width: inherit;
|
||||||
|
height: inherit;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
transform: scale(1);
|
||||||
|
transform-origin: center center;
|
||||||
|
animation-name: mx_RightPanel_indicator_pulse_shadow;
|
||||||
|
animation-duration: inherit;
|
||||||
|
animation-iteration-count: inherit;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: rgba($pulse-color, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes mx_RightPanel_indicator_pulse {
|
@keyframes mx_RightPanel_indicator_pulse {
|
||||||
0% {
|
0% {
|
||||||
transform: scale(0.95);
|
transform: scale(0.95);
|
||||||
box-shadow: 0 0 0 0 rgba($pulse-color, 0.7);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
70% {
|
70% {
|
||||||
transform: scale(1);
|
transform: scale(1);
|
||||||
box-shadow: 0 0 0 10px rgba($pulse-color, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
100% {
|
100% {
|
||||||
transform: scale(0.95);
|
transform: scale(0.95);
|
||||||
box-shadow: 0 0 0 0 rgba($pulse-color, 0);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes mx_RightPanel_indicator_pulse_shadow {
|
||||||
|
0% {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
70% {
|
||||||
|
transform: scale(2.2);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,14 +57,15 @@ limitations under the License.
|
||||||
|
|
||||||
@keyframes mx_RoomView_fileDropTarget_image_animation {
|
@keyframes mx_RoomView_fileDropTarget_image_animation {
|
||||||
from {
|
from {
|
||||||
width: 0px;
|
transform: scaleX(0);
|
||||||
}
|
}
|
||||||
to {
|
to {
|
||||||
width: 32px;
|
transform: scaleX(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomView_fileDropTarget_image {
|
.mx_RoomView_fileDropTarget_image {
|
||||||
|
width: 32px;
|
||||||
animation: mx_RoomView_fileDropTarget_image_animation;
|
animation: mx_RoomView_fileDropTarget_image_animation;
|
||||||
animation-duration: 0.5s;
|
animation-duration: 0.5s;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2020 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_PulsedAvatar {
|
|
||||||
@keyframes shadow-pulse {
|
|
||||||
0% {
|
|
||||||
box-shadow: 0 0 0 0px rgba($accent-color, 0.2);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
box-shadow: 0 0 0 6px rgba($accent-color, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
animation: shadow-pulse 1s infinite;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -110,24 +110,52 @@ $dot-size: 12px;
|
||||||
width: $dot-size;
|
width: $dot-size;
|
||||||
transform: scale(1);
|
transform: scale(1);
|
||||||
background: rgba($pulse-color, 1);
|
background: rgba($pulse-color, 1);
|
||||||
box-shadow: 0 0 0 0 rgba($pulse-color, 1);
|
|
||||||
animation: mx_Beta_bluePulse 2s infinite;
|
animation: mx_Beta_bluePulse 2s infinite;
|
||||||
animation-iteration-count: 20;
|
animation-iteration-count: 20;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
width: inherit;
|
||||||
|
height: inherit;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
transform: scale(1);
|
||||||
|
transform-origin: center center;
|
||||||
|
animation-name: mx_Beta_bluePulse_shadow;
|
||||||
|
animation-duration: inherit;
|
||||||
|
animation-iteration-count: inherit;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: rgba($pulse-color, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes mx_Beta_bluePulse {
|
@keyframes mx_Beta_bluePulse {
|
||||||
0% {
|
0% {
|
||||||
transform: scale(0.95);
|
transform: scale(0.95);
|
||||||
box-shadow: 0 0 0 0 rgba($pulse-color, 0.7);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
70% {
|
70% {
|
||||||
transform: scale(1);
|
transform: scale(1);
|
||||||
box-shadow: 0 0 0 10px rgba($pulse-color, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
100% {
|
100% {
|
||||||
transform: scale(0.95);
|
transform: scale(0.95);
|
||||||
box-shadow: 0 0 0 0 rgba($pulse-color, 0);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes mx_Beta_bluePulse_shadow {
|
||||||
|
0% {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
70% {
|
||||||
|
transform: scale(2.2);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
opacity: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ limitations under the License.
|
||||||
left: 0;
|
left: 0;
|
||||||
top: 2px; // alignment
|
top: 2px; // alignment
|
||||||
background-image: url("$(res)/img/element-icons/warning-badge.svg");
|
background-image: url("$(res)/img/element-icons/warning-badge.svg");
|
||||||
|
background-size: contain;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_AccessSecretStorageDialog_reset_link {
|
.mx_AccessSecretStorageDialog_reset_link {
|
||||||
|
|
2
src/@types/global.d.ts
vendored
2
src/@types/global.d.ts
vendored
|
@ -46,6 +46,7 @@ import { VoiceRecordingStore } from "../stores/VoiceRecordingStore";
|
||||||
import PerformanceMonitor from "../performance";
|
import PerformanceMonitor from "../performance";
|
||||||
import UIStore from "../stores/UIStore";
|
import UIStore from "../stores/UIStore";
|
||||||
import { SetupEncryptionStore } from "../stores/SetupEncryptionStore";
|
import { SetupEncryptionStore } from "../stores/SetupEncryptionStore";
|
||||||
|
import { RoomScrollStateStore } from "../stores/RoomScrollStateStore";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
|
@ -87,6 +88,7 @@ declare global {
|
||||||
mxPerformanceEntryNames: any;
|
mxPerformanceEntryNames: any;
|
||||||
mxUIStore: UIStore;
|
mxUIStore: UIStore;
|
||||||
mxSetupEncryptionStore?: SetupEncryptionStore;
|
mxSetupEncryptionStore?: SetupEncryptionStore;
|
||||||
|
mxRoomScrollStateStore?: RoomScrollStateStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Document {
|
interface Document {
|
||||||
|
|
|
@ -41,7 +41,7 @@ import eventSearch, { searchPagination } from '../../Searching';
|
||||||
import MainSplit from './MainSplit';
|
import MainSplit from './MainSplit';
|
||||||
import RightPanel from './RightPanel';
|
import RightPanel from './RightPanel';
|
||||||
import RoomViewStore from '../../stores/RoomViewStore';
|
import RoomViewStore from '../../stores/RoomViewStore';
|
||||||
import RoomScrollStateStore from '../../stores/RoomScrollStateStore';
|
import RoomScrollStateStore, { ScrollState } from '../../stores/RoomScrollStateStore';
|
||||||
import WidgetEchoStore from '../../stores/WidgetEchoStore';
|
import WidgetEchoStore from '../../stores/WidgetEchoStore';
|
||||||
import SettingsStore from "../../settings/SettingsStore";
|
import SettingsStore from "../../settings/SettingsStore";
|
||||||
import { Layout } from "../../settings/Layout";
|
import { Layout } from "../../settings/Layout";
|
||||||
|
@ -1578,7 +1578,7 @@ export default class RoomView extends React.Component<IProps, IState> {
|
||||||
// get the current scroll position of the room, so that it can be
|
// get the current scroll position of the room, so that it can be
|
||||||
// restored when we switch back to it.
|
// restored when we switch back to it.
|
||||||
//
|
//
|
||||||
private getScrollState() {
|
private getScrollState(): ScrollState {
|
||||||
const messagePanel = this.messagePanel;
|
const messagePanel = this.messagePanel;
|
||||||
if (!messagePanel) return null;
|
if (!messagePanel) return null;
|
||||||
|
|
||||||
|
|
|
@ -15,39 +15,42 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import { SetupEncryptionStore, Phase } from '../../../stores/SetupEncryptionStore';
|
import { SetupEncryptionStore, Phase } from '../../../stores/SetupEncryptionStore';
|
||||||
import SetupEncryptionBody from "./SetupEncryptionBody";
|
import SetupEncryptionBody from "./SetupEncryptionBody";
|
||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
|
|
||||||
@replaceableComponent("structures.auth.CompleteSecurity")
|
interface IProps {
|
||||||
export default class CompleteSecurity extends React.Component {
|
onFinished: () => void;
|
||||||
static propTypes = {
|
}
|
||||||
onFinished: PropTypes.func.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor() {
|
interface IState {
|
||||||
super();
|
phase: Phase;
|
||||||
|
}
|
||||||
|
|
||||||
|
@replaceableComponent("structures.auth.CompleteSecurity")
|
||||||
|
export default class CompleteSecurity extends React.Component<IProps, IState> {
|
||||||
|
constructor(props: IProps) {
|
||||||
|
super(props);
|
||||||
const store = SetupEncryptionStore.sharedInstance();
|
const store = SetupEncryptionStore.sharedInstance();
|
||||||
store.on("update", this._onStoreUpdate);
|
store.on("update", this.onStoreUpdate);
|
||||||
store.start();
|
store.start();
|
||||||
this.state = { phase: store.phase };
|
this.state = { phase: store.phase };
|
||||||
}
|
}
|
||||||
|
|
||||||
_onStoreUpdate = () => {
|
private onStoreUpdate = (): void => {
|
||||||
const store = SetupEncryptionStore.sharedInstance();
|
const store = SetupEncryptionStore.sharedInstance();
|
||||||
this.setState({ phase: store.phase });
|
this.setState({ phase: store.phase });
|
||||||
};
|
};
|
||||||
|
|
||||||
componentWillUnmount() {
|
public componentWillUnmount(): void {
|
||||||
const store = SetupEncryptionStore.sharedInstance();
|
const store = SetupEncryptionStore.sharedInstance();
|
||||||
store.off("update", this._onStoreUpdate);
|
store.off("update", this.onStoreUpdate);
|
||||||
store.stop();
|
store.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
public render() {
|
||||||
const AuthPage = sdk.getComponent("auth.AuthPage");
|
const AuthPage = sdk.getComponent("auth.AuthPage");
|
||||||
const CompleteSecurityBody = sdk.getComponent("auth.CompleteSecurityBody");
|
const CompleteSecurityBody = sdk.getComponent("auth.CompleteSecurityBody");
|
||||||
const { phase } = this.state;
|
const { phase } = this.state;
|
|
@ -15,20 +15,19 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import AuthPage from '../../views/auth/AuthPage';
|
import AuthPage from '../../views/auth/AuthPage';
|
||||||
import CompleteSecurityBody from '../../views/auth/CompleteSecurityBody';
|
import CompleteSecurityBody from '../../views/auth/CompleteSecurityBody';
|
||||||
import CreateCrossSigningDialog from '../../views/dialogs/security/CreateCrossSigningDialog';
|
import CreateCrossSigningDialog from '../../views/dialogs/security/CreateCrossSigningDialog';
|
||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
|
|
||||||
@replaceableComponent("structures.auth.E2eSetup")
|
interface IProps {
|
||||||
export default class E2eSetup extends React.Component {
|
onFinished: () => void;
|
||||||
static propTypes = {
|
accountPassword?: string;
|
||||||
onFinished: PropTypes.func.isRequired,
|
tokenLogin?: boolean;
|
||||||
accountPassword: PropTypes.string,
|
}
|
||||||
tokenLogin: PropTypes.bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
@replaceableComponent("structures.auth.E2eSetup")
|
||||||
|
export default class E2eSetup extends React.Component<IProps> {
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<AuthPage>
|
<AuthPage>
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { _t, _td } from '../../../languageHandler';
|
import { _t, _td } from '../../../languageHandler';
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import Modal from "../../../Modal";
|
import Modal from "../../../Modal";
|
||||||
|
@ -31,27 +30,50 @@ import PassphraseField from '../../views/auth/PassphraseField';
|
||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
import { PASSWORD_MIN_SCORE } from '../../views/auth/RegistrationForm';
|
import { PASSWORD_MIN_SCORE } from '../../views/auth/RegistrationForm';
|
||||||
|
|
||||||
// Phases
|
import { IValidationResult } from "../../views/elements/Validation";
|
||||||
// Show the forgot password inputs
|
|
||||||
const PHASE_FORGOT = 1;
|
enum Phase {
|
||||||
// Email is in the process of being sent
|
// Show the forgot password inputs
|
||||||
const PHASE_SENDING_EMAIL = 2;
|
Forgot = 1,
|
||||||
// Email has been sent
|
// Email is in the process of being sent
|
||||||
const PHASE_EMAIL_SENT = 3;
|
SendingEmail = 2,
|
||||||
// User has clicked the link in email and completed reset
|
// Email has been sent
|
||||||
const PHASE_DONE = 4;
|
EmailSent = 3,
|
||||||
|
// User has clicked the link in email and completed reset
|
||||||
|
Done = 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
serverConfig: ValidatedServerConfig;
|
||||||
|
onServerConfigChange: () => void;
|
||||||
|
onLoginClick?: () => void;
|
||||||
|
onComplete: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IState {
|
||||||
|
phase: Phase;
|
||||||
|
email: string;
|
||||||
|
password: string;
|
||||||
|
password2: string;
|
||||||
|
errorText: string;
|
||||||
|
|
||||||
|
// We perform liveliness checks later, but for now suppress the errors.
|
||||||
|
// We also track the server dead errors independently of the regular errors so
|
||||||
|
// that we can render it differently, and override any other error the user may
|
||||||
|
// be seeing.
|
||||||
|
serverIsAlive: boolean;
|
||||||
|
serverErrorIsFatal: boolean;
|
||||||
|
serverDeadError: string;
|
||||||
|
|
||||||
|
passwordFieldValid: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
@replaceableComponent("structures.auth.ForgotPassword")
|
@replaceableComponent("structures.auth.ForgotPassword")
|
||||||
export default class ForgotPassword extends React.Component {
|
export default class ForgotPassword extends React.Component<IProps, IState> {
|
||||||
static propTypes = {
|
private reset: PasswordReset;
|
||||||
serverConfig: PropTypes.instanceOf(ValidatedServerConfig).isRequired,
|
|
||||||
onServerConfigChange: PropTypes.func.isRequired,
|
|
||||||
onLoginClick: PropTypes.func,
|
|
||||||
onComplete: PropTypes.func.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
phase: PHASE_FORGOT,
|
phase: Phase.Forgot,
|
||||||
email: "",
|
email: "",
|
||||||
password: "",
|
password: "",
|
||||||
password2: "",
|
password2: "",
|
||||||
|
@ -64,30 +86,31 @@ export default class ForgotPassword extends React.Component {
|
||||||
serverIsAlive: true,
|
serverIsAlive: true,
|
||||||
serverErrorIsFatal: false,
|
serverErrorIsFatal: false,
|
||||||
serverDeadError: "",
|
serverDeadError: "",
|
||||||
|
passwordFieldValid: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props: IProps) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
CountlyAnalytics.instance.track("onboarding_forgot_password_begin");
|
CountlyAnalytics.instance.track("onboarding_forgot_password_begin");
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
public componentDidMount() {
|
||||||
this.reset = null;
|
this.reset = null;
|
||||||
this._checkServerLiveliness(this.props.serverConfig);
|
this.checkServerLiveliness(this.props.serverConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Replace with appropriate lifecycle event
|
// TODO: [REACT-WARNING] Replace with appropriate lifecycle event
|
||||||
// eslint-disable-next-line camelcase
|
// eslint-disable-next-line camelcase
|
||||||
UNSAFE_componentWillReceiveProps(newProps) {
|
public UNSAFE_componentWillReceiveProps(newProps: IProps): void {
|
||||||
if (newProps.serverConfig.hsUrl === this.props.serverConfig.hsUrl &&
|
if (newProps.serverConfig.hsUrl === this.props.serverConfig.hsUrl &&
|
||||||
newProps.serverConfig.isUrl === this.props.serverConfig.isUrl) return;
|
newProps.serverConfig.isUrl === this.props.serverConfig.isUrl) return;
|
||||||
|
|
||||||
// Do a liveliness check on the new URLs
|
// Do a liveliness check on the new URLs
|
||||||
this._checkServerLiveliness(newProps.serverConfig);
|
this.checkServerLiveliness(newProps.serverConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
async _checkServerLiveliness(serverConfig) {
|
private async checkServerLiveliness(serverConfig): Promise<void> {
|
||||||
try {
|
try {
|
||||||
await AutoDiscoveryUtils.validateServerConfigWithStaticUrls(
|
await AutoDiscoveryUtils.validateServerConfigWithStaticUrls(
|
||||||
serverConfig.hsUrl,
|
serverConfig.hsUrl,
|
||||||
|
@ -98,28 +121,28 @@ export default class ForgotPassword extends React.Component {
|
||||||
serverIsAlive: true,
|
serverIsAlive: true,
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.setState(AutoDiscoveryUtils.authComponentStateForError(e, "forgot_password"));
|
this.setState(AutoDiscoveryUtils.authComponentStateForError(e, "forgot_password") as IState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
submitPasswordReset(email, password) {
|
public submitPasswordReset(email: string, password: string): void {
|
||||||
this.setState({
|
this.setState({
|
||||||
phase: PHASE_SENDING_EMAIL,
|
phase: Phase.SendingEmail,
|
||||||
});
|
});
|
||||||
this.reset = new PasswordReset(this.props.serverConfig.hsUrl, this.props.serverConfig.isUrl);
|
this.reset = new PasswordReset(this.props.serverConfig.hsUrl, this.props.serverConfig.isUrl);
|
||||||
this.reset.resetPassword(email, password).then(() => {
|
this.reset.resetPassword(email, password).then(() => {
|
||||||
this.setState({
|
this.setState({
|
||||||
phase: PHASE_EMAIL_SENT,
|
phase: Phase.EmailSent,
|
||||||
});
|
});
|
||||||
}, (err) => {
|
}, (err) => {
|
||||||
this.showErrorDialog(_t('Failed to send email') + ": " + err.message);
|
this.showErrorDialog(_t('Failed to send email') + ": " + err.message);
|
||||||
this.setState({
|
this.setState({
|
||||||
phase: PHASE_FORGOT,
|
phase: Phase.Forgot,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onVerify = async ev => {
|
private onVerify = async (ev: React.MouseEvent): Promise<void> => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
if (!this.reset) {
|
if (!this.reset) {
|
||||||
console.error("onVerify called before submitPasswordReset!");
|
console.error("onVerify called before submitPasswordReset!");
|
||||||
|
@ -127,17 +150,17 @@ export default class ForgotPassword extends React.Component {
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await this.reset.checkEmailLinkClicked();
|
await this.reset.checkEmailLinkClicked();
|
||||||
this.setState({ phase: PHASE_DONE });
|
this.setState({ phase: Phase.Done });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.showErrorDialog(err.message);
|
this.showErrorDialog(err.message);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
onSubmitForm = async ev => {
|
private onSubmitForm = async (ev: React.FormEvent): Promise<void> => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
|
||||||
// refresh the server errors, just in case the server came back online
|
// refresh the server errors, just in case the server came back online
|
||||||
await this._checkServerLiveliness(this.props.serverConfig);
|
await this.checkServerLiveliness(this.props.serverConfig);
|
||||||
|
|
||||||
await this['password_field'].validate({ allowEmpty: false });
|
await this['password_field'].validate({ allowEmpty: false });
|
||||||
|
|
||||||
|
@ -172,27 +195,27 @@ export default class ForgotPassword extends React.Component {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
onInputChanged = (stateKey, ev) => {
|
private onInputChanged = (stateKey: string, ev: React.FormEvent<HTMLInputElement>) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
[stateKey]: ev.target.value,
|
[stateKey]: ev.currentTarget.value,
|
||||||
});
|
} as any);
|
||||||
};
|
};
|
||||||
|
|
||||||
onLoginClick = ev => {
|
private onLoginClick = (ev: React.MouseEvent): void => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
this.props.onLoginClick();
|
this.props.onLoginClick();
|
||||||
};
|
};
|
||||||
|
|
||||||
showErrorDialog(body, title) {
|
public showErrorDialog(description: string, title?: string) {
|
||||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||||
Modal.createTrackedDialog('Forgot Password Error', '', ErrorDialog, {
|
Modal.createTrackedDialog('Forgot Password Error', '', ErrorDialog, {
|
||||||
title: title,
|
title,
|
||||||
description: body,
|
description,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onPasswordValidate(result) {
|
private onPasswordValidate(result: IValidationResult) {
|
||||||
this.setState({
|
this.setState({
|
||||||
passwordFieldValid: result.valid,
|
passwordFieldValid: result.valid,
|
||||||
});
|
});
|
||||||
|
@ -316,16 +339,16 @@ export default class ForgotPassword extends React.Component {
|
||||||
|
|
||||||
let resetPasswordJsx;
|
let resetPasswordJsx;
|
||||||
switch (this.state.phase) {
|
switch (this.state.phase) {
|
||||||
case PHASE_FORGOT:
|
case Phase.Forgot:
|
||||||
resetPasswordJsx = this.renderForgot();
|
resetPasswordJsx = this.renderForgot();
|
||||||
break;
|
break;
|
||||||
case PHASE_SENDING_EMAIL:
|
case Phase.SendingEmail:
|
||||||
resetPasswordJsx = this.renderSendingEmail();
|
resetPasswordJsx = this.renderSendingEmail();
|
||||||
break;
|
break;
|
||||||
case PHASE_EMAIL_SENT:
|
case Phase.EmailSent:
|
||||||
resetPasswordJsx = this.renderEmailSent();
|
resetPasswordJsx = this.renderEmailSent();
|
||||||
break;
|
break;
|
||||||
case PHASE_DONE:
|
case Phase.Done:
|
||||||
resetPasswordJsx = this.renderDone();
|
resetPasswordJsx = this.renderDone();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
|
@ -24,7 +24,7 @@ import FocusLock from "react-focus-lock";
|
||||||
import MemberAvatar from "../avatars/MemberAvatar";
|
import MemberAvatar from "../avatars/MemberAvatar";
|
||||||
import { ContextMenuTooltipButton } from "../../../accessibility/context_menu/ContextMenuTooltipButton";
|
import { ContextMenuTooltipButton } from "../../../accessibility/context_menu/ContextMenuTooltipButton";
|
||||||
import MessageContextMenu from "../context_menus/MessageContextMenu";
|
import MessageContextMenu from "../context_menus/MessageContextMenu";
|
||||||
import { aboveLeftOf, ContextMenu } from '../../structures/ContextMenu';
|
import { aboveLeftOf } from '../../structures/ContextMenu';
|
||||||
import MessageTimestamp from "../messages/MessageTimestamp";
|
import MessageTimestamp from "../messages/MessageTimestamp";
|
||||||
import SettingsStore from "../../../settings/SettingsStore";
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
import { formatFullDate } from "../../../DateUtils";
|
import { formatFullDate } from "../../../DateUtils";
|
||||||
|
@ -122,7 +122,7 @@ export default class ImageView extends React.Component<IProps, IState> {
|
||||||
const image = this.image.current;
|
const image = this.image.current;
|
||||||
const imageWrapper = this.imageWrapper.current;
|
const imageWrapper = this.imageWrapper.current;
|
||||||
|
|
||||||
const rotation = inputRotation || this.state.rotation;
|
const rotation = inputRotation ?? this.state.rotation;
|
||||||
|
|
||||||
const imageIsNotFlipped = rotation % 180 === 0;
|
const imageIsNotFlipped = rotation % 180 === 0;
|
||||||
|
|
||||||
|
@ -304,17 +304,13 @@ export default class ImageView extends React.Component<IProps, IState> {
|
||||||
let contextMenu = null;
|
let contextMenu = null;
|
||||||
if (this.state.contextMenuDisplayed) {
|
if (this.state.contextMenuDisplayed) {
|
||||||
contextMenu = (
|
contextMenu = (
|
||||||
<ContextMenu
|
|
||||||
{...aboveLeftOf(this.contextMenuButton.current.getBoundingClientRect())}
|
|
||||||
onFinished={this.onCloseContextMenu}
|
|
||||||
>
|
|
||||||
<MessageContextMenu
|
<MessageContextMenu
|
||||||
|
{...aboveLeftOf(this.contextMenuButton.current.getBoundingClientRect())}
|
||||||
mxEvent={this.props.mxEvent}
|
mxEvent={this.props.mxEvent}
|
||||||
permalinkCreator={this.props.permalinkCreator}
|
permalinkCreator={this.props.permalinkCreator}
|
||||||
onFinished={this.onCloseContextMenu}
|
onFinished={this.onCloseContextMenu}
|
||||||
onCloseDialog={this.props.onFinished}
|
onCloseDialog={this.props.onFinished}
|
||||||
/>
|
/>
|
||||||
</ContextMenu>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,15 +17,25 @@ limitations under the License.
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { _t } from "../../../languageHandler";
|
import { _t } from "../../../languageHandler";
|
||||||
import { IntegrationManagers } from "../../../integrations/IntegrationManagers";
|
import { IntegrationManagers } from "../../../integrations/IntegrationManagers";
|
||||||
|
import { IntegrationManagerInstance } from "../../../integrations/IntegrationManagerInstance";
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import SettingsStore from "../../../settings/SettingsStore";
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
import { SettingLevel } from "../../../settings/SettingLevel";
|
import { SettingLevel } from "../../../settings/SettingLevel";
|
||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IState {
|
||||||
|
currentManager: IntegrationManagerInstance;
|
||||||
|
provisioningEnabled: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
@replaceableComponent("views.settings.SetIntegrationManager")
|
@replaceableComponent("views.settings.SetIntegrationManager")
|
||||||
export default class SetIntegrationManager extends React.Component {
|
export default class SetIntegrationManager extends React.Component<IProps, IState> {
|
||||||
constructor() {
|
constructor(props: IProps) {
|
||||||
super();
|
super(props);
|
||||||
|
|
||||||
const currentManager = IntegrationManagers.sharedInstance().getPrimaryManager();
|
const currentManager = IntegrationManagers.sharedInstance().getPrimaryManager();
|
||||||
|
|
||||||
|
@ -35,7 +45,7 @@ export default class SetIntegrationManager extends React.Component {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
onProvisioningToggled = () => {
|
private onProvisioningToggled = (): void => {
|
||||||
const current = this.state.provisioningEnabled;
|
const current = this.state.provisioningEnabled;
|
||||||
SettingsStore.setValue("integrationProvisioning", null, SettingLevel.ACCOUNT, !current).catch(err => {
|
SettingsStore.setValue("integrationProvisioning", null, SettingLevel.ACCOUNT, !current).catch(err => {
|
||||||
console.error("Error changing integration manager provisioning");
|
console.error("Error changing integration manager provisioning");
|
||||||
|
@ -46,7 +56,7 @@ export default class SetIntegrationManager extends React.Component {
|
||||||
this.setState({ provisioningEnabled: !current });
|
this.setState({ provisioningEnabled: !current });
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
public render(): React.ReactNode {
|
||||||
const ToggleSwitch = sdk.getComponent("views.elements.ToggleSwitch");
|
const ToggleSwitch = sdk.getComponent("views.elements.ToggleSwitch");
|
||||||
|
|
||||||
const currentManager = this.state.currentManager;
|
const currentManager = this.state.currentManager;
|
|
@ -24,6 +24,8 @@ import PlatformPeg from "../../../../../PlatformPeg";
|
||||||
import { SettingLevel } from "../../../../../settings/SettingLevel";
|
import { SettingLevel } from "../../../../../settings/SettingLevel";
|
||||||
import { replaceableComponent } from "../../../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../../../utils/replaceableComponent";
|
||||||
import SettingsFlag from '../../../elements/SettingsFlag';
|
import SettingsFlag from '../../../elements/SettingsFlag';
|
||||||
|
import * as KeyboardShortcuts from "../../../../../accessibility/KeyboardShortcuts";
|
||||||
|
import AccessibleButton from "../../../elements/AccessibleButton";
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
autoLaunch: boolean;
|
autoLaunch: boolean;
|
||||||
|
@ -45,6 +47,10 @@ export default class PreferencesUserSettingsTab extends React.Component<{}, ISta
|
||||||
'breadcrumbs',
|
'breadcrumbs',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
static KEYBINDINGS_SETTINGS = [
|
||||||
|
'ctrlFForSearch',
|
||||||
|
];
|
||||||
|
|
||||||
static COMPOSER_SETTINGS = [
|
static COMPOSER_SETTINGS = [
|
||||||
'MessageComposerInput.autoReplaceEmoji',
|
'MessageComposerInput.autoReplaceEmoji',
|
||||||
'MessageComposerInput.suggestEmoji',
|
'MessageComposerInput.suggestEmoji',
|
||||||
|
@ -53,28 +59,32 @@ export default class PreferencesUserSettingsTab extends React.Component<{}, ISta
|
||||||
'MessageComposerInput.showStickersButton',
|
'MessageComposerInput.showStickersButton',
|
||||||
];
|
];
|
||||||
|
|
||||||
static TIMELINE_SETTINGS = [
|
static TIME_SETTINGS = [
|
||||||
'showTypingNotifications',
|
|
||||||
'autoplayGifsAndVideos',
|
|
||||||
'urlPreviewsEnabled',
|
|
||||||
'TextualBody.enableBigEmoji',
|
|
||||||
'showReadReceipts',
|
|
||||||
'showTwelveHourTimestamps',
|
'showTwelveHourTimestamps',
|
||||||
'alwaysShowTimestamps',
|
'alwaysShowTimestamps',
|
||||||
'showRedactions',
|
];
|
||||||
|
static CODE_BLOCKS_SETTINGS = [
|
||||||
'enableSyntaxHighlightLanguageDetection',
|
'enableSyntaxHighlightLanguageDetection',
|
||||||
'expandCodeByDefault',
|
'expandCodeByDefault',
|
||||||
'scrollToBottomOnMessageSent',
|
|
||||||
'showCodeLineNumbers',
|
'showCodeLineNumbers',
|
||||||
'showJoinLeaves',
|
|
||||||
'showAvatarChanges',
|
|
||||||
'showDisplaynameChanges',
|
|
||||||
'showImages',
|
|
||||||
'showChatEffects',
|
|
||||||
'Pill.shouldShowPillAvatar',
|
|
||||||
'ctrlFForSearch',
|
|
||||||
];
|
];
|
||||||
|
static IMAGES_AND_VIDEOS_SETTINGS = [
|
||||||
|
'urlPreviewsEnabled',
|
||||||
|
'autoplayGifsAndVideos',
|
||||||
|
'showImages',
|
||||||
|
];
|
||||||
|
static TIMELINE_SETTINGS = [
|
||||||
|
'showTypingNotifications',
|
||||||
|
'showRedactions',
|
||||||
|
'showReadReceipts',
|
||||||
|
'showJoinLeaves',
|
||||||
|
'showDisplaynameChanges',
|
||||||
|
'showChatEffects',
|
||||||
|
'showAvatarChanges',
|
||||||
|
'Pill.shouldShowPillAvatar',
|
||||||
|
'TextualBody.enableBigEmoji',
|
||||||
|
'scrollToBottomOnMessageSent',
|
||||||
|
];
|
||||||
static GENERAL_SETTINGS = [
|
static GENERAL_SETTINGS = [
|
||||||
'TagPanel.enableTagPanel',
|
'TagPanel.enableTagPanel',
|
||||||
'promptBeforeInviteUnknownUsers',
|
'promptBeforeInviteUnknownUsers',
|
||||||
|
@ -221,11 +231,34 @@ export default class PreferencesUserSettingsTab extends React.Component<{}, ISta
|
||||||
{this.renderGroup(PreferencesUserSettingsTab.ROOM_LIST_SETTINGS)}
|
{this.renderGroup(PreferencesUserSettingsTab.ROOM_LIST_SETTINGS)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="mx_SettingsTab_section">
|
||||||
|
<span className="mx_SettingsTab_subheading">{_t("Keyboard shortcuts")}</span>
|
||||||
|
<AccessibleButton className="mx_SettingsFlag" onClick={KeyboardShortcuts.toggleDialog}>
|
||||||
|
{ _t("To view all keyboard shortcuts, click here.") }
|
||||||
|
</AccessibleButton>
|
||||||
|
{this.renderGroup(PreferencesUserSettingsTab.KEYBINDINGS_SETTINGS)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mx_SettingsTab_section">
|
||||||
|
<span className="mx_SettingsTab_subheading">{_t("Displaying time")}</span>
|
||||||
|
{this.renderGroup(PreferencesUserSettingsTab.TIME_SETTINGS)}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="mx_SettingsTab_section">
|
<div className="mx_SettingsTab_section">
|
||||||
<span className="mx_SettingsTab_subheading">{_t("Composer")}</span>
|
<span className="mx_SettingsTab_subheading">{_t("Composer")}</span>
|
||||||
{this.renderGroup(PreferencesUserSettingsTab.COMPOSER_SETTINGS)}
|
{this.renderGroup(PreferencesUserSettingsTab.COMPOSER_SETTINGS)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="mx_SettingsTab_section">
|
||||||
|
<span className="mx_SettingsTab_subheading">{_t("Code blocks")}</span>
|
||||||
|
{this.renderGroup(PreferencesUserSettingsTab.CODE_BLOCKS_SETTINGS)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mx_SettingsTab_section">
|
||||||
|
<span className="mx_SettingsTab_subheading">{_t("Images, GIFs and videos")}</span>
|
||||||
|
{this.renderGroup(PreferencesUserSettingsTab.IMAGES_AND_VIDEOS_SETTINGS)}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="mx_SettingsTab_section">
|
<div className="mx_SettingsTab_section">
|
||||||
<span className="mx_SettingsTab_subheading">{_t("Timeline")}</span>
|
<span className="mx_SettingsTab_subheading">{_t("Timeline")}</span>
|
||||||
{this.renderGroup(PreferencesUserSettingsTab.TIMELINE_SETTINGS)}
|
{this.renderGroup(PreferencesUserSettingsTab.TIMELINE_SETTINGS)}
|
||||||
|
|
|
@ -15,39 +15,48 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
|
||||||
import { _t, pickBestLanguage } from "../../../languageHandler";
|
import { _t, pickBestLanguage } from "../../../languageHandler";
|
||||||
import * as sdk from "../../..";
|
import * as sdk from "../../..";
|
||||||
import { objectClone } from "../../../utils/objects";
|
import { objectClone } from "../../../utils/objects";
|
||||||
import StyledCheckbox from "../elements/StyledCheckbox";
|
import StyledCheckbox from "../elements/StyledCheckbox";
|
||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
policiesAndServicePairs: any[];
|
||||||
|
onFinished: (string) => void;
|
||||||
|
agreedUrls: string[]; // array of URLs the user has accepted
|
||||||
|
introElement: Node;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IState {
|
||||||
|
policies: Policy[];
|
||||||
|
busy: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Policy {
|
||||||
|
checked: boolean;
|
||||||
|
url: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
@replaceableComponent("views.terms.InlineTermsAgreement")
|
@replaceableComponent("views.terms.InlineTermsAgreement")
|
||||||
export default class InlineTermsAgreement extends React.Component {
|
export default class InlineTermsAgreement extends React.Component<IProps, IState> {
|
||||||
static propTypes = {
|
constructor(props: IProps) {
|
||||||
policiesAndServicePairs: PropTypes.array.isRequired, // array of service/policy pairs
|
super(props);
|
||||||
agreedUrls: PropTypes.array.isRequired, // array of URLs the user has accepted
|
|
||||||
onFinished: PropTypes.func.isRequired, // takes an argument of accepted URLs
|
|
||||||
introElement: PropTypes.node,
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
policies: [],
|
policies: [],
|
||||||
busy: false,
|
busy: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
public componentDidMount(): void {
|
||||||
// Build all the terms the user needs to accept
|
// Build all the terms the user needs to accept
|
||||||
const policies = []; // { checked, url, name }
|
const policies = []; // { checked, url, name }
|
||||||
for (const servicePolicies of this.props.policiesAndServicePairs) {
|
for (const servicePolicies of this.props.policiesAndServicePairs) {
|
||||||
const availablePolicies = Object.values(servicePolicies.policies);
|
const availablePolicies = Object.values(servicePolicies.policies);
|
||||||
for (const policy of availablePolicies) {
|
for (const policy of availablePolicies) {
|
||||||
const language = pickBestLanguage(Object.keys(policy).filter(p => p !== 'version'));
|
const language = pickBestLanguage(Object.keys(policy).filter(p => p !== 'version'));
|
||||||
const renderablePolicy = {
|
const renderablePolicy: Policy = {
|
||||||
checked: false,
|
checked: false,
|
||||||
url: policy[language].url,
|
url: policy[language].url,
|
||||||
name: policy[language].name,
|
name: policy[language].name,
|
||||||
|
@ -59,13 +68,13 @@ export default class InlineTermsAgreement extends React.Component {
|
||||||
this.setState({ policies });
|
this.setState({ policies });
|
||||||
}
|
}
|
||||||
|
|
||||||
_togglePolicy = (index) => {
|
private togglePolicy = (index: number): void => {
|
||||||
const policies = objectClone(this.state.policies);
|
const policies = objectClone(this.state.policies);
|
||||||
policies[index].checked = !policies[index].checked;
|
policies[index].checked = !policies[index].checked;
|
||||||
this.setState({ policies });
|
this.setState({ policies });
|
||||||
};
|
};
|
||||||
|
|
||||||
_onContinue = () => {
|
private onContinue = (): void => {
|
||||||
const hasUnchecked = !!this.state.policies.some(p => !p.checked);
|
const hasUnchecked = !!this.state.policies.some(p => !p.checked);
|
||||||
if (hasUnchecked) return;
|
if (hasUnchecked) return;
|
||||||
|
|
||||||
|
@ -73,7 +82,7 @@ export default class InlineTermsAgreement extends React.Component {
|
||||||
this.props.onFinished(this.state.policies.map(p => p.url));
|
this.props.onFinished(this.state.policies.map(p => p.url));
|
||||||
};
|
};
|
||||||
|
|
||||||
_renderCheckboxes() {
|
private renderCheckboxes(): React.ReactNode[] {
|
||||||
const rendered = [];
|
const rendered = [];
|
||||||
for (let i = 0; i < this.state.policies.length; i++) {
|
for (let i = 0; i < this.state.policies.length; i++) {
|
||||||
const policy = this.state.policies[i];
|
const policy = this.state.policies[i];
|
||||||
|
@ -93,7 +102,7 @@ export default class InlineTermsAgreement extends React.Component {
|
||||||
<div key={i} className='mx_InlineTermsAgreement_cbContainer'>
|
<div key={i} className='mx_InlineTermsAgreement_cbContainer'>
|
||||||
<div>{introText}</div>
|
<div>{introText}</div>
|
||||||
<div className='mx_InlineTermsAgreement_checkbox'>
|
<div className='mx_InlineTermsAgreement_checkbox'>
|
||||||
<StyledCheckbox onChange={() => this._togglePolicy(i)} checked={policy.checked}>
|
<StyledCheckbox onChange={() => this.togglePolicy(i)} checked={policy.checked}>
|
||||||
{_t("Accept")}
|
{_t("Accept")}
|
||||||
</StyledCheckbox>
|
</StyledCheckbox>
|
||||||
</div>
|
</div>
|
||||||
|
@ -103,16 +112,16 @@ export default class InlineTermsAgreement extends React.Component {
|
||||||
return rendered;
|
return rendered;
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
public render(): React.ReactNode {
|
||||||
const AccessibleButton = sdk.getComponent("views.elements.AccessibleButton");
|
const AccessibleButton = sdk.getComponent("views.elements.AccessibleButton");
|
||||||
const hasUnchecked = !!this.state.policies.some(p => !p.checked);
|
const hasUnchecked = !!this.state.policies.some(p => !p.checked);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{this.props.introElement}
|
{this.props.introElement}
|
||||||
{this._renderCheckboxes()}
|
{this.renderCheckboxes()}
|
||||||
<AccessibleButton
|
<AccessibleButton
|
||||||
onClick={this._onContinue}
|
onClick={this.onContinue}
|
||||||
disabled={hasUnchecked || this.state.busy}
|
disabled={hasUnchecked || this.state.busy}
|
||||||
kind="primary_sm"
|
kind="primary_sm"
|
||||||
>
|
>
|
|
@ -15,18 +15,17 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
|
|
||||||
@replaceableComponent("views.verification.VerificationCancelled")
|
interface IProps {
|
||||||
export default class VerificationCancelled extends React.Component {
|
onDone: () => void;
|
||||||
static propTypes = {
|
}
|
||||||
onDone: PropTypes.func.isRequired,
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
@replaceableComponent("views.verification.VerificationCancelled")
|
||||||
|
export default class VerificationCancelled extends React.Component<IProps> {
|
||||||
|
public render(): React.ReactNode {
|
||||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||||
return <div>
|
return <div>
|
||||||
<p>{_t(
|
<p>{_t(
|
|
@ -15,18 +15,17 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
|
|
||||||
@replaceableComponent("views.verification.VerificationComplete")
|
interface IProps {
|
||||||
export default class VerificationComplete extends React.Component {
|
onDone: () => void;
|
||||||
static propTypes = {
|
}
|
||||||
onDone: PropTypes.func.isRequired,
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
@replaceableComponent("views.verification.VerificationComplete")
|
||||||
|
export default class VerificationComplete extends React.Component<IProps> {
|
||||||
|
public render(): React.ReactNode {
|
||||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||||
return <div>
|
return <div>
|
||||||
<h2>{_t("Verified!")}</h2>
|
<h2>{_t("Verified!")}</h2>
|
|
@ -1,68 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { _t } from '../../../languageHandler';
|
|
||||||
import AccessibleButton from "../elements/AccessibleButton";
|
|
||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
|
||||||
import VerificationQRCode from "../elements/crypto/VerificationQRCode";
|
|
||||||
import Spinner from "../elements/Spinner";
|
|
||||||
import { SCAN_QR_CODE_METHOD } from "matrix-js-sdk/src/crypto/verification/QRCode";
|
|
||||||
|
|
||||||
@replaceableComponent("views.verification.VerificationQREmojiOptions")
|
|
||||||
export default class VerificationQREmojiOptions extends React.Component {
|
|
||||||
static propTypes = {
|
|
||||||
request: PropTypes.object.isRequired,
|
|
||||||
onCancel: PropTypes.func.isRequired,
|
|
||||||
onStartEmoji: PropTypes.func.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { request } = this.props;
|
|
||||||
const showQR = request.otherPartySupportsMethod(SCAN_QR_CODE_METHOD);
|
|
||||||
|
|
||||||
let qrCode;
|
|
||||||
if (showQR) {
|
|
||||||
qrCode = <VerificationQRCode qrCodeData={request.qrCodeData} />;
|
|
||||||
} else {
|
|
||||||
qrCode = <div className='mx_VerificationQREmojiOptions_noQR'><Spinner /></div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{_t("Verify this session by completing one of the following:")}
|
|
||||||
<div className='mx_IncomingSasDialog_startOptions'>
|
|
||||||
<div className='mx_IncomingSasDialog_startOption'>
|
|
||||||
<p>{_t("Scan this unique code")}</p>
|
|
||||||
{qrCode}
|
|
||||||
</div>
|
|
||||||
<div className='mx_IncomingSasDialog_betweenText'>{_t("or")}</div>
|
|
||||||
<div className='mx_IncomingSasDialog_startOption'>
|
|
||||||
<p>{_t("Compare unique emoji")}</p>
|
|
||||||
<span className='mx_IncomingSasDialog_helpText'>{_t("Compare a unique set of emoji if you don't have a camera on either device")}</span>
|
|
||||||
<AccessibleButton onClick={this.props.onStartEmoji} kind='primary'>
|
|
||||||
{_t("Start")}
|
|
||||||
</AccessibleButton>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<AccessibleButton onClick={this.props.onCancel} kind='danger'>
|
|
||||||
{_t("Cancel")}
|
|
||||||
</AccessibleButton>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -15,7 +15,8 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import { SAS } from "matrix-js-sdk/src/crypto/verification/SAS";
|
||||||
|
import { DeviceInfo } from "matrix-js-sdk/src//crypto/deviceinfo";
|
||||||
import { _t, _td } from '../../../languageHandler';
|
import { _t, _td } from '../../../languageHandler';
|
||||||
import { PendingActionSpinner } from "../right_panel/EncryptionInfo";
|
import { PendingActionSpinner } from "../right_panel/EncryptionInfo";
|
||||||
import AccessibleButton from "../elements/AccessibleButton";
|
import AccessibleButton from "../elements/AccessibleButton";
|
||||||
|
@ -23,24 +24,29 @@ import DialogButtons from "../elements/DialogButtons";
|
||||||
import { fixupColorFonts } from '../../../utils/FontManager';
|
import { fixupColorFonts } from '../../../utils/FontManager';
|
||||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
pending?: boolean;
|
||||||
|
displayName?: string; // required if pending is true
|
||||||
|
device?: DeviceInfo;
|
||||||
|
onDone: () => void;
|
||||||
|
onCancel: () => void;
|
||||||
|
sas: SAS.sas;
|
||||||
|
isSelf?: boolean;
|
||||||
|
inDialog?: boolean; // whether this component is being shown in a dialog and to use DialogButtons
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IState {
|
||||||
|
pending: boolean;
|
||||||
|
cancelling?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
function capFirst(s) {
|
function capFirst(s) {
|
||||||
return s.charAt(0).toUpperCase() + s.slice(1);
|
return s.charAt(0).toUpperCase() + s.slice(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@replaceableComponent("views.verification.VerificationShowSas")
|
@replaceableComponent("views.verification.VerificationShowSas")
|
||||||
export default class VerificationShowSas extends React.Component {
|
export default class VerificationShowSas extends React.Component<IProps, IState> {
|
||||||
static propTypes = {
|
constructor(props: IProps) {
|
||||||
pending: PropTypes.bool,
|
|
||||||
displayName: PropTypes.string, // required if pending is true
|
|
||||||
device: PropTypes.object,
|
|
||||||
onDone: PropTypes.func.isRequired,
|
|
||||||
onCancel: PropTypes.func.isRequired,
|
|
||||||
sas: PropTypes.object.isRequired,
|
|
||||||
isSelf: PropTypes.bool,
|
|
||||||
inDialog: PropTypes.bool, // whether this component is being shown in a dialog and to use DialogButtons
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
|
@ -48,19 +54,19 @@ export default class VerificationShowSas extends React.Component {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillMount() {
|
public componentWillMount(): void {
|
||||||
// As this component is also used before login (during complete security),
|
// As this component is also used before login (during complete security),
|
||||||
// also make sure we have a working emoji font to display the SAS emojis here.
|
// also make sure we have a working emoji font to display the SAS emojis here.
|
||||||
// This is also done from LoggedInView.
|
// This is also done from LoggedInView.
|
||||||
fixupColorFonts();
|
fixupColorFonts();
|
||||||
}
|
}
|
||||||
|
|
||||||
onMatchClick = () => {
|
private onMatchClick = (): void => {
|
||||||
this.setState({ pending: true });
|
this.setState({ pending: true });
|
||||||
this.props.onDone();
|
this.props.onDone();
|
||||||
};
|
};
|
||||||
|
|
||||||
onDontMatchClick = () => {
|
private onDontMatchClick = (): void => {
|
||||||
this.setState({ cancelling: true });
|
this.setState({ cancelling: true });
|
||||||
this.props.onCancel();
|
this.props.onCancel();
|
||||||
};
|
};
|
|
@ -840,8 +840,8 @@
|
||||||
"Enable big emoji in chat": "Enable big emoji in chat",
|
"Enable big emoji in chat": "Enable big emoji in chat",
|
||||||
"Send typing notifications": "Send typing notifications",
|
"Send typing notifications": "Send typing notifications",
|
||||||
"Show typing notifications": "Show typing notifications",
|
"Show typing notifications": "Show typing notifications",
|
||||||
"Use Command + F to search": "Use Command + F to search",
|
"Use Command + F to search timeline": "Use Command + F to search timeline",
|
||||||
"Use Ctrl + F to search": "Use Ctrl + F to search",
|
"Use Ctrl + F to search timeline": "Use Ctrl + F to search timeline",
|
||||||
"Use Command + Enter to send a message": "Use Command + Enter to send a message",
|
"Use Command + Enter to send a message": "Use Command + Enter to send a message",
|
||||||
"Use Ctrl + Enter to send a message": "Use Ctrl + Enter to send a message",
|
"Use Ctrl + Enter to send a message": "Use Ctrl + Enter to send a message",
|
||||||
"Automatically replace plain text Emoji": "Automatically replace plain text Emoji",
|
"Automatically replace plain text Emoji": "Automatically replace plain text Emoji",
|
||||||
|
@ -923,12 +923,6 @@
|
||||||
"You've successfully verified this user.": "You've successfully verified this user.",
|
"You've successfully verified this user.": "You've successfully verified this user.",
|
||||||
"Secure messages with this user are end-to-end encrypted and not able to be read by third parties.": "Secure messages with this user are end-to-end encrypted and not able to be read by third parties.",
|
"Secure messages with this user are end-to-end encrypted and not able to be read by third parties.": "Secure messages with this user are end-to-end encrypted and not able to be read by third parties.",
|
||||||
"Got It": "Got It",
|
"Got It": "Got It",
|
||||||
"Verify this session by completing one of the following:": "Verify this session by completing one of the following:",
|
|
||||||
"Scan this unique code": "Scan this unique code",
|
|
||||||
"or": "or",
|
|
||||||
"Compare unique emoji": "Compare unique emoji",
|
|
||||||
"Compare a unique set of emoji if you don't have a camera on either device": "Compare a unique set of emoji if you don't have a camera on either device",
|
|
||||||
"Start": "Start",
|
|
||||||
"Confirm the emoji below are displayed on both sessions, in the same order:": "Confirm the emoji below are displayed on both sessions, in the same order:",
|
"Confirm the emoji below are displayed on both sessions, in the same order:": "Confirm the emoji below are displayed on both sessions, in the same order:",
|
||||||
"Verify this user by confirming the following emoji appear on their screen.": "Verify this user by confirming the following emoji appear on their screen.",
|
"Verify this user by confirming the following emoji appear on their screen.": "Verify this user by confirming the following emoji appear on their screen.",
|
||||||
"Verify this session by confirming the following number appears on its screen.": "Verify this session by confirming the following number appears on its screen.",
|
"Verify this session by confirming the following number appears on its screen.": "Verify this session by confirming the following number appears on its screen.",
|
||||||
|
@ -1339,7 +1333,12 @@
|
||||||
"Show tray icon and minimize window to it on close": "Show tray icon and minimize window to it on close",
|
"Show tray icon and minimize window to it on close": "Show tray icon and minimize window to it on close",
|
||||||
"Preferences": "Preferences",
|
"Preferences": "Preferences",
|
||||||
"Room list": "Room list",
|
"Room list": "Room list",
|
||||||
|
"Keyboard shortcuts": "Keyboard shortcuts",
|
||||||
|
"To view all keyboard shortcuts, click here.": "To view all keyboard shortcuts, click here.",
|
||||||
|
"Displaying time": "Displaying time",
|
||||||
"Composer": "Composer",
|
"Composer": "Composer",
|
||||||
|
"Code blocks": "Code blocks",
|
||||||
|
"Images, GIFs and videos": "Images, GIFs and videos",
|
||||||
"Timeline": "Timeline",
|
"Timeline": "Timeline",
|
||||||
"Autocomplete delay (ms)": "Autocomplete delay (ms)",
|
"Autocomplete delay (ms)": "Autocomplete delay (ms)",
|
||||||
"Read Marker lifetime (ms)": "Read Marker lifetime (ms)",
|
"Read Marker lifetime (ms)": "Read Marker lifetime (ms)",
|
||||||
|
@ -1828,6 +1827,12 @@
|
||||||
"Edit devices": "Edit devices",
|
"Edit devices": "Edit devices",
|
||||||
"Security": "Security",
|
"Security": "Security",
|
||||||
"The session you are trying to verify doesn't support scanning a QR code or emoji verification, which is what %(brand)s supports. Try with a different client.": "The session you are trying to verify doesn't support scanning a QR code or emoji verification, which is what %(brand)s supports. Try with a different client.",
|
"The session you are trying to verify doesn't support scanning a QR code or emoji verification, which is what %(brand)s supports. Try with a different client.": "The session you are trying to verify doesn't support scanning a QR code or emoji verification, which is what %(brand)s supports. Try with a different client.",
|
||||||
|
"Scan this unique code": "Scan this unique code",
|
||||||
|
"Compare unique emoji": "Compare unique emoji",
|
||||||
|
"Compare a unique set of emoji if you don't have a camera on either device": "Compare a unique set of emoji if you don't have a camera on either device",
|
||||||
|
"Start": "Start",
|
||||||
|
"or": "or",
|
||||||
|
"Verify this session by completing one of the following:": "Verify this session by completing one of the following:",
|
||||||
"Verify by scanning": "Verify by scanning",
|
"Verify by scanning": "Verify by scanning",
|
||||||
"Ask %(displayName)s to scan your code:": "Ask %(displayName)s to scan your code:",
|
"Ask %(displayName)s to scan your code:": "Ask %(displayName)s to scan your code:",
|
||||||
"If you can't scan the code above, verify by comparing unique emoji.": "If you can't scan the code above, verify by comparing unique emoji.",
|
"If you can't scan the code above, verify by comparing unique emoji.": "If you can't scan the code above, verify by comparing unique emoji.",
|
||||||
|
|
|
@ -20,15 +20,25 @@ import { StandardActions } from "./StandardActions";
|
||||||
import { PushRuleVectorState } from "./PushRuleVectorState";
|
import { PushRuleVectorState } from "./PushRuleVectorState";
|
||||||
import { NotificationUtils } from "./NotificationUtils";
|
import { NotificationUtils } from "./NotificationUtils";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
kind: Kind;
|
||||||
|
description: string;
|
||||||
|
vectorStateToActions: Action;
|
||||||
|
}
|
||||||
|
|
||||||
class VectorPushRuleDefinition {
|
class VectorPushRuleDefinition {
|
||||||
constructor(opts) {
|
private kind: Kind;
|
||||||
|
private description: string;
|
||||||
|
private vectorStateToActions: Action;
|
||||||
|
|
||||||
|
constructor(opts: IProps) {
|
||||||
this.kind = opts.kind;
|
this.kind = opts.kind;
|
||||||
this.description = opts.description;
|
this.description = opts.description;
|
||||||
this.vectorStateToActions = opts.vectorStateToActions;
|
this.vectorStateToActions = opts.vectorStateToActions;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Translate the rule actions and its enabled value into vector state
|
// Translate the rule actions and its enabled value into vector state
|
||||||
ruleToVectorState(rule) {
|
public ruleToVectorState(rule): VectorPushRuleDefinition {
|
||||||
let enabled = false;
|
let enabled = false;
|
||||||
if (rule) {
|
if (rule) {
|
||||||
enabled = rule.enabled;
|
enabled = rule.enabled;
|
||||||
|
@ -63,13 +73,24 @@ class VectorPushRuleDefinition {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum Kind {
|
||||||
|
Override = "override",
|
||||||
|
Underride = "underride",
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Action {
|
||||||
|
on: StandardActions;
|
||||||
|
loud: StandardActions;
|
||||||
|
off: StandardActions;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The descriptions of rules managed by the Vector UI.
|
* The descriptions of rules managed by the Vector UI.
|
||||||
*/
|
*/
|
||||||
export const VectorPushRulesDefinitions = {
|
export const VectorPushRulesDefinitions = {
|
||||||
// Messages containing user's display name
|
// Messages containing user's display name
|
||||||
".m.rule.contains_display_name": new VectorPushRuleDefinition({
|
".m.rule.contains_display_name": new VectorPushRuleDefinition({
|
||||||
kind: "override",
|
kind: Kind.Override,
|
||||||
description: _td("Messages containing my display name"), // passed through _t() translation in src/components/views/settings/Notifications.js
|
description: _td("Messages containing my display name"), // passed through _t() translation in src/components/views/settings/Notifications.js
|
||||||
vectorStateToActions: { // The actions for each vector state, or null to disable the rule.
|
vectorStateToActions: { // The actions for each vector state, or null to disable the rule.
|
||||||
on: StandardActions.ACTION_NOTIFY,
|
on: StandardActions.ACTION_NOTIFY,
|
||||||
|
@ -80,7 +101,7 @@ export const VectorPushRulesDefinitions = {
|
||||||
|
|
||||||
// Messages containing user's username (localpart/MXID)
|
// Messages containing user's username (localpart/MXID)
|
||||||
".m.rule.contains_user_name": new VectorPushRuleDefinition({
|
".m.rule.contains_user_name": new VectorPushRuleDefinition({
|
||||||
kind: "override",
|
kind: Kind.Override,
|
||||||
description: _td("Messages containing my username"), // passed through _t() translation in src/components/views/settings/Notifications.js
|
description: _td("Messages containing my username"), // passed through _t() translation in src/components/views/settings/Notifications.js
|
||||||
vectorStateToActions: { // The actions for each vector state, or null to disable the rule.
|
vectorStateToActions: { // The actions for each vector state, or null to disable the rule.
|
||||||
on: StandardActions.ACTION_NOTIFY,
|
on: StandardActions.ACTION_NOTIFY,
|
||||||
|
@ -91,7 +112,7 @@ export const VectorPushRulesDefinitions = {
|
||||||
|
|
||||||
// Messages containing @room
|
// Messages containing @room
|
||||||
".m.rule.roomnotif": new VectorPushRuleDefinition({
|
".m.rule.roomnotif": new VectorPushRuleDefinition({
|
||||||
kind: "override",
|
kind: Kind.Override,
|
||||||
description: _td("Messages containing @room"), // passed through _t() translation in src/components/views/settings/Notifications.js
|
description: _td("Messages containing @room"), // passed through _t() translation in src/components/views/settings/Notifications.js
|
||||||
vectorStateToActions: { // The actions for each vector state, or null to disable the rule.
|
vectorStateToActions: { // The actions for each vector state, or null to disable the rule.
|
||||||
on: StandardActions.ACTION_NOTIFY,
|
on: StandardActions.ACTION_NOTIFY,
|
||||||
|
@ -102,7 +123,7 @@ export const VectorPushRulesDefinitions = {
|
||||||
|
|
||||||
// Messages just sent to the user in a 1:1 room
|
// Messages just sent to the user in a 1:1 room
|
||||||
".m.rule.room_one_to_one": new VectorPushRuleDefinition({
|
".m.rule.room_one_to_one": new VectorPushRuleDefinition({
|
||||||
kind: "underride",
|
kind: Kind.Underride,
|
||||||
description: _td("Messages in one-to-one chats"), // passed through _t() translation in src/components/views/settings/Notifications.js
|
description: _td("Messages in one-to-one chats"), // passed through _t() translation in src/components/views/settings/Notifications.js
|
||||||
vectorStateToActions: {
|
vectorStateToActions: {
|
||||||
on: StandardActions.ACTION_NOTIFY,
|
on: StandardActions.ACTION_NOTIFY,
|
||||||
|
@ -113,7 +134,7 @@ export const VectorPushRulesDefinitions = {
|
||||||
|
|
||||||
// Encrypted messages just sent to the user in a 1:1 room
|
// Encrypted messages just sent to the user in a 1:1 room
|
||||||
".m.rule.encrypted_room_one_to_one": new VectorPushRuleDefinition({
|
".m.rule.encrypted_room_one_to_one": new VectorPushRuleDefinition({
|
||||||
kind: "underride",
|
kind: Kind.Underride,
|
||||||
description: _td("Encrypted messages in one-to-one chats"), // passed through _t() translation in src/components/views/settings/Notifications.js
|
description: _td("Encrypted messages in one-to-one chats"), // passed through _t() translation in src/components/views/settings/Notifications.js
|
||||||
vectorStateToActions: {
|
vectorStateToActions: {
|
||||||
on: StandardActions.ACTION_NOTIFY,
|
on: StandardActions.ACTION_NOTIFY,
|
||||||
|
@ -126,7 +147,7 @@ export const VectorPushRulesDefinitions = {
|
||||||
// 1:1 room messages are catched by the .m.rule.room_one_to_one rule if any defined
|
// 1:1 room messages are catched by the .m.rule.room_one_to_one rule if any defined
|
||||||
// By opposition, all other room messages are from group chat rooms.
|
// By opposition, all other room messages are from group chat rooms.
|
||||||
".m.rule.message": new VectorPushRuleDefinition({
|
".m.rule.message": new VectorPushRuleDefinition({
|
||||||
kind: "underride",
|
kind: Kind.Underride,
|
||||||
description: _td("Messages in group chats"), // passed through _t() translation in src/components/views/settings/Notifications.js
|
description: _td("Messages in group chats"), // passed through _t() translation in src/components/views/settings/Notifications.js
|
||||||
vectorStateToActions: {
|
vectorStateToActions: {
|
||||||
on: StandardActions.ACTION_NOTIFY,
|
on: StandardActions.ACTION_NOTIFY,
|
||||||
|
@ -139,7 +160,7 @@ export const VectorPushRulesDefinitions = {
|
||||||
// Encrypted 1:1 room messages are catched by the .m.rule.encrypted_room_one_to_one rule if any defined
|
// Encrypted 1:1 room messages are catched by the .m.rule.encrypted_room_one_to_one rule if any defined
|
||||||
// By opposition, all other room messages are from group chat rooms.
|
// By opposition, all other room messages are from group chat rooms.
|
||||||
".m.rule.encrypted": new VectorPushRuleDefinition({
|
".m.rule.encrypted": new VectorPushRuleDefinition({
|
||||||
kind: "underride",
|
kind: Kind.Underride,
|
||||||
description: _td("Encrypted messages in group chats"), // passed through _t() translation in src/components/views/settings/Notifications.js
|
description: _td("Encrypted messages in group chats"), // passed through _t() translation in src/components/views/settings/Notifications.js
|
||||||
vectorStateToActions: {
|
vectorStateToActions: {
|
||||||
on: StandardActions.ACTION_NOTIFY,
|
on: StandardActions.ACTION_NOTIFY,
|
||||||
|
@ -150,7 +171,7 @@ export const VectorPushRulesDefinitions = {
|
||||||
|
|
||||||
// Invitation for the user
|
// Invitation for the user
|
||||||
".m.rule.invite_for_me": new VectorPushRuleDefinition({
|
".m.rule.invite_for_me": new VectorPushRuleDefinition({
|
||||||
kind: "underride",
|
kind: Kind.Underride,
|
||||||
description: _td("When I'm invited to a room"), // passed through _t() translation in src/components/views/settings/Notifications.js
|
description: _td("When I'm invited to a room"), // passed through _t() translation in src/components/views/settings/Notifications.js
|
||||||
vectorStateToActions: {
|
vectorStateToActions: {
|
||||||
on: StandardActions.ACTION_NOTIFY,
|
on: StandardActions.ACTION_NOTIFY,
|
||||||
|
@ -161,7 +182,7 @@ export const VectorPushRulesDefinitions = {
|
||||||
|
|
||||||
// Incoming call
|
// Incoming call
|
||||||
".m.rule.call": new VectorPushRuleDefinition({
|
".m.rule.call": new VectorPushRuleDefinition({
|
||||||
kind: "underride",
|
kind: Kind.Underride,
|
||||||
description: _td("Call invitation"), // passed through _t() translation in src/components/views/settings/Notifications.js
|
description: _td("Call invitation"), // passed through _t() translation in src/components/views/settings/Notifications.js
|
||||||
vectorStateToActions: {
|
vectorStateToActions: {
|
||||||
on: StandardActions.ACTION_NOTIFY,
|
on: StandardActions.ACTION_NOTIFY,
|
||||||
|
@ -172,7 +193,7 @@ export const VectorPushRulesDefinitions = {
|
||||||
|
|
||||||
// Notifications from bots
|
// Notifications from bots
|
||||||
".m.rule.suppress_notices": new VectorPushRuleDefinition({
|
".m.rule.suppress_notices": new VectorPushRuleDefinition({
|
||||||
kind: "override",
|
kind: Kind.Override,
|
||||||
description: _td("Messages sent by bot"), // passed through _t() translation in src/components/views/settings/Notifications.js
|
description: _td("Messages sent by bot"), // passed through _t() translation in src/components/views/settings/Notifications.js
|
||||||
vectorStateToActions: {
|
vectorStateToActions: {
|
||||||
// .m.rule.suppress_notices is a "negative" rule, we have to invert its enabled value for vector UI
|
// .m.rule.suppress_notices is a "negative" rule, we have to invert its enabled value for vector UI
|
||||||
|
@ -184,7 +205,7 @@ export const VectorPushRulesDefinitions = {
|
||||||
|
|
||||||
// Room upgrades (tombstones)
|
// Room upgrades (tombstones)
|
||||||
".m.rule.tombstone": new VectorPushRuleDefinition({
|
".m.rule.tombstone": new VectorPushRuleDefinition({
|
||||||
kind: "override",
|
kind: Kind.Override,
|
||||||
description: _td("When rooms are upgraded"), // passed through _t() translation in src/components/views/settings/Notifications.js
|
description: _td("When rooms are upgraded"), // passed through _t() translation in src/components/views/settings/Notifications.js
|
||||||
vectorStateToActions: { // The actions for each vector state, or null to disable the rule.
|
vectorStateToActions: { // The actions for each vector state, or null to disable the rule.
|
||||||
on: StandardActions.ACTION_NOTIFY,
|
on: StandardActions.ACTION_NOTIFY,
|
|
@ -93,15 +93,15 @@ async function collectBugReport(opts: IOpts = {}, gzipLogs = true) {
|
||||||
body.append("cross_signing_supported_by_hs",
|
body.append("cross_signing_supported_by_hs",
|
||||||
String(await client.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing")));
|
String(await client.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing")));
|
||||||
body.append("cross_signing_key", crossSigning.getId());
|
body.append("cross_signing_key", crossSigning.getId());
|
||||||
body.append("cross_signing_pk_in_secret_storage",
|
body.append("cross_signing_privkey_in_secret_storage",
|
||||||
String(!!(await crossSigning.isStoredInSecretStorage(secretStorage))));
|
String(!!(await crossSigning.isStoredInSecretStorage(secretStorage))));
|
||||||
|
|
||||||
const pkCache = client.getCrossSigningCacheCallbacks();
|
const pkCache = client.getCrossSigningCacheCallbacks();
|
||||||
body.append("cross_signing_master_pk_cached",
|
body.append("cross_signing_master_privkey_cached",
|
||||||
String(!!(pkCache && await pkCache.getCrossSigningKeyCache("master"))));
|
String(!!(pkCache && await pkCache.getCrossSigningKeyCache("master"))));
|
||||||
body.append("cross_signing_self_signing_pk_cached",
|
body.append("cross_signing_self_signing_privkey_cached",
|
||||||
String(!!(pkCache && await pkCache.getCrossSigningKeyCache("self_signing"))));
|
String(!!(pkCache && await pkCache.getCrossSigningKeyCache("self_signing"))));
|
||||||
body.append("cross_signing_user_signing_pk_cached",
|
body.append("cross_signing_user_signing_privkey_cached",
|
||||||
String(!!(pkCache && await pkCache.getCrossSigningKeyCache("user_signing"))));
|
String(!!(pkCache && await pkCache.getCrossSigningKeyCache("user_signing"))));
|
||||||
|
|
||||||
body.append("secret_storage_ready", String(await client.isSecretStorageReady()));
|
body.append("secret_storage_ready", String(await client.isSecretStorageReady()));
|
||||||
|
|
|
@ -455,7 +455,7 @@ export const SETTINGS: {[setting: string]: ISetting} = {
|
||||||
},
|
},
|
||||||
"ctrlFForSearch": {
|
"ctrlFForSearch": {
|
||||||
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
|
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
|
||||||
displayName: isMac ? _td("Use Command + F to search") : _td("Use Ctrl + F to search"),
|
displayName: isMac ? _td("Use Command + F to search timeline") : _td("Use Ctrl + F to search timeline"),
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
"MessageComposerInput.ctrlEnterToSend": {
|
"MessageComposerInput.ctrlEnterToSend": {
|
||||||
|
|
|
@ -1,50 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2017 New Vector Ltd
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stores where the user has scrolled to in each room
|
|
||||||
*/
|
|
||||||
class RoomScrollStateStore {
|
|
||||||
constructor() {
|
|
||||||
// A map from room id to scroll state.
|
|
||||||
//
|
|
||||||
// If there is no special scroll state (ie, we are following the live
|
|
||||||
// timeline), the scroll state is null. Otherwise, it is an object with
|
|
||||||
// the following properties:
|
|
||||||
//
|
|
||||||
// focussedEvent: the ID of the 'focussed' event. Typically this is
|
|
||||||
// the last event fully visible in the viewport, though if we
|
|
||||||
// have done an explicit scroll to an explicit event, it will be
|
|
||||||
// that event.
|
|
||||||
//
|
|
||||||
// pixelOffset: the number of pixels the window is scrolled down
|
|
||||||
// from the focussedEvent.
|
|
||||||
this._scrollStateMap = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
getScrollState(roomId) {
|
|
||||||
return this._scrollStateMap[roomId];
|
|
||||||
}
|
|
||||||
|
|
||||||
setScrollState(roomId, scrollState) {
|
|
||||||
this._scrollStateMap[roomId] = scrollState;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (global.mx_RoomScrollStateStore === undefined) {
|
|
||||||
global.mx_RoomScrollStateStore = new RoomScrollStateStore();
|
|
||||||
}
|
|
||||||
export default global.mx_RoomScrollStateStore;
|
|
53
src/stores/RoomScrollStateStore.ts
Normal file
53
src/stores/RoomScrollStateStore.ts
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
Copyright 2017 New Vector Ltd
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface ScrollState {
|
||||||
|
focussedEvent: string;
|
||||||
|
pixelOffset: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores where the user has scrolled to in each room
|
||||||
|
*/
|
||||||
|
export class RoomScrollStateStore {
|
||||||
|
// A map from room id to scroll state.
|
||||||
|
//
|
||||||
|
// If there is no special scroll state (ie, we are following the live
|
||||||
|
// timeline), the scroll state is null. Otherwise, it is an object with
|
||||||
|
// the following properties:
|
||||||
|
//
|
||||||
|
// focussedEvent: the ID of the 'focussed' event. Typically this is
|
||||||
|
// the last event fully visible in the viewport, though if we
|
||||||
|
// have done an explicit scroll to an explicit event, it will be
|
||||||
|
// that event.
|
||||||
|
//
|
||||||
|
// pixelOffset: the number of pixels the window is scrolled down
|
||||||
|
// from the focussedEvent.
|
||||||
|
private scrollStateMap = new Map<string, ScrollState>();
|
||||||
|
|
||||||
|
public getScrollState(roomId: string): ScrollState {
|
||||||
|
return this.scrollStateMap.get(roomId);
|
||||||
|
}
|
||||||
|
|
||||||
|
setScrollState(roomId: string, scrollState: ScrollState): void {
|
||||||
|
this.scrollStateMap.set(roomId, scrollState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window.mxRoomScrollStateStore === undefined) {
|
||||||
|
window.mxRoomScrollStateStore = new RoomScrollStateStore();
|
||||||
|
}
|
||||||
|
export default window.mxRoomScrollStateStore;
|
27
yarn.lock
27
yarn.lock
|
@ -1677,6 +1677,11 @@
|
||||||
"@types/scheduler" "*"
|
"@types/scheduler" "*"
|
||||||
csstype "^3.0.2"
|
csstype "^3.0.2"
|
||||||
|
|
||||||
|
"@types/retry@^0.12.0":
|
||||||
|
version "0.12.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d"
|
||||||
|
integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==
|
||||||
|
|
||||||
"@types/sanitize-html@^2.3.1":
|
"@types/sanitize-html@^2.3.1":
|
||||||
version "2.3.1"
|
version "2.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/@types/sanitize-html/-/sanitize-html-2.3.1.tgz#094d696b83b7394b016e96342bbffa6a028795ce"
|
resolved "https://registry.yarnpkg.com/@types/sanitize-html/-/sanitize-html-2.3.1.tgz#094d696b83b7394b016e96342bbffa6a028795ce"
|
||||||
|
@ -5445,10 +5450,10 @@ mathml-tag-names@^2.1.3:
|
||||||
resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz#4ddadd67308e780cf16a47685878ee27b736a0a3"
|
resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz#4ddadd67308e780cf16a47685878ee27b736a0a3"
|
||||||
integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==
|
integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==
|
||||||
|
|
||||||
matrix-js-sdk@12.0.0:
|
matrix-js-sdk@12.0.1:
|
||||||
version "12.0.0"
|
version "12.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-12.0.0.tgz#8ee7cc37661476341d0c792a1a12bc78b19f9fdd"
|
resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-12.0.1.tgz#3a63881f743420a4d39474daa39bd0fb90930d43"
|
||||||
integrity sha512-DHeq87Sx9Dv37FYyvZkmA1VYsQUNaVgc3QzMUkFwoHt1T4EZzgyYpdsp3uYruJzUW0ACvVJcwFdrU4e1VS97dQ==
|
integrity sha512-HkOWv8QHojceo3kPbC+vAIFUjsRAig6MBvEY35UygS3g2dL0UcJ5Qx09/2wcXtu6dowlDnWsz2HHk62tS2cklA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.12.5"
|
"@babel/runtime" "^7.12.5"
|
||||||
another-json "^0.2.0"
|
another-json "^0.2.0"
|
||||||
|
@ -5456,6 +5461,7 @@ matrix-js-sdk@12.0.0:
|
||||||
bs58 "^4.0.1"
|
bs58 "^4.0.1"
|
||||||
content-type "^1.0.4"
|
content-type "^1.0.4"
|
||||||
loglevel "^1.7.1"
|
loglevel "^1.7.1"
|
||||||
|
p-retry "^4.5.0"
|
||||||
qs "^6.9.6"
|
qs "^6.9.6"
|
||||||
request "^2.88.2"
|
request "^2.88.2"
|
||||||
unhomoglyph "^1.0.6"
|
unhomoglyph "^1.0.6"
|
||||||
|
@ -6007,6 +6013,14 @@ p-locate@^4.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
p-limit "^2.2.0"
|
p-limit "^2.2.0"
|
||||||
|
|
||||||
|
p-retry@^4.5.0:
|
||||||
|
version "4.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.6.0.tgz#9de15ae696278cffe86fce2d8f73b7f894f8bc9e"
|
||||||
|
integrity sha512-SAHbQEwg3X5DRNaLmWjT+DlGc93ba5i+aP3QLfVNDncQEQO4xjbYW4N/lcVTSuP0aJietGfx2t94dJLzfBMpXw==
|
||||||
|
dependencies:
|
||||||
|
"@types/retry" "^0.12.0"
|
||||||
|
retry "^0.13.1"
|
||||||
|
|
||||||
p-try@^2.0.0:
|
p-try@^2.0.0:
|
||||||
version "2.2.0"
|
version "2.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
|
resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
|
||||||
|
@ -6816,6 +6830,11 @@ ret@~0.1.10:
|
||||||
resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc"
|
resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc"
|
||||||
integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==
|
integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==
|
||||||
|
|
||||||
|
retry@^0.13.1:
|
||||||
|
version "0.13.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658"
|
||||||
|
integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==
|
||||||
|
|
||||||
reusify@^1.0.4:
|
reusify@^1.0.4:
|
||||||
version "1.0.4"
|
version "1.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
|
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue