mirror of https://github.com/vector-im/riot-web
Merge remote-tracking branch 'upstream/develop' into burn-sdk-get-comp-with-fire
Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com>pull/21833/head
commit
f91b35a0a0
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)
|
||||
=====================================================================================================
|
||||
[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",
|
||||
"version": "3.24.0",
|
||||
"version": "3.25.0",
|
||||
"description": "SDK for matrix.org using React",
|
||||
"author": "matrix.org",
|
||||
"repository": {
|
||||
|
@ -80,7 +80,7 @@
|
|||
"katex": "^0.12.0",
|
||||
"linkifyjs": "^2.1.9",
|
||||
"lodash": "^4.17.20",
|
||||
"matrix-js-sdk": "12.0.0",
|
||||
"matrix-js-sdk": "12.0.1",
|
||||
"matrix-widget-api": "^0.1.0-beta.15",
|
||||
"minimist": "^1.2.5",
|
||||
"opus-recorder": "^8.0.3",
|
||||
|
|
|
@ -57,7 +57,6 @@
|
|||
@import "./views/avatars/_BaseAvatar.scss";
|
||||
@import "./views/avatars/_DecoratedRoomAvatar.scss";
|
||||
@import "./views/avatars/_MemberStatusMessageAvatar.scss";
|
||||
@import "./views/avatars/_PulsedAvatar.scss";
|
||||
@import "./views/avatars/_WidgetAvatar.scss";
|
||||
@import "./views/beta/_BetaCard.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);
|
||||
animation: mx_RightPanel_indicator_pulse 2s infinite;
|
||||
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 {
|
||||
0% {
|
||||
transform: scale(0.95);
|
||||
box-shadow: 0 0 0 0 rgba($pulse-color, 0.7);
|
||||
}
|
||||
|
||||
70% {
|
||||
transform: scale(1);
|
||||
box-shadow: 0 0 0 10px rgba($pulse-color, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
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 {
|
||||
from {
|
||||
width: 0px;
|
||||
transform: scaleX(0);
|
||||
}
|
||||
to {
|
||||
width: 32px;
|
||||
transform: scaleX(1);
|
||||
}
|
||||
}
|
||||
|
||||
.mx_RoomView_fileDropTarget_image {
|
||||
width: 32px;
|
||||
animation: mx_RoomView_fileDropTarget_image_animation;
|
||||
animation-duration: 0.5s;
|
||||
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;
|
||||
transform: scale(1);
|
||||
background: rgba($pulse-color, 1);
|
||||
box-shadow: 0 0 0 0 rgba($pulse-color, 1);
|
||||
animation: mx_Beta_bluePulse 2s infinite;
|
||||
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 {
|
||||
0% {
|
||||
transform: scale(0.95);
|
||||
box-shadow: 0 0 0 0 rgba($pulse-color, 0.7);
|
||||
}
|
||||
|
||||
70% {
|
||||
transform: scale(1);
|
||||
box-shadow: 0 0 0 10px rgba($pulse-color, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
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;
|
||||
top: 2px; // alignment
|
||||
background-image: url("$(res)/img/element-icons/warning-badge.svg");
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.mx_AccessSecretStorageDialog_reset_link {
|
||||
|
|
|
@ -46,6 +46,7 @@ import { VoiceRecordingStore } from "../stores/VoiceRecordingStore";
|
|||
import PerformanceMonitor from "../performance";
|
||||
import UIStore from "../stores/UIStore";
|
||||
import { SetupEncryptionStore } from "../stores/SetupEncryptionStore";
|
||||
import { RoomScrollStateStore } from "../stores/RoomScrollStateStore";
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
|
@ -87,6 +88,7 @@ declare global {
|
|||
mxPerformanceEntryNames: any;
|
||||
mxUIStore: UIStore;
|
||||
mxSetupEncryptionStore?: SetupEncryptionStore;
|
||||
mxRoomScrollStateStore?: RoomScrollStateStore;
|
||||
}
|
||||
|
||||
interface Document {
|
||||
|
|
|
@ -41,7 +41,7 @@ import eventSearch, { searchPagination } from '../../Searching';
|
|||
import MainSplit from './MainSplit';
|
||||
import RightPanel from './RightPanel';
|
||||
import RoomViewStore from '../../stores/RoomViewStore';
|
||||
import RoomScrollStateStore from '../../stores/RoomScrollStateStore';
|
||||
import RoomScrollStateStore, { ScrollState } from '../../stores/RoomScrollStateStore';
|
||||
import WidgetEchoStore from '../../stores/WidgetEchoStore';
|
||||
import SettingsStore from "../../settings/SettingsStore";
|
||||
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
|
||||
// restored when we switch back to it.
|
||||
//
|
||||
private getScrollState() {
|
||||
private getScrollState(): ScrollState {
|
||||
const messagePanel = this.messagePanel;
|
||||
if (!messagePanel) return null;
|
||||
|
||||
|
|
|
@ -15,39 +15,42 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import * as sdk from '../../../index';
|
||||
import { SetupEncryptionStore, Phase } from '../../../stores/SetupEncryptionStore';
|
||||
import SetupEncryptionBody from "./SetupEncryptionBody";
|
||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||
|
||||
@replaceableComponent("structures.auth.CompleteSecurity")
|
||||
export default class CompleteSecurity extends React.Component {
|
||||
static propTypes = {
|
||||
onFinished: PropTypes.func.isRequired,
|
||||
};
|
||||
interface IProps {
|
||||
onFinished: () => void;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
interface IState {
|
||||
phase: Phase;
|
||||
}
|
||||
|
||||
@replaceableComponent("structures.auth.CompleteSecurity")
|
||||
export default class CompleteSecurity extends React.Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
const store = SetupEncryptionStore.sharedInstance();
|
||||
store.on("update", this._onStoreUpdate);
|
||||
store.on("update", this.onStoreUpdate);
|
||||
store.start();
|
||||
this.state = { phase: store.phase };
|
||||
}
|
||||
|
||||
_onStoreUpdate = () => {
|
||||
private onStoreUpdate = (): void => {
|
||||
const store = SetupEncryptionStore.sharedInstance();
|
||||
this.setState({ phase: store.phase });
|
||||
};
|
||||
|
||||
componentWillUnmount() {
|
||||
public componentWillUnmount(): void {
|
||||
const store = SetupEncryptionStore.sharedInstance();
|
||||
store.off("update", this._onStoreUpdate);
|
||||
store.off("update", this.onStoreUpdate);
|
||||
store.stop();
|
||||
}
|
||||
|
||||
render() {
|
||||
public render() {
|
||||
const AuthPage = sdk.getComponent("auth.AuthPage");
|
||||
const CompleteSecurityBody = sdk.getComponent("auth.CompleteSecurityBody");
|
||||
const { phase } = this.state;
|
|
@ -15,20 +15,19 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import AuthPage from '../../views/auth/AuthPage';
|
||||
import CompleteSecurityBody from '../../views/auth/CompleteSecurityBody';
|
||||
import CreateCrossSigningDialog from '../../views/dialogs/security/CreateCrossSigningDialog';
|
||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||
|
||||
@replaceableComponent("structures.auth.E2eSetup")
|
||||
export default class E2eSetup extends React.Component {
|
||||
static propTypes = {
|
||||
onFinished: PropTypes.func.isRequired,
|
||||
accountPassword: PropTypes.string,
|
||||
tokenLogin: PropTypes.bool,
|
||||
};
|
||||
interface IProps {
|
||||
onFinished: () => void;
|
||||
accountPassword?: string;
|
||||
tokenLogin?: boolean;
|
||||
}
|
||||
|
||||
@replaceableComponent("structures.auth.E2eSetup")
|
||||
export default class E2eSetup extends React.Component<IProps> {
|
||||
render() {
|
||||
return (
|
||||
<AuthPage>
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { _t, _td } from '../../../languageHandler';
|
||||
import * as sdk from '../../../index';
|
||||
import Modal from "../../../Modal";
|
||||
|
@ -31,27 +30,50 @@ import PassphraseField from '../../views/auth/PassphraseField';
|
|||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||
import { PASSWORD_MIN_SCORE } from '../../views/auth/RegistrationForm';
|
||||
|
||||
// Phases
|
||||
// Show the forgot password inputs
|
||||
const PHASE_FORGOT = 1;
|
||||
// Email is in the process of being sent
|
||||
const PHASE_SENDING_EMAIL = 2;
|
||||
// Email has been sent
|
||||
const PHASE_EMAIL_SENT = 3;
|
||||
// User has clicked the link in email and completed reset
|
||||
const PHASE_DONE = 4;
|
||||
import { IValidationResult } from "../../views/elements/Validation";
|
||||
|
||||
enum Phase {
|
||||
// Show the forgot password inputs
|
||||
Forgot = 1,
|
||||
// Email is in the process of being sent
|
||||
SendingEmail = 2,
|
||||
// Email has been sent
|
||||
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")
|
||||
export default class ForgotPassword extends React.Component {
|
||||
static propTypes = {
|
||||
serverConfig: PropTypes.instanceOf(ValidatedServerConfig).isRequired,
|
||||
onServerConfigChange: PropTypes.func.isRequired,
|
||||
onLoginClick: PropTypes.func,
|
||||
onComplete: PropTypes.func.isRequired,
|
||||
};
|
||||
export default class ForgotPassword extends React.Component<IProps, IState> {
|
||||
private reset: PasswordReset;
|
||||
|
||||
state = {
|
||||
phase: PHASE_FORGOT,
|
||||
phase: Phase.Forgot,
|
||||
email: "",
|
||||
password: "",
|
||||
password2: "",
|
||||
|
@ -64,30 +86,31 @@ export default class ForgotPassword extends React.Component {
|
|||
serverIsAlive: true,
|
||||
serverErrorIsFatal: false,
|
||||
serverDeadError: "",
|
||||
passwordFieldValid: false,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
CountlyAnalytics.instance.track("onboarding_forgot_password_begin");
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
public componentDidMount() {
|
||||
this.reset = null;
|
||||
this._checkServerLiveliness(this.props.serverConfig);
|
||||
this.checkServerLiveliness(this.props.serverConfig);
|
||||
}
|
||||
|
||||
// TODO: [REACT-WARNING] Replace with appropriate lifecycle event
|
||||
// eslint-disable-next-line camelcase
|
||||
UNSAFE_componentWillReceiveProps(newProps) {
|
||||
public UNSAFE_componentWillReceiveProps(newProps: IProps): void {
|
||||
if (newProps.serverConfig.hsUrl === this.props.serverConfig.hsUrl &&
|
||||
newProps.serverConfig.isUrl === this.props.serverConfig.isUrl) return;
|
||||
|
||||
// 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 {
|
||||
await AutoDiscoveryUtils.validateServerConfigWithStaticUrls(
|
||||
serverConfig.hsUrl,
|
||||
|
@ -98,28 +121,28 @@ export default class ForgotPassword extends React.Component {
|
|||
serverIsAlive: true,
|
||||
});
|
||||
} 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({
|
||||
phase: PHASE_SENDING_EMAIL,
|
||||
phase: Phase.SendingEmail,
|
||||
});
|
||||
this.reset = new PasswordReset(this.props.serverConfig.hsUrl, this.props.serverConfig.isUrl);
|
||||
this.reset.resetPassword(email, password).then(() => {
|
||||
this.setState({
|
||||
phase: PHASE_EMAIL_SENT,
|
||||
phase: Phase.EmailSent,
|
||||
});
|
||||
}, (err) => {
|
||||
this.showErrorDialog(_t('Failed to send email') + ": " + err.message);
|
||||
this.setState({
|
||||
phase: PHASE_FORGOT,
|
||||
phase: Phase.Forgot,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
onVerify = async ev => {
|
||||
private onVerify = async (ev: React.MouseEvent): Promise<void> => {
|
||||
ev.preventDefault();
|
||||
if (!this.reset) {
|
||||
console.error("onVerify called before submitPasswordReset!");
|
||||
|
@ -127,17 +150,17 @@ export default class ForgotPassword extends React.Component {
|
|||
}
|
||||
try {
|
||||
await this.reset.checkEmailLinkClicked();
|
||||
this.setState({ phase: PHASE_DONE });
|
||||
this.setState({ phase: Phase.Done });
|
||||
} catch (err) {
|
||||
this.showErrorDialog(err.message);
|
||||
}
|
||||
};
|
||||
|
||||
onSubmitForm = async ev => {
|
||||
private onSubmitForm = async (ev: React.FormEvent): Promise<void> => {
|
||||
ev.preventDefault();
|
||||
|
||||
// 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 });
|
||||
|
||||
|
@ -172,27 +195,27 @@ export default class ForgotPassword extends React.Component {
|
|||
}
|
||||
};
|
||||
|
||||
onInputChanged = (stateKey, ev) => {
|
||||
private onInputChanged = (stateKey: string, ev: React.FormEvent<HTMLInputElement>) => {
|
||||
this.setState({
|
||||
[stateKey]: ev.target.value,
|
||||
});
|
||||
[stateKey]: ev.currentTarget.value,
|
||||
} as any);
|
||||
};
|
||||
|
||||
onLoginClick = ev => {
|
||||
private onLoginClick = (ev: React.MouseEvent): void => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
this.props.onLoginClick();
|
||||
};
|
||||
|
||||
showErrorDialog(body, title) {
|
||||
public showErrorDialog(description: string, title?: string) {
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createTrackedDialog('Forgot Password Error', '', ErrorDialog, {
|
||||
title: title,
|
||||
description: body,
|
||||
title,
|
||||
description,
|
||||
});
|
||||
}
|
||||
|
||||
onPasswordValidate(result) {
|
||||
private onPasswordValidate(result: IValidationResult) {
|
||||
this.setState({
|
||||
passwordFieldValid: result.valid,
|
||||
});
|
||||
|
@ -316,16 +339,16 @@ export default class ForgotPassword extends React.Component {
|
|||
|
||||
let resetPasswordJsx;
|
||||
switch (this.state.phase) {
|
||||
case PHASE_FORGOT:
|
||||
case Phase.Forgot:
|
||||
resetPasswordJsx = this.renderForgot();
|
||||
break;
|
||||
case PHASE_SENDING_EMAIL:
|
||||
case Phase.SendingEmail:
|
||||
resetPasswordJsx = this.renderSendingEmail();
|
||||
break;
|
||||
case PHASE_EMAIL_SENT:
|
||||
case Phase.EmailSent:
|
||||
resetPasswordJsx = this.renderEmailSent();
|
||||
break;
|
||||
case PHASE_DONE:
|
||||
case Phase.Done:
|
||||
resetPasswordJsx = this.renderDone();
|
||||
break;
|
||||
}
|
|
@ -24,7 +24,7 @@ import FocusLock from "react-focus-lock";
|
|||
import MemberAvatar from "../avatars/MemberAvatar";
|
||||
import { ContextMenuTooltipButton } from "../../../accessibility/context_menu/ContextMenuTooltipButton";
|
||||
import MessageContextMenu from "../context_menus/MessageContextMenu";
|
||||
import { aboveLeftOf, ContextMenu } from '../../structures/ContextMenu';
|
||||
import { aboveLeftOf } from '../../structures/ContextMenu';
|
||||
import MessageTimestamp from "../messages/MessageTimestamp";
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import { formatFullDate } from "../../../DateUtils";
|
||||
|
@ -122,7 +122,7 @@ export default class ImageView extends React.Component<IProps, IState> {
|
|||
const image = this.image.current;
|
||||
const imageWrapper = this.imageWrapper.current;
|
||||
|
||||
const rotation = inputRotation || this.state.rotation;
|
||||
const rotation = inputRotation ?? this.state.rotation;
|
||||
|
||||
const imageIsNotFlipped = rotation % 180 === 0;
|
||||
|
||||
|
@ -304,17 +304,13 @@ export default class ImageView extends React.Component<IProps, IState> {
|
|||
let contextMenu = null;
|
||||
if (this.state.contextMenuDisplayed) {
|
||||
contextMenu = (
|
||||
<ContextMenu
|
||||
<MessageContextMenu
|
||||
{...aboveLeftOf(this.contextMenuButton.current.getBoundingClientRect())}
|
||||
mxEvent={this.props.mxEvent}
|
||||
permalinkCreator={this.props.permalinkCreator}
|
||||
onFinished={this.onCloseContextMenu}
|
||||
>
|
||||
<MessageContextMenu
|
||||
mxEvent={this.props.mxEvent}
|
||||
permalinkCreator={this.props.permalinkCreator}
|
||||
onFinished={this.onCloseContextMenu}
|
||||
onCloseDialog={this.props.onFinished}
|
||||
/>
|
||||
</ContextMenu>
|
||||
onCloseDialog={this.props.onFinished}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -17,15 +17,25 @@ limitations under the License.
|
|||
import React from 'react';
|
||||
import { _t } from "../../../languageHandler";
|
||||
import { IntegrationManagers } from "../../../integrations/IntegrationManagers";
|
||||
import { IntegrationManagerInstance } from "../../../integrations/IntegrationManagerInstance";
|
||||
import * as sdk from '../../../index';
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import { SettingLevel } from "../../../settings/SettingLevel";
|
||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||
|
||||
interface IProps {
|
||||
|
||||
}
|
||||
|
||||
interface IState {
|
||||
currentManager: IntegrationManagerInstance;
|
||||
provisioningEnabled: boolean;
|
||||
}
|
||||
|
||||
@replaceableComponent("views.settings.SetIntegrationManager")
|
||||
export default class SetIntegrationManager extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
export default class SetIntegrationManager extends React.Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
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;
|
||||
SettingsStore.setValue("integrationProvisioning", null, SettingLevel.ACCOUNT, !current).catch(err => {
|
||||
console.error("Error changing integration manager provisioning");
|
||||
|
@ -46,7 +56,7 @@ export default class SetIntegrationManager extends React.Component {
|
|||
this.setState({ provisioningEnabled: !current });
|
||||
};
|
||||
|
||||
render() {
|
||||
public render(): React.ReactNode {
|
||||
const ToggleSwitch = sdk.getComponent("views.elements.ToggleSwitch");
|
||||
|
||||
const currentManager = this.state.currentManager;
|
|
@ -24,6 +24,8 @@ import PlatformPeg from "../../../../../PlatformPeg";
|
|||
import { SettingLevel } from "../../../../../settings/SettingLevel";
|
||||
import { replaceableComponent } from "../../../../../utils/replaceableComponent";
|
||||
import SettingsFlag from '../../../elements/SettingsFlag';
|
||||
import * as KeyboardShortcuts from "../../../../../accessibility/KeyboardShortcuts";
|
||||
import AccessibleButton from "../../../elements/AccessibleButton";
|
||||
|
||||
interface IState {
|
||||
autoLaunch: boolean;
|
||||
|
@ -45,6 +47,10 @@ export default class PreferencesUserSettingsTab extends React.Component<{}, ISta
|
|||
'breadcrumbs',
|
||||
];
|
||||
|
||||
static KEYBINDINGS_SETTINGS = [
|
||||
'ctrlFForSearch',
|
||||
];
|
||||
|
||||
static COMPOSER_SETTINGS = [
|
||||
'MessageComposerInput.autoReplaceEmoji',
|
||||
'MessageComposerInput.suggestEmoji',
|
||||
|
@ -53,28 +59,32 @@ export default class PreferencesUserSettingsTab extends React.Component<{}, ISta
|
|||
'MessageComposerInput.showStickersButton',
|
||||
];
|
||||
|
||||
static TIMELINE_SETTINGS = [
|
||||
'showTypingNotifications',
|
||||
'autoplayGifsAndVideos',
|
||||
'urlPreviewsEnabled',
|
||||
'TextualBody.enableBigEmoji',
|
||||
'showReadReceipts',
|
||||
static TIME_SETTINGS = [
|
||||
'showTwelveHourTimestamps',
|
||||
'alwaysShowTimestamps',
|
||||
'showRedactions',
|
||||
];
|
||||
static CODE_BLOCKS_SETTINGS = [
|
||||
'enableSyntaxHighlightLanguageDetection',
|
||||
'expandCodeByDefault',
|
||||
'scrollToBottomOnMessageSent',
|
||||
'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 = [
|
||||
'TagPanel.enableTagPanel',
|
||||
'promptBeforeInviteUnknownUsers',
|
||||
|
@ -221,11 +231,34 @@ export default class PreferencesUserSettingsTab extends React.Component<{}, ISta
|
|||
{this.renderGroup(PreferencesUserSettingsTab.ROOM_LIST_SETTINGS)}
|
||||
</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">
|
||||
<span className="mx_SettingsTab_subheading">{_t("Composer")}</span>
|
||||
{this.renderGroup(PreferencesUserSettingsTab.COMPOSER_SETTINGS)}
|
||||
</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">
|
||||
<span className="mx_SettingsTab_subheading">{_t("Timeline")}</span>
|
||||
{this.renderGroup(PreferencesUserSettingsTab.TIMELINE_SETTINGS)}
|
||||
|
|
|
@ -15,39 +15,48 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { _t, pickBestLanguage } from "../../../languageHandler";
|
||||
import * as sdk from "../../..";
|
||||
import { objectClone } from "../../../utils/objects";
|
||||
import StyledCheckbox from "../elements/StyledCheckbox";
|
||||
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")
|
||||
export default class InlineTermsAgreement extends React.Component {
|
||||
static propTypes = {
|
||||
policiesAndServicePairs: PropTypes.array.isRequired, // array of service/policy pairs
|
||||
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();
|
||||
|
||||
export default class InlineTermsAgreement extends React.Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
policies: [],
|
||||
busy: false,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
public componentDidMount(): void {
|
||||
// Build all the terms the user needs to accept
|
||||
const policies = []; // { checked, url, name }
|
||||
for (const servicePolicies of this.props.policiesAndServicePairs) {
|
||||
const availablePolicies = Object.values(servicePolicies.policies);
|
||||
for (const policy of availablePolicies) {
|
||||
const language = pickBestLanguage(Object.keys(policy).filter(p => p !== 'version'));
|
||||
const renderablePolicy = {
|
||||
const renderablePolicy: Policy = {
|
||||
checked: false,
|
||||
url: policy[language].url,
|
||||
name: policy[language].name,
|
||||
|
@ -59,13 +68,13 @@ export default class InlineTermsAgreement extends React.Component {
|
|||
this.setState({ policies });
|
||||
}
|
||||
|
||||
_togglePolicy = (index) => {
|
||||
private togglePolicy = (index: number): void => {
|
||||
const policies = objectClone(this.state.policies);
|
||||
policies[index].checked = !policies[index].checked;
|
||||
this.setState({ policies });
|
||||
};
|
||||
|
||||
_onContinue = () => {
|
||||
private onContinue = (): void => {
|
||||
const hasUnchecked = !!this.state.policies.some(p => !p.checked);
|
||||
if (hasUnchecked) return;
|
||||
|
||||
|
@ -73,7 +82,7 @@ export default class InlineTermsAgreement extends React.Component {
|
|||
this.props.onFinished(this.state.policies.map(p => p.url));
|
||||
};
|
||||
|
||||
_renderCheckboxes() {
|
||||
private renderCheckboxes(): React.ReactNode[] {
|
||||
const rendered = [];
|
||||
for (let i = 0; i < this.state.policies.length; 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>{introText}</div>
|
||||
<div className='mx_InlineTermsAgreement_checkbox'>
|
||||
<StyledCheckbox onChange={() => this._togglePolicy(i)} checked={policy.checked}>
|
||||
<StyledCheckbox onChange={() => this.togglePolicy(i)} checked={policy.checked}>
|
||||
{_t("Accept")}
|
||||
</StyledCheckbox>
|
||||
</div>
|
||||
|
@ -103,16 +112,16 @@ export default class InlineTermsAgreement extends React.Component {
|
|||
return rendered;
|
||||
}
|
||||
|
||||
render() {
|
||||
public render(): React.ReactNode {
|
||||
const AccessibleButton = sdk.getComponent("views.elements.AccessibleButton");
|
||||
const hasUnchecked = !!this.state.policies.some(p => !p.checked);
|
||||
|
||||
return (
|
||||
<div>
|
||||
{this.props.introElement}
|
||||
{this._renderCheckboxes()}
|
||||
{this.renderCheckboxes()}
|
||||
<AccessibleButton
|
||||
onClick={this._onContinue}
|
||||
onClick={this.onContinue}
|
||||
disabled={hasUnchecked || this.state.busy}
|
||||
kind="primary_sm"
|
||||
>
|
|
@ -15,18 +15,17 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import * as sdk from '../../../index';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||
|
||||
@replaceableComponent("views.verification.VerificationCancelled")
|
||||
export default class VerificationCancelled extends React.Component {
|
||||
static propTypes = {
|
||||
onDone: PropTypes.func.isRequired,
|
||||
}
|
||||
interface IProps {
|
||||
onDone: () => void;
|
||||
}
|
||||
|
||||
render() {
|
||||
@replaceableComponent("views.verification.VerificationCancelled")
|
||||
export default class VerificationCancelled extends React.Component<IProps> {
|
||||
public render(): React.ReactNode {
|
||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||
return <div>
|
||||
<p>{_t(
|
|
@ -15,18 +15,17 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import * as sdk from '../../../index';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||
|
||||
@replaceableComponent("views.verification.VerificationComplete")
|
||||
export default class VerificationComplete extends React.Component {
|
||||
static propTypes = {
|
||||
onDone: PropTypes.func.isRequired,
|
||||
}
|
||||
interface IProps {
|
||||
onDone: () => void;
|
||||
}
|
||||
|
||||
render() {
|
||||
@replaceableComponent("views.verification.VerificationComplete")
|
||||
export default class VerificationComplete extends React.Component<IProps> {
|
||||
public render(): React.ReactNode {
|
||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||
return <div>
|
||||
<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 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 { PendingActionSpinner } from "../right_panel/EncryptionInfo";
|
||||
import AccessibleButton from "../elements/AccessibleButton";
|
||||
|
@ -23,24 +24,29 @@ import DialogButtons from "../elements/DialogButtons";
|
|||
import { fixupColorFonts } from '../../../utils/FontManager';
|
||||
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) {
|
||||
return s.charAt(0).toUpperCase() + s.slice(1);
|
||||
}
|
||||
|
||||
@replaceableComponent("views.verification.VerificationShowSas")
|
||||
export default class VerificationShowSas extends React.Component {
|
||||
static propTypes = {
|
||||
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) {
|
||||
export default class VerificationShowSas extends React.Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
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),
|
||||
// also make sure we have a working emoji font to display the SAS emojis here.
|
||||
// This is also done from LoggedInView.
|
||||
fixupColorFonts();
|
||||
}
|
||||
|
||||
onMatchClick = () => {
|
||||
private onMatchClick = (): void => {
|
||||
this.setState({ pending: true });
|
||||
this.props.onDone();
|
||||
};
|
||||
|
||||
onDontMatchClick = () => {
|
||||
private onDontMatchClick = (): void => {
|
||||
this.setState({ cancelling: true });
|
||||
this.props.onCancel();
|
||||
};
|
|
@ -840,8 +840,8 @@
|
|||
"Enable big emoji in chat": "Enable big emoji in chat",
|
||||
"Send typing notifications": "Send typing notifications",
|
||||
"Show typing notifications": "Show typing notifications",
|
||||
"Use Command + F to search": "Use Command + F to search",
|
||||
"Use Ctrl + F to search": "Use Ctrl + F to search",
|
||||
"Use Command + F to search timeline": "Use Command + F to search timeline",
|
||||
"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 Ctrl + Enter to send a message": "Use Ctrl + Enter to send a message",
|
||||
"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.",
|
||||
"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",
|
||||
"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:",
|
||||
"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.",
|
||||
|
@ -1339,7 +1333,12 @@
|
|||
"Show tray icon and minimize window to it on close": "Show tray icon and minimize window to it on close",
|
||||
"Preferences": "Preferences",
|
||||
"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",
|
||||
"Code blocks": "Code blocks",
|
||||
"Images, GIFs and videos": "Images, GIFs and videos",
|
||||
"Timeline": "Timeline",
|
||||
"Autocomplete delay (ms)": "Autocomplete delay (ms)",
|
||||
"Read Marker lifetime (ms)": "Read Marker lifetime (ms)",
|
||||
|
@ -1828,6 +1827,12 @@
|
|||
"Edit devices": "Edit devices",
|
||||
"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.",
|
||||
"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",
|
||||
"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.",
|
||||
|
|
|
@ -20,15 +20,25 @@ import { StandardActions } from "./StandardActions";
|
|||
import { PushRuleVectorState } from "./PushRuleVectorState";
|
||||
import { NotificationUtils } from "./NotificationUtils";
|
||||
|
||||
interface IProps {
|
||||
kind: Kind;
|
||||
description: string;
|
||||
vectorStateToActions: Action;
|
||||
}
|
||||
|
||||
class VectorPushRuleDefinition {
|
||||
constructor(opts) {
|
||||
private kind: Kind;
|
||||
private description: string;
|
||||
private vectorStateToActions: Action;
|
||||
|
||||
constructor(opts: IProps) {
|
||||
this.kind = opts.kind;
|
||||
this.description = opts.description;
|
||||
this.vectorStateToActions = opts.vectorStateToActions;
|
||||
}
|
||||
|
||||
// Translate the rule actions and its enabled value into vector state
|
||||
ruleToVectorState(rule) {
|
||||
public ruleToVectorState(rule): VectorPushRuleDefinition {
|
||||
let enabled = false;
|
||||
if (rule) {
|
||||
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.
|
||||
*/
|
||||
export const VectorPushRulesDefinitions = {
|
||||
// Messages containing user's display name
|
||||
".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
|
||||
vectorStateToActions: { // The actions for each vector state, or null to disable the rule.
|
||||
on: StandardActions.ACTION_NOTIFY,
|
||||
|
@ -80,7 +101,7 @@ export const VectorPushRulesDefinitions = {
|
|||
|
||||
// Messages containing user's username (localpart/MXID)
|
||||
".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
|
||||
vectorStateToActions: { // The actions for each vector state, or null to disable the rule.
|
||||
on: StandardActions.ACTION_NOTIFY,
|
||||
|
@ -91,7 +112,7 @@ export const VectorPushRulesDefinitions = {
|
|||
|
||||
// Messages containing @room
|
||||
".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
|
||||
vectorStateToActions: { // The actions for each vector state, or null to disable the rule.
|
||||
on: StandardActions.ACTION_NOTIFY,
|
||||
|
@ -102,7 +123,7 @@ export const VectorPushRulesDefinitions = {
|
|||
|
||||
// Messages just sent to the user in a 1:1 room
|
||||
".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
|
||||
vectorStateToActions: {
|
||||
on: StandardActions.ACTION_NOTIFY,
|
||||
|
@ -113,7 +134,7 @@ export const VectorPushRulesDefinitions = {
|
|||
|
||||
// Encrypted messages just sent to the user in a 1:1 room
|
||||
".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
|
||||
vectorStateToActions: {
|
||||
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
|
||||
// By opposition, all other room messages are from group chat rooms.
|
||||
".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
|
||||
vectorStateToActions: {
|
||||
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
|
||||
// By opposition, all other room messages are from group chat rooms.
|
||||
".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
|
||||
vectorStateToActions: {
|
||||
on: StandardActions.ACTION_NOTIFY,
|
||||
|
@ -150,7 +171,7 @@ export const VectorPushRulesDefinitions = {
|
|||
|
||||
// Invitation for the user
|
||||
".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
|
||||
vectorStateToActions: {
|
||||
on: StandardActions.ACTION_NOTIFY,
|
||||
|
@ -161,7 +182,7 @@ export const VectorPushRulesDefinitions = {
|
|||
|
||||
// Incoming call
|
||||
".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
|
||||
vectorStateToActions: {
|
||||
on: StandardActions.ACTION_NOTIFY,
|
||||
|
@ -172,7 +193,7 @@ export const VectorPushRulesDefinitions = {
|
|||
|
||||
// Notifications from bots
|
||||
".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
|
||||
vectorStateToActions: {
|
||||
// .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)
|
||||
".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
|
||||
vectorStateToActions: { // The actions for each vector state, or null to disable the rule.
|
||||
on: StandardActions.ACTION_NOTIFY,
|
|
@ -93,15 +93,15 @@ async function collectBugReport(opts: IOpts = {}, gzipLogs = true) {
|
|||
body.append("cross_signing_supported_by_hs",
|
||||
String(await client.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing")));
|
||||
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))));
|
||||
|
||||
const pkCache = client.getCrossSigningCacheCallbacks();
|
||||
body.append("cross_signing_master_pk_cached",
|
||||
body.append("cross_signing_master_privkey_cached",
|
||||
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"))));
|
||||
body.append("cross_signing_user_signing_pk_cached",
|
||||
body.append("cross_signing_user_signing_privkey_cached",
|
||||
String(!!(pkCache && await pkCache.getCrossSigningKeyCache("user_signing"))));
|
||||
|
||||
body.append("secret_storage_ready", String(await client.isSecretStorageReady()));
|
||||
|
|
|
@ -455,7 +455,7 @@ export const SETTINGS: {[setting: string]: ISetting} = {
|
|||
},
|
||||
"ctrlFForSearch": {
|
||||
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,
|
||||
},
|
||||
"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;
|
|
@ -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" "*"
|
||||
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":
|
||||
version "2.3.1"
|
||||
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"
|
||||
integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==
|
||||
|
||||
matrix-js-sdk@12.0.0:
|
||||
version "12.0.0"
|
||||
resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-12.0.0.tgz#8ee7cc37661476341d0c792a1a12bc78b19f9fdd"
|
||||
integrity sha512-DHeq87Sx9Dv37FYyvZkmA1VYsQUNaVgc3QzMUkFwoHt1T4EZzgyYpdsp3uYruJzUW0ACvVJcwFdrU4e1VS97dQ==
|
||||
matrix-js-sdk@12.0.1:
|
||||
version "12.0.1"
|
||||
resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-12.0.1.tgz#3a63881f743420a4d39474daa39bd0fb90930d43"
|
||||
integrity sha512-HkOWv8QHojceo3kPbC+vAIFUjsRAig6MBvEY35UygS3g2dL0UcJ5Qx09/2wcXtu6dowlDnWsz2HHk62tS2cklA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.12.5"
|
||||
another-json "^0.2.0"
|
||||
|
@ -5456,6 +5461,7 @@ matrix-js-sdk@12.0.0:
|
|||
bs58 "^4.0.1"
|
||||
content-type "^1.0.4"
|
||||
loglevel "^1.7.1"
|
||||
p-retry "^4.5.0"
|
||||
qs "^6.9.6"
|
||||
request "^2.88.2"
|
||||
unhomoglyph "^1.0.6"
|
||||
|
@ -6007,6 +6013,14 @@ p-locate@^4.1.0:
|
|||
dependencies:
|
||||
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:
|
||||
version "2.2.0"
|
||||
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"
|
||||
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:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
|
||||
|
|
Loading…
Reference in New Issue