Merge branch 'develop' into travis/render-join-rules
						commit
						8cdfdd2e24
					
				|  | @ -77,7 +77,6 @@ | |||
| @import "./views/elements/_Dropdown.scss"; | ||||
| @import "./views/elements/_EditableItemList.scss"; | ||||
| @import "./views/elements/_Field.scss"; | ||||
| @import "./views/elements/_HexVerify.scss"; | ||||
| @import "./views/elements/_ImageView.scss"; | ||||
| @import "./views/elements/_InlineSpinner.scss"; | ||||
| @import "./views/elements/_ManageIntegsButton.scss"; | ||||
|  | @ -156,6 +155,7 @@ | |||
| @import "./views/settings/tabs/_SecuritySettingsTab.scss"; | ||||
| @import "./views/settings/tabs/_SettingsTab.scss"; | ||||
| @import "./views/settings/tabs/_VoiceSettingsTab.scss"; | ||||
| @import "./views/verification/_VerificationShowSas.scss"; | ||||
| @import "./views/voip/_CallView.scss"; | ||||
| @import "./views/voip/_IncomingCallbox.scss"; | ||||
| @import "./views/voip/_VideoView.scss"; | ||||
|  |  | |||
|  | @ -14,21 +14,9 @@ See the License for the specific language governing permissions and | |||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| .mx_HexVerify { | ||||
| .mx_VerificationShowSas_sas { | ||||
|     text-align: center; | ||||
| } | ||||
| 
 | ||||
| .mx_HexVerify_pair { | ||||
|     display: inline-block; | ||||
|     font-weight: bold; | ||||
|     padding-left: 3px; | ||||
|     padding-right: 3px; | ||||
| } | ||||
| 
 | ||||
| .mx_HexVerify_pair_verified { | ||||
|     color: $accent-color; | ||||
| } | ||||
| 
 | ||||
| .mx_HexVerify_pair:hover{ | ||||
|     color: $accent-color; | ||||
| } | ||||
|  | @ -135,14 +135,7 @@ class MatrixClientPeg { | |||
|         const opts = utils.deepCopy(this.opts); | ||||
|         // the react sdk doesn't work without this, so don't allow
 | ||||
|         opts.pendingEventOrdering = "detached"; | ||||
| 
 | ||||
|         const LAZY_LOADING_FEATURE = "feature_lazyloading"; | ||||
|         if (SettingsStore.isFeatureEnabled(LAZY_LOADING_FEATURE)) { | ||||
|             const userId = this.matrixClient.credentials.userId; | ||||
|             if (phasedRollOutExpiredForUser(userId, LAZY_LOADING_FEATURE, Date.now())) { | ||||
|                 opts.lazyLoadMembers = true; | ||||
|             } | ||||
|         } | ||||
|         opts.lazyLoadMembers = true; | ||||
| 
 | ||||
|         // Connect the matrix client to the dispatcher
 | ||||
|         MatrixActionCreators.start(this.matrixClient); | ||||
|  |  | |||
|  | @ -190,10 +190,13 @@ const LeftPanel = React.createClass({ | |||
| 
 | ||||
|         const tagPanelEnabled = SettingsStore.getValue("TagPanel.enableTagPanel"); | ||||
|         let tagPanelContainer; | ||||
| 
 | ||||
|         const isCustomTagsEnabled = SettingsStore.isFeatureEnabled("feature_custom_tags"); | ||||
| 
 | ||||
|         if (tagPanelEnabled) { | ||||
|             tagPanelContainer = (<div className="mx_LeftPanel_tagPanelContainer"> | ||||
|                 <TagPanel /> | ||||
|                 <CustomRoomTagPanel /> | ||||
|                 { isCustomTagsEnabled ? <CustomRoomTagPanel /> : undefined } | ||||
|                 <TagPanelButtons /> | ||||
|             </div>); | ||||
|         } | ||||
|  |  | |||
|  | @ -19,6 +19,8 @@ import sdk from '../../../../index'; | |||
| import MatrixClientPeg from '../../../../MatrixClientPeg'; | ||||
| import Modal from '../../../../Modal'; | ||||
| 
 | ||||
| import { MatrixClient } from 'matrix-js-sdk'; | ||||
| 
 | ||||
| import { _t } from '../../../../languageHandler'; | ||||
| 
 | ||||
| const RESTORE_TYPE_PASSPHRASE = 0; | ||||
|  | @ -88,7 +90,7 @@ export default React.createClass({ | |||
|         }); | ||||
|         try { | ||||
|             const recoverInfo = await MatrixClientPeg.get().restoreKeyBackupWithPassword( | ||||
|                 this.state.passPhrase, undefined, undefined, this.state.backupInfo.version, | ||||
|                 this.state.passPhrase, undefined, undefined, this.state.backupInfo, | ||||
|             ); | ||||
|             this.setState({ | ||||
|                 loading: false, | ||||
|  | @ -107,11 +109,11 @@ export default React.createClass({ | |||
|         this.setState({ | ||||
|             loading: true, | ||||
|             restoreError: null, | ||||
|             restoreType: RESTORE_TYPE_PASSPHRASE, | ||||
|             restoreType: RESTORE_TYPE_RECOVERYKEY, | ||||
|         }); | ||||
|         try { | ||||
|             const recoverInfo = await MatrixClientPeg.get().restoreKeyBackupWithRecoveryKey( | ||||
|                 this.state.recoveryKey, undefined, undefined, this.state.backupInfo.version, | ||||
|                 this.state.recoveryKey, undefined, undefined, this.state.backupInfo, | ||||
|             ); | ||||
|             this.setState({ | ||||
|                 loading: false, | ||||
|  | @ -185,32 +187,31 @@ export default React.createClass({ | |||
|             title = _t("Error"); | ||||
|             content = _t("Unable to load backup status"); | ||||
|         } else if (this.state.restoreError) { | ||||
|             title = _t("Error"); | ||||
|             content = _t("Unable to restore backup"); | ||||
|             if (this.state.restoreError.errcode === MatrixClient.RESTORE_BACKUP_ERROR_BAD_KEY) { | ||||
|                 if (this.state.restoreType === RESTORE_TYPE_RECOVERYKEY) { | ||||
|                     title = _t("Recovery Key Mismatch"); | ||||
|                     content = <div> | ||||
|                         <p>{_t( | ||||
|                             "Backup could not be decrypted with this key: " + | ||||
|                             "please verify that you entered the correct recovery key.", | ||||
|                         )}</p> | ||||
|                     </div>; | ||||
|                 } else { | ||||
|                     title = _t("Incorrect Recovery Passphrase"); | ||||
|                     content = <div> | ||||
|                         <p>{_t( | ||||
|                             "Backup could not be decrypted with this passphrase: " + | ||||
|                             "please verify that you entered the correct recovery passphrase.", | ||||
|                         )}</p> | ||||
|                     </div>; | ||||
|                 } | ||||
|             } else { | ||||
|                 title = _t("Error"); | ||||
|                 content = _t("Unable to restore backup"); | ||||
|             } | ||||
|         } else if (this.state.backupInfo === null) { | ||||
|             title = _t("Error"); | ||||
|             content = _t("No backup found!"); | ||||
|         } else if ( | ||||
|             this.state.recoverInfo && | ||||
|             this.state.recoverInfo.imported === 0 && | ||||
|             this.state.recoverInfo.total > 0 | ||||
|         ) { | ||||
|             title = _t("Error Restoring Backup"); | ||||
|             if (this.state.restoreType === RESTORE_TYPE_RECOVERYKEY) { | ||||
|                 content = <div> | ||||
|                     <p>{_t( | ||||
|                         "Backup could not be decrypted with this key: " + | ||||
|                         "please verify that you entered the correct recovery key.", | ||||
|                     )}</p> | ||||
|                 </div>; | ||||
|             } else { | ||||
|                 content = <div> | ||||
|                     <p>{_t( | ||||
|                         "Backup could not be decrypted with this passphrase: " + | ||||
|                         "please verify that you entered the correct recovery passphrase.", | ||||
|                     )}</p> | ||||
|                 </div>; | ||||
|             } | ||||
|         } else if (this.state.recoverInfo) { | ||||
|             title = _t("Backup Restored"); | ||||
|             let failedToDecrypt; | ||||
|  |  | |||
|  | @ -1,103 +0,0 @@ | |||
| /* | ||||
| Copyright 2019 New Vector Ltd. | ||||
| 
 | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
| 
 | ||||
|     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| 
 | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| import React from "react"; | ||||
| import PropTypes from "prop-types"; | ||||
| import classnames from 'classnames'; | ||||
| 
 | ||||
| import sdk from '../../../index'; | ||||
| 
 | ||||
| class HexVerifyPair extends React.Component { | ||||
|     static propTypes = { | ||||
|         text: PropTypes.string.isRequired, | ||||
|         index: PropTypes.number, | ||||
|         verified: PropTypes.bool, | ||||
|         onChange: PropTypes.func.isRequired, | ||||
|     } | ||||
| 
 | ||||
|     _onClick = () => { | ||||
|         this.setState({verified: !this.props.verified}); | ||||
|         this.props.onChange(this.props.index, !this.props.verified); | ||||
|     } | ||||
| 
 | ||||
|     render() { | ||||
|         const classNames = { | ||||
|             mx_HexVerify_pair: true, | ||||
|             mx_HexVerify_pair_verified: this.props.verified, | ||||
|         }; | ||||
|         const AccessibleButton = sdk.getComponent('views.elements.AccessibleButton'); | ||||
|         return <AccessibleButton className={classnames(classNames)} | ||||
|             onClick={this._onClick} | ||||
|         > | ||||
|             {this.props.text} | ||||
|         </AccessibleButton>; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /* | ||||
|  * Helps a user verify a hexadecimal code matches one displayed | ||||
|  * elsewhere (eg. on a different device) | ||||
|  */ | ||||
| export default class HexVerify extends React.Component { | ||||
|     static propTypes = { | ||||
|         text: PropTypes.string.isRequired, | ||||
|         onVerifiedStateChange: PropTypes.func, | ||||
|     } | ||||
| 
 | ||||
|     static defaultProps = { | ||||
|         onVerifiedStateChange: function() {}, | ||||
|     } | ||||
| 
 | ||||
|     constructor(props) { | ||||
|         super(props); | ||||
|         this.state = { | ||||
|             pairsVerified: [], | ||||
|         }; | ||||
|         for (let i = 0; i < props.text.length; i += 2) { | ||||
|             this.state.pairsVerified.push(false); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     _onPairChange = (index, newVal) => { | ||||
|         const oldVerified = this.state.pairsVerified.reduce((acc, val) => { | ||||
|             return acc && val; | ||||
|         }, true); | ||||
|         const newPairsVerified = this.state.pairsVerified.slice(0); | ||||
|         newPairsVerified[index] = newVal; | ||||
|         const newVerified = newPairsVerified.reduce((acc, val) => { | ||||
|             return acc && val; | ||||
|         }, true); | ||||
|         this.setState({pairsVerified: newPairsVerified}); | ||||
|         if (oldVerified !== newVerified) { | ||||
|             this.props.onVerifiedStateChange(newVerified); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     render() { | ||||
|         const pairs = []; | ||||
| 
 | ||||
|         for (let i = 0; i < this.props.text.length / 2; ++i) { | ||||
|             pairs.push(<HexVerifyPair key={i} index={i} | ||||
|                 text={this.props.text.substr(i * 2, 2)} | ||||
|                 verified={this.state.pairsVerified[i]} | ||||
|                 onChange={this._onPairChange} | ||||
|             />); | ||||
|         } | ||||
|         return <div className="mx_HexVerify"> | ||||
|             {pairs} | ||||
|         </div>; | ||||
|     } | ||||
| } | ||||
|  | @ -38,6 +38,12 @@ export default class ToggleSwitch extends React.Component { | |||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     componentWillReceiveProps(nextProps) { | ||||
|         if (nextProps.checked !== this.state.checked) { | ||||
|             this.setState({checked: nextProps.checked}); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     _onClick = (e) => { | ||||
|         e.stopPropagation(); | ||||
|         e.preventDefault(); | ||||
|  |  | |||
|  | @ -172,11 +172,14 @@ module.exports = React.createClass({ | |||
|             this._delayedRefreshRoomList(); | ||||
|         }); | ||||
| 
 | ||||
|         this._customTagStoreToken = CustomRoomTagStore.addListener(() => { | ||||
|             this.setState({ | ||||
|                 customTags: CustomRoomTagStore.getTags(), | ||||
| 
 | ||||
|         if (SettingsStore.isFeatureEnabled("feature_custom_tags")) { | ||||
|             this._customTagStoreToken = CustomRoomTagStore.addListener(() => { | ||||
|                 this.setState({ | ||||
|                     customTags: CustomRoomTagStore.getTags(), | ||||
|                 }); | ||||
|             }); | ||||
|         }); | ||||
|         } | ||||
| 
 | ||||
|         this.refreshRoomList(); | ||||
| 
 | ||||
|  | @ -728,7 +731,8 @@ module.exports = React.createClass({ | |||
|         ]; | ||||
|         const tagSubLists = Object.keys(this.state.lists) | ||||
|             .filter((tagName) => { | ||||
|                 return this.state.customTags[tagName] && !tagName.match(STANDARD_TAGS_REGEX); | ||||
|                 return (!this.state.customTags || this.state.customTags[tagName]) && | ||||
|                     !tagName.match(STANDARD_TAGS_REGEX); | ||||
|             }).map((tagName) => { | ||||
|                 return { | ||||
|                     list: this.state.lists[tagName], | ||||
|  |  | |||
|  | @ -250,19 +250,26 @@ export default class KeyBackupPanel extends React.PureComponent { | |||
|                 backupSigStatuses = _t("Backup is not signed by any of your devices"); | ||||
|             } | ||||
| 
 | ||||
|             let trustedLocally; | ||||
|             if (this.state.backupSigStatus.trusted_locally) { | ||||
|                 trustedLocally = _t("This backup is trusted because it has been restored on this device"); | ||||
|             } | ||||
| 
 | ||||
|             return <div> | ||||
|                 {_t("Backup version: ")}{this.state.backupInfo.version}<br /> | ||||
|                 {_t("Algorithm: ")}{this.state.backupInfo.algorithm}<br /> | ||||
|                 {clientBackupStatus}<br /> | ||||
|                 <div>{_t("Backup version: ")}{this.state.backupInfo.version}</div> | ||||
|                 <div>{_t("Algorithm: ")}{this.state.backupInfo.algorithm}</div> | ||||
|                 <div>{clientBackupStatus}</div> | ||||
|                 {uploadStatus} | ||||
|                 <div>{backupSigStatuses}</div><br /> | ||||
|                 <br /> | ||||
|                 <AccessibleButton kind="primary" onClick={this._restoreBackup}> | ||||
|                     { _t("Restore backup") } | ||||
|                 </AccessibleButton>    | ||||
|                 <AccessibleButton kind="danger" onClick={this._deleteBackup}> | ||||
|                     { _t("Delete backup") } | ||||
|                 </AccessibleButton> | ||||
|                 <div>{backupSigStatuses}</div> | ||||
|                 <div>{trustedLocally}</div> | ||||
|                 <p> | ||||
|                     <AccessibleButton kind="primary" onClick={this._restoreBackup}> | ||||
|                         { _t("Restore backup") } | ||||
|                     </AccessibleButton>    | ||||
|                     <AccessibleButton kind="danger" onClick={this._deleteBackup}> | ||||
|                         { _t("Delete backup") } | ||||
|                     </AccessibleButton> | ||||
|                 </p> | ||||
|             </div>; | ||||
|         } else { | ||||
|             return <div> | ||||
|  |  | |||
|  | @ -18,9 +18,7 @@ import React from 'react'; | |||
| import {_t} from "../../../../languageHandler"; | ||||
| import PropTypes from "prop-types"; | ||||
| import SettingsStore, {SettingLevel} from "../../../../settings/SettingsStore"; | ||||
| import MatrixClientPeg from "../../../../MatrixClientPeg"; | ||||
| import LabelledToggleSwitch from "../../elements/LabelledToggleSwitch"; | ||||
| const Modal = require("../../../../Modal"); | ||||
| const sdk = require("../../../../index"); | ||||
| 
 | ||||
| export class LabsSettingToggle extends React.Component { | ||||
|  | @ -28,38 +26,7 @@ export class LabsSettingToggle extends React.Component { | |||
|         featureId: PropTypes.string.isRequired, | ||||
|     }; | ||||
| 
 | ||||
|     async _onLazyLoadChanging(enabling) { | ||||
|         // don't prevent turning LL off when not supported
 | ||||
|         if (enabling) { | ||||
|             const supported = await MatrixClientPeg.get().doesServerSupportLazyLoading(); | ||||
|             if (!supported) { | ||||
|                 await new Promise((resolve) => { | ||||
|                     const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); | ||||
|                     Modal.createDialog(QuestionDialog, { | ||||
|                         title: _t("Lazy loading members not supported"), | ||||
|                         description: | ||||
|                             <div> | ||||
|                                 { _t("Lazy loading is not supported by your " + | ||||
|                                     "current homeserver.") } | ||||
|                             </div>, | ||||
|                         button: _t("OK"), | ||||
|                         onFinished: resolve, | ||||
|                     }); | ||||
|                 }); | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     _onChange = async (checked) => { | ||||
|         if (this.props.featureId === "feature_lazyloading") { | ||||
|             const confirmed = await this._onLazyLoadChanging(checked); | ||||
|             if (!confirmed) { | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         await SettingsStore.setFeatureEnabled(this.props.featureId, checked); | ||||
|         this.forceUpdate(); | ||||
|     }; | ||||
|  |  | |||
|  | @ -27,8 +27,27 @@ export default class SecurityRoomSettingsTab extends React.Component { | |||
|         roomId: PropTypes.string.isRequired, | ||||
|     }; | ||||
| 
 | ||||
|     constructor() { | ||||
|         super(); | ||||
| 
 | ||||
|         this.state = { | ||||
|             joinRule: "invite", | ||||
|             guestAccess: "can_join", | ||||
|             history: "shared", | ||||
|             encrypted: false, | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     componentWillMount(): void { | ||||
|         MatrixClientPeg.get().on("RoomState.events", this._onStateEvent); | ||||
| 
 | ||||
|         const room = MatrixClientPeg.get().getRoom(this.props.roomId); | ||||
|         const state = room.currentState; | ||||
|         const joinRule = state.getStateEvents("m.room.join_rules", "").getContent()['join_rule']; | ||||
|         const guestAccess = state.getStateEvents("m.room.guest_access", "").getContent()['guest_access']; | ||||
|         const history = state.getStateEvents("m.room.history_visibility", "").getContent()['history_visibility']; | ||||
|         const encrypted = MatrixClientPeg.get().isRoomEncrypted(this.props.roomId); | ||||
|         this.setState({joinRule, guestAccess, history, encrypted}); | ||||
|     } | ||||
| 
 | ||||
|     componentWillUnmount(): void { | ||||
|  | @ -46,19 +65,37 @@ export default class SecurityRoomSettingsTab extends React.Component { | |||
|     }; | ||||
| 
 | ||||
|     _onEncryptionChange = (e) => { | ||||
|         const beforeEncrypted = this.state.encrypted; | ||||
|         this.setState({encrypted: true}); | ||||
|         MatrixClientPeg.get().sendStateEvent( | ||||
|             this.props.roomId, "m.room.encryption", | ||||
|             { algorithm: "m.megolm.v1.aes-sha2" }, | ||||
|         ); | ||||
|         ).catch((e) => { | ||||
|             console.error(e); | ||||
|             this.setState({encrypted: beforeEncrypted}); | ||||
|         }); | ||||
|     }; | ||||
| 
 | ||||
|     _fixGuestAccess = (e) => { | ||||
|         e.preventDefault(); | ||||
|         e.stopPropagation(); | ||||
| 
 | ||||
|         const joinRule = "invite"; | ||||
|         const guestAccess = "can_join"; | ||||
| 
 | ||||
|         const beforeJoinRule = this.state.joinRule; | ||||
|         const beforeGuestAccess = this.state.guestAccess; | ||||
|         this.setState({joinRule, guestAccess}); | ||||
| 
 | ||||
|         const client = MatrixClientPeg.get(); | ||||
|         client.sendStateEvent(this.props.roomId, "m.room.join_rules", {join_rule: "invite"}, ""); | ||||
|         client.sendStateEvent(this.props.roomId, "m.room.guest_access", {guest_access: "can_join"}, ""); | ||||
|         client.sendStateEvent(this.props.roomId, "m.room.join_rules", {join_rule: joinRule}, "").catch((e) => { | ||||
|             console.error(e); | ||||
|             this.setState({joinRule: beforeJoinRule}); | ||||
|         }); | ||||
|         client.sendStateEvent(this.props.roomId, "m.room.guest_access", {guest_access: guestAccess}, "").catch((e) => { | ||||
|             console.error(e); | ||||
|             this.setState({guestAccess: beforeGuestAccess}); | ||||
|         }); | ||||
|     }; | ||||
| 
 | ||||
|     _onRoomAccessRadioToggle = (ev) => { | ||||
|  | @ -92,24 +129,39 @@ export default class SecurityRoomSettingsTab extends React.Component { | |||
|                 break; | ||||
|         } | ||||
| 
 | ||||
|         const beforeJoinRule = this.state.joinRule; | ||||
|         const beforeGuestAccess = this.state.guestAccess; | ||||
|         this.setState({joinRule, guestAccess}); | ||||
| 
 | ||||
|         const client = MatrixClientPeg.get(); | ||||
|         client.sendStateEvent(this.props.roomId, "m.room.join_rules", {join_rule: joinRule}, ""); | ||||
|         client.sendStateEvent(this.props.roomId, "m.room.guest_access", {guest_access: guestAccess}, ""); | ||||
|         client.sendStateEvent(this.props.roomId, "m.room.join_rules", {join_rule: joinRule}, "").catch((e) => { | ||||
|             console.error(e); | ||||
|             this.setState({joinRule: beforeJoinRule}); | ||||
|         }); | ||||
|         client.sendStateEvent(this.props.roomId, "m.room.guest_access", {guest_access: guestAccess}, "").catch((e) => { | ||||
|             console.error(e); | ||||
|             this.setState({guestAccess: beforeGuestAccess}); | ||||
|         }); | ||||
|     }; | ||||
| 
 | ||||
|     _onHistoryRadioToggle = (ev) => { | ||||
|         const beforeHistory = this.state.history; | ||||
|         this.setState({history: ev.target.value}); | ||||
|         MatrixClientPeg.get().sendStateEvent(this.props.roomId, "m.room.history_visibility", { | ||||
|             history_visibility: ev.target.value, | ||||
|         }, ""); | ||||
|         }, "").catch((e) => { | ||||
|             console.error(e); | ||||
|             this.setState({history: beforeHistory}); | ||||
|         }); | ||||
|     }; | ||||
| 
 | ||||
|     _renderRoomAccess() { | ||||
|         const client = MatrixClientPeg.get(); | ||||
|         const room = client.getRoom(this.props.roomId); | ||||
|         const joinRule = room.currentState.getStateEvents("m.room.join_rules", "").getContent()['join_rule']; | ||||
|         const guestAccess = room.currentState.getStateEvents("m.room.guest_access", "").getContent()['guest_access']; | ||||
|         const joinRule = this.state.joinRule; | ||||
|         const guestAccess = this.state.guestAccess; | ||||
|         const aliasEvents = room.currentState.getStateEvents("m.room.aliases") || []; | ||||
|         const hasAliases = aliasEvents.includes((ev) => (ev.getContent().aliases || []).length); | ||||
|         const hasAliases = !!aliasEvents.find((ev) => (ev.getContent().aliases || []).length > 0); | ||||
| 
 | ||||
|         const canChangeAccess = room.currentState.mayClientSendStateEvent("m.room.join_rules", client) | ||||
|             && room.currentState.mayClientSendStateEvent("m.room.guest_access", client); | ||||
|  | @ -170,9 +222,8 @@ export default class SecurityRoomSettingsTab extends React.Component { | |||
| 
 | ||||
|     _renderHistory() { | ||||
|         const client = MatrixClientPeg.get(); | ||||
|         const room = client.getRoom(this.props.roomId); | ||||
|         const state = room.currentState; | ||||
|         const history = state.getStateEvents("m.room.history_visibility", "").getContent()['history_visibility']; | ||||
|         const history = this.state.history; | ||||
|         const state = client.getRoom(this.props.roomId).currentState; | ||||
|         const canChangeHistory = state.mayClientSendStateEvent('m.room.history_visibility', client); | ||||
| 
 | ||||
|         return ( | ||||
|  | @ -218,7 +269,7 @@ export default class SecurityRoomSettingsTab extends React.Component { | |||
| 
 | ||||
|         const client = MatrixClientPeg.get(); | ||||
|         const room = client.getRoom(this.props.roomId); | ||||
|         const isEncrypted = client.isRoomEncrypted(this.props.roomId); | ||||
|         const isEncrypted = this.state.encrypted; | ||||
|         const hasEncryptionPermission = room.currentState.mayClientSendStateEvent("m.room.encryption", client); | ||||
|         const canEnableEncryption = !isEncrypted && hasEncryptionPermission; | ||||
| 
 | ||||
|  |  | |||
|  | @ -28,19 +28,11 @@ export default class VerificationShowSas extends React.Component { | |||
| 
 | ||||
|     constructor() { | ||||
|         super(); | ||||
|         this.state = { | ||||
|             sasVerified: false, | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     _onVerifiedStateChange = (newVal) => { | ||||
|         this.setState({sasVerified: newVal}); | ||||
|     } | ||||
| 
 | ||||
|     render() { | ||||
|         const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); | ||||
|         const HexVerify = sdk.getComponent('views.elements.HexVerify'); | ||||
|         return <div> | ||||
|         return <div className="mx_VerificationShowSas"> | ||||
|             <p>{_t( | ||||
|                 "Verify this user by confirming the following number appears on their screen.", | ||||
|             )}</p> | ||||
|  | @ -48,15 +40,11 @@ export default class VerificationShowSas extends React.Component { | |||
|                 "For maximum security, we recommend you do this in person or use another " + | ||||
|                 "trusted means of communication.", | ||||
|             )}</p> | ||||
|             <HexVerify text={this.props.sas} | ||||
|                 onVerifiedStateChange={this._onVerifiedStateChange} | ||||
|             /> | ||||
|             <p>{_t( | ||||
|                 "To continue, click on each pair to confirm it's correct.", | ||||
|             )}</p> | ||||
|             <div className="mx_VerificationShowSas_sas"> | ||||
|                 {this.props.sas} | ||||
|             </div> | ||||
|             <DialogButtons onPrimaryButtonClick={this.props.onDone} | ||||
|                 primaryButton={_t("Continue")} | ||||
|                 primaryDisabled={!this.state.sasVerified} | ||||
|                 hasCancel={true} | ||||
|                 onCancel={this.props.onCancel} | ||||
|             /> | ||||
|  |  | |||
|  | @ -270,7 +270,7 @@ | |||
|     "Failed to join room": "Failed to join room", | ||||
|     "Message Pinning": "Message Pinning", | ||||
|     "Custom user status messages": "Custom user status messages", | ||||
|     "Increase performance by only loading room members on first view": "Increase performance by only loading room members on first view", | ||||
|     "Group & filter rooms by custom tags (refresh to apply changes)": "Group & filter rooms by custom tags (refresh to apply changes)", | ||||
|     "Backup of encryption keys to server": "Backup of encryption keys to server", | ||||
|     "Render simple counters in room header": "Render simple counters in room header", | ||||
|     "Two-way device verification using short text": "Two-way device verification using short text", | ||||
|  | @ -391,6 +391,7 @@ | |||
|     "Backup has an <validity>invalid</validity> signature from <verify>unverified</verify> device <device></device>": "Backup has an <validity>invalid</validity> signature from <verify>unverified</verify> device <device></device>", | ||||
|     "Verify...": "Verify...", | ||||
|     "Backup is not signed by any of your devices": "Backup is not signed by any of your devices", | ||||
|     "This backup is trusted because it has been restored on this device": "This backup is trusted because it has been restored on this device", | ||||
|     "Backup version: ": "Backup version: ", | ||||
|     "Algorithm: ": "Algorithm: ", | ||||
|     "Restore backup": "Restore backup", | ||||
|  | @ -482,8 +483,6 @@ | |||
|     "Identity Server is": "Identity Server is", | ||||
|     "Access Token:": "Access Token:", | ||||
|     "click to reveal": "click to reveal", | ||||
|     "Lazy loading members not supported": "Lazy loading members not supported", | ||||
|     "Lazy loading is not supported by your current homeserver.": "Lazy loading is not supported by your current homeserver.", | ||||
|     "Labs": "Labs", | ||||
|     "Notifications": "Notifications", | ||||
|     "Start automatically after system login": "Start automatically after system login", | ||||
|  | @ -1101,11 +1100,12 @@ | |||
|     "\"%(RoomName)s\" contains devices that you haven't seen before.": "\"%(RoomName)s\" contains devices that you haven't seen before.", | ||||
|     "Unknown devices": "Unknown devices", | ||||
|     "Unable to load backup status": "Unable to load backup status", | ||||
|     "Recovery Key Mismatch": "Recovery Key Mismatch", | ||||
|     "Backup could not be decrypted with this key: please verify that you entered the correct recovery key.": "Backup could not be decrypted with this key: please verify that you entered the correct recovery key.", | ||||
|     "Incorrect Recovery Passphrase": "Incorrect Recovery Passphrase", | ||||
|     "Backup could not be decrypted with this passphrase: please verify that you entered the correct recovery passphrase.": "Backup could not be decrypted with this passphrase: please verify that you entered the correct recovery passphrase.", | ||||
|     "Unable to restore backup": "Unable to restore backup", | ||||
|     "No backup found!": "No backup found!", | ||||
|     "Error Restoring Backup": "Error Restoring Backup", | ||||
|     "Backup could not be decrypted with this key: please verify that you entered the correct recovery key.": "Backup could not be decrypted with this key: please verify that you entered the correct recovery key.", | ||||
|     "Backup could not be decrypted with this passphrase: please verify that you entered the correct recovery passphrase.": "Backup could not be decrypted with this passphrase: please verify that you entered the correct recovery passphrase.", | ||||
|     "Backup Restored": "Backup Restored", | ||||
|     "Failed to decrypt %(failedCount)s sessions!": "Failed to decrypt %(failedCount)s sessions!", | ||||
|     "Restored %(sessionCount)s session keys": "Restored %(sessionCount)s session keys", | ||||
|  |  | |||
|  | @ -21,7 +21,6 @@ import { | |||
|     NotificationBodyEnabledController, | ||||
|     NotificationsEnabledController, | ||||
| } from "./controllers/NotificationControllers"; | ||||
| import LazyLoadingController from "./controllers/LazyLoadingController"; | ||||
| import CustomStatusController from "./controllers/CustomStatusController"; | ||||
| 
 | ||||
| // These are just a bunch of helper arrays to avoid copy/pasting a bunch of times
 | ||||
|  | @ -100,12 +99,11 @@ export const SETTINGS = { | |||
|         default: false, | ||||
|         controller: new CustomStatusController(), | ||||
|     }, | ||||
|     "feature_lazyloading": { | ||||
|     "feature_custom_tags": { | ||||
|         isFeature: true, | ||||
|         displayName: _td("Increase performance by only loading room members on first view"), | ||||
|         displayName: _td("Group & filter rooms by custom tags (refresh to apply changes)"), | ||||
|         supportedLevels: LEVELS_FEATURE, | ||||
|         controller: new LazyLoadingController(), | ||||
|         default: true, | ||||
|         default: false, | ||||
|     }, | ||||
|     "feature_keybackup": { | ||||
|         isFeature: true, | ||||
|  |  | |||
|  | @ -1,29 +0,0 @@ | |||
| /* | ||||
| Copyright 2018 New Vector | ||||
| 
 | ||||
| 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 SettingController from "./SettingController"; | ||||
| import MatrixClientPeg from "../../MatrixClientPeg"; | ||||
| import PlatformPeg from "../../PlatformPeg"; | ||||
| 
 | ||||
| export default class LazyLoadingController extends SettingController { | ||||
|     async onChange(level, roomId, newValue) { | ||||
|         if (!PlatformPeg.get()) return; | ||||
| 
 | ||||
|         MatrixClientPeg.get().stopClient(); | ||||
|         await MatrixClientPeg.get().store.deleteAllData(); | ||||
|         PlatformPeg.get().reload(); | ||||
|     } | ||||
| } | ||||
|  | @ -18,6 +18,7 @@ import * as RoomNotifs from '../RoomNotifs'; | |||
| import RoomListStore from './RoomListStore'; | ||||
| import EventEmitter from 'events'; | ||||
| import { throttle } from "lodash"; | ||||
| import SettingsStore from "../settings/SettingsStore"; | ||||
| 
 | ||||
| const STANDARD_TAGS_REGEX = /^(m\.(favourite|lowpriority|server_notice)|im\.vector\.fake\.(invite|recent|direct|archived))$/; | ||||
| 
 | ||||
|  | @ -50,6 +51,7 @@ class CustomRoomTagStore extends EventEmitter { | |||
|         super(); | ||||
|         // Initialise state
 | ||||
|         this._state = {tags: {}}; | ||||
| 
 | ||||
|         // as RoomListStore gets updated by every timeline event
 | ||||
|         // throttle this to only run every 500ms
 | ||||
|         this._getUpdatedTags = throttle( | ||||
|  | @ -133,6 +135,10 @@ class CustomRoomTagStore extends EventEmitter { | |||
|     } | ||||
| 
 | ||||
|     _getUpdatedTags() { | ||||
|         if (!SettingsStore.isFeatureEnabled("feature_custom_tags")) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         const newTagNames = Object.keys(RoomListStore.getRoomLists()) | ||||
|             .filter((tagName) => { | ||||
|                 return !tagName.match(STANDARD_TAGS_REGEX); | ||||
|  |  | |||
|  | @ -202,6 +202,8 @@ class RoomListStore extends Store { | |||
|         // If somehow we dispatched a RoomListActions.tagRoom.failure before a MatrixActions.sync
 | ||||
|         if (!this._matrixClient) return; | ||||
| 
 | ||||
|         const isCustomTagsEnabled = SettingsStore.isFeatureEnabled("feature_custom_tags"); | ||||
| 
 | ||||
|         this._matrixClient.getRooms().forEach((room, index) => { | ||||
|             const myUserId = this._matrixClient.getUserId(); | ||||
|             const membership = room.getMyMembership(); | ||||
|  | @ -226,7 +228,7 @@ class RoomListStore extends Store { | |||
| 
 | ||||
|                 // ignore any m. tag names we don't know about
 | ||||
|                 tagNames = tagNames.filter((t) => { | ||||
|                     return !t.startsWith('m.') || lists[t] !== undefined; | ||||
|                     return (isCustomTagsEnabled && !t.startsWith('m.')) || lists[t] !== undefined; | ||||
|                 }); | ||||
| 
 | ||||
|                 if (tagNames.length) { | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Travis Ralston
						Travis Ralston