mirror of https://github.com/vector-im/riot-web
				
				
				
			Merge pull request #6315 from matrix-org/gsouquet/ts-1
						commit
						7d6342fa40
					
				|  | @ -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 { | ||||
|  |  | |||
|  | @ -42,7 +42,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"; | ||||
|  | @ -1577,7 +1577,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; | ||||
|         } | ||||
|  | @ -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; | ||||
|  | @ -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(); | ||||
|     }; | ||||
|  | @ -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.", | ||||
|  | @ -1833,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, | ||||
|  | @ -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; | ||||
		Loading…
	
		Reference in New Issue
	
	 Germain
						Germain