Merge pull request #2685 from matrix-org/travis/settings-cleanup-1

Maintenance: Clean up some stuff around settings
pull/21833/head
Travis Ralston 2019-02-25 11:53:23 -07:00 committed by GitHub
commit 9a42d2edee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 243 additions and 241 deletions

View File

@ -150,16 +150,16 @@
@import "./views/settings/_Notifications.scss"; @import "./views/settings/_Notifications.scss";
@import "./views/settings/_PhoneNumbers.scss"; @import "./views/settings/_PhoneNumbers.scss";
@import "./views/settings/_ProfileSettings.scss"; @import "./views/settings/_ProfileSettings.scss";
@import "./views/settings/tabs/_GeneralRoomSettingsTab.scss";
@import "./views/settings/tabs/_GeneralUserSettingsTab.scss";
@import "./views/settings/tabs/_HelpSettingsTab.scss";
@import "./views/settings/tabs/_NotificationSettingsTab.scss";
@import "./views/settings/tabs/_PreferencesSettingsTab.scss";
@import "./views/settings/tabs/_RolesRoomSettingsTab.scss";
@import "./views/settings/tabs/_SecurityRoomSettingsTab.scss";
@import "./views/settings/tabs/_SecuritySettingsTab.scss";
@import "./views/settings/tabs/_SettingsTab.scss"; @import "./views/settings/tabs/_SettingsTab.scss";
@import "./views/settings/tabs/_VoiceSettingsTab.scss"; @import "./views/settings/tabs/room/_GeneralRoomSettingsTab.scss";
@import "./views/settings/tabs/room/_RolesRoomSettingsTab.scss";
@import "./views/settings/tabs/room/_SecurityRoomSettingsTab.scss";
@import "./views/settings/tabs/user/_GeneralUserSettingsTab.scss";
@import "./views/settings/tabs/user/_HelpUserSettingsTab.scss";
@import "./views/settings/tabs/user/_NotificationUserSettingsTab.scss";
@import "./views/settings/tabs/user/_PreferencesUserSettingsTab.scss";
@import "./views/settings/tabs/user/_SecurityUserSettingsTab.scss";
@import "./views/settings/tabs/user/_VoiceUserSettingsTab.scss";
@import "./views/verification/_VerificationShowSas.scss"; @import "./views/verification/_VerificationShowSas.scss";
@import "./views/voip/_CallView.scss"; @import "./views/voip/_CallView.scss";
@import "./views/voip/_IncomingCallbox.scss"; @import "./views/voip/_IncomingCallbox.scss";

View File

@ -14,11 +14,11 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
.mx_HelpSettingsTab_debugButton { .mx_HelpUserSettingsTab_debugButton {
margin-bottom: 5px; margin-bottom: 5px;
margin-top: 5px; margin-top: 5px;
} }
.mx_HelpSettingsTab span.mx_AccessibleButton { .mx_HelpUserSettingsTab span.mx_AccessibleButton {
word-break: break-word; word-break: break-word;
} }

View File

@ -14,6 +14,6 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
.mx_NotificationSettingsTab .mx_SettingsTab_heading { .mx_NotificationUserSettingsTab .mx_SettingsTab_heading {
margin-bottom: 10px; // Give some spacing between the title and the first elements margin-bottom: 10px; // Give some spacing between the title and the first elements
} }

View File

@ -14,11 +14,11 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
.mx_PreferencesSettingsTab .mx_Field { .mx_PreferencesUserSettingsTab .mx_Field {
margin-right: 100px; // Align with the rest of the controls margin-right: 100px; // Align with the rest of the controls
} }
.mx_PreferencesSettingsTab .mx_Field input { .mx_PreferencesUserSettingsTab .mx_Field input {
display: block; display: block;
// Subtract 10px padding on left and right // Subtract 10px padding on left and right

View File

@ -14,40 +14,40 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
.mx_SecuritySettingsTab .mx_DevicesPanel { .mx_SecurityUserSettingsTab .mx_DevicesPanel {
// Normally the panel is 880px, however this can easily overflow the container. // Normally the panel is 880px, however this can easily overflow the container.
// TODO: Fix the table to not be squishy // TODO: Fix the table to not be squishy
width: auto; width: auto;
max-width: 880px; max-width: 880px;
} }
.mx_SecuritySettingsTab_deviceInfo { .mx_SecurityUserSettingsTab_deviceInfo {
display: table; display: table;
padding-left: 0; padding-left: 0;
} }
.mx_SecuritySettingsTab_deviceInfo > li { .mx_SecurityUserSettingsTab_deviceInfo > li {
display: table-row; display: table-row;
} }
.mx_SecuritySettingsTab_deviceInfo > li > label, .mx_SecurityUserSettingsTab_deviceInfo > li > label,
.mx_SecuritySettingsTab_deviceInfo > li > span { .mx_SecurityUserSettingsTab_deviceInfo > li > span {
display: table-cell; display: table-cell;
padding-right: 1em; padding-right: 1em;
} }
.mx_SecuritySettingsTab_importExportButtons .mx_AccessibleButton { .mx_SecurityUserSettingsTab_importExportButtons .mx_AccessibleButton {
margin-right: 10px; margin-right: 10px;
} }
.mx_SecuritySettingsTab_importExportButtons { .mx_SecurityUserSettingsTab_importExportButtons {
margin-bottom: 15px; margin-bottom: 15px;
} }
.mx_SecuritySettingsTab_ignoredUser { .mx_SecurityUserSettingsTab_ignoredUser {
margin-bottom: 5px; margin-bottom: 5px;
} }
.mx_SecuritySettingsTab_ignoredUser .mx_AccessibleButton { .mx_SecurityUserSettingsTab_ignoredUser .mx_AccessibleButton {
margin-right: 10px; margin-right: 10px;
} }

View File

@ -14,15 +14,15 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
.mx_VoiceSettingsTab .mx_Field select { .mx_VoiceUserSettingsTab .mx_Field select {
width: 100%; width: 100%;
max-width: 100%; max-width: 100%;
} }
.mx_VoiceSettingsTab .mx_Field { .mx_VoiceUserSettingsTab .mx_Field {
margin-right: 100px; // align with the rest of the fields margin-right: 100px; // align with the rest of the fields
} }
.mx_VoiceSettingsTab_missingMediaPermissions { .mx_VoiceUserSettingsTab_missingMediaPermissions {
margin-bottom: 15px; margin-bottom: 15px;
} }

View File

@ -18,10 +18,10 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import {Tab, TabbedView} from "../../structures/TabbedView"; import {Tab, TabbedView} from "../../structures/TabbedView";
import {_t, _td} from "../../../languageHandler"; import {_t, _td} from "../../../languageHandler";
import AdvancedRoomSettingsTab from "../settings/tabs/AdvancedRoomSettingsTab"; import AdvancedRoomSettingsTab from "../settings/tabs/room/AdvancedRoomSettingsTab";
import RolesRoomSettingsTab from "../settings/tabs/RolesRoomSettingsTab"; import RolesRoomSettingsTab from "../settings/tabs/room/RolesRoomSettingsTab";
import GeneralRoomSettingsTab from "../settings/tabs/GeneralRoomSettingsTab"; import GeneralRoomSettingsTab from "../settings/tabs/room/GeneralRoomSettingsTab";
import SecurityRoomSettingsTab from "../settings/tabs/SecurityRoomSettingsTab"; import SecurityRoomSettingsTab from "../settings/tabs/room/SecurityRoomSettingsTab";
import sdk from "../../../index"; import sdk from "../../../index";
export default class RoomSettingsDialog extends React.Component { export default class RoomSettingsDialog extends React.Component {

View File

@ -18,15 +18,15 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import {Tab, TabbedView} from "../../structures/TabbedView"; import {Tab, TabbedView} from "../../structures/TabbedView";
import {_t, _td} from "../../../languageHandler"; import {_t, _td} from "../../../languageHandler";
import GeneralUserSettingsTab from "../settings/tabs/GeneralUserSettingsTab"; import GeneralUserSettingsTab from "../settings/tabs/user/GeneralUserSettingsTab";
import SettingsStore from "../../../settings/SettingsStore"; import SettingsStore from "../../../settings/SettingsStore";
import LabsSettingsTab from "../settings/tabs/LabsSettingsTab"; import LabsUserSettingsTab from "../settings/tabs/user/LabsUserSettingsTab";
import SecuritySettingsTab from "../settings/tabs/SecuritySettingsTab"; import SecurityUserSettingsTab from "../settings/tabs/user/SecurityUserSettingsTab";
import NotificationSettingsTab from "../settings/tabs/NotificationSettingsTab"; import NotificationUserSettingsTab from "../settings/tabs/user/NotificationUserSettingsTab";
import PreferencesSettingsTab from "../settings/tabs/PreferencesSettingsTab"; import PreferencesUserSettingsTab from "../settings/tabs/user/PreferencesUserSettingsTab";
import VoiceSettingsTab from "../settings/tabs/VoiceSettingsTab"; import VoiceUserSettingsTab from "../settings/tabs/user/VoiceUserSettingsTab";
import HelpSettingsTab from "../settings/tabs/HelpSettingsTab"; import HelpUserSettingsTab from "../settings/tabs/user/HelpUserSettingsTab";
import FlairSettingsTab from "../settings/tabs/FlairSettingsTab"; import FlairUserSettingsTab from "../settings/tabs/user/FlairUserSettingsTab";
import sdk from "../../../index"; import sdk from "../../../index";
export default class UserSettingsDialog extends React.Component { export default class UserSettingsDialog extends React.Component {
@ -45,39 +45,39 @@ export default class UserSettingsDialog extends React.Component {
tabs.push(new Tab( tabs.push(new Tab(
_td("Flair"), _td("Flair"),
"mx_UserSettingsDialog_flairIcon", "mx_UserSettingsDialog_flairIcon",
<FlairSettingsTab />, <FlairUserSettingsTab />,
)); ));
tabs.push(new Tab( tabs.push(new Tab(
_td("Notifications"), _td("Notifications"),
"mx_UserSettingsDialog_bellIcon", "mx_UserSettingsDialog_bellIcon",
<NotificationSettingsTab />, <NotificationUserSettingsTab />,
)); ));
tabs.push(new Tab( tabs.push(new Tab(
_td("Preferences"), _td("Preferences"),
"mx_UserSettingsDialog_preferencesIcon", "mx_UserSettingsDialog_preferencesIcon",
<PreferencesSettingsTab />, <PreferencesUserSettingsTab />,
)); ));
tabs.push(new Tab( tabs.push(new Tab(
_td("Voice & Video"), _td("Voice & Video"),
"mx_UserSettingsDialog_voiceIcon", "mx_UserSettingsDialog_voiceIcon",
<VoiceSettingsTab />, <VoiceUserSettingsTab />,
)); ));
tabs.push(new Tab( tabs.push(new Tab(
_td("Security & Privacy"), _td("Security & Privacy"),
"mx_UserSettingsDialog_securityIcon", "mx_UserSettingsDialog_securityIcon",
<SecuritySettingsTab />, <SecurityUserSettingsTab />,
)); ));
if (SettingsStore.getLabsFeatures().length > 0) { if (SettingsStore.getLabsFeatures().length > 0) {
tabs.push(new Tab( tabs.push(new Tab(
_td("Labs"), _td("Labs"),
"mx_UserSettingsDialog_labsIcon", "mx_UserSettingsDialog_labsIcon",
<LabsSettingsTab />, <LabsUserSettingsTab />,
)); ));
} }
tabs.push(new Tab( tabs.push(new Tab(
_td("Help & About"), _td("Help & About"),
"mx_UserSettingsDialog_helpIcon", "mx_UserSettingsDialog_helpIcon",
<HelpSettingsTab closeSettingsFn={this.props.onFinished} />, <HelpUserSettingsTab closeSettingsFn={this.props.onFinished} />,
)); ));
return tabs; return tabs;

View File

@ -68,7 +68,9 @@ export default React.createClass({
render() { render() {
const GroupTile = sdk.getComponent('groups.GroupTile'); const GroupTile = sdk.getComponent('groups.GroupTile');
return <div className="mx_GroupPublicity_toggle"> return <div className="mx_GroupPublicity_toggle">
<GroupTile groupId={this.props.groupId} showDescription={false} avatarHeight={40} /> <GroupTile groupId={this.props.groupId} showDescription={false}
avatarHeight={40} draggable={false}
/>
<ToggleSwitch checked={this.state.isGroupPublicised} <ToggleSwitch checked={this.state.isGroupPublicised}
disabled={!this.state.ready || this.state.busy} disabled={!this.state.ready || this.state.busy}
onChange={this._onPublicityToggle} /> onChange={this._onPublicityToggle} />

View File

@ -33,6 +33,7 @@ const GroupTile = React.createClass({
showDescription: PropTypes.bool, showDescription: PropTypes.bool,
// Height of the group avatar in pixels // Height of the group avatar in pixels
avatarHeight: PropTypes.number, avatarHeight: PropTypes.number,
draggable: PropTypes.bool,
}, },
contextTypes: { contextTypes: {
@ -49,6 +50,7 @@ const GroupTile = React.createClass({
return { return {
showDescription: true, showDescription: true,
avatarHeight: 50, avatarHeight: 50,
draggable: true,
}; };
}, },
@ -78,11 +80,21 @@ const GroupTile = React.createClass({
<div className="mx_GroupTile_desc">{ profile.shortDescription }</div> : <div className="mx_GroupTile_desc">{ profile.shortDescription }</div> :
<div />; <div />;
const httpUrl = profile.avatarUrl ? this.context.matrixClient.mxcUrlToHttp( const httpUrl = profile.avatarUrl ? this.context.matrixClient.mxcUrlToHttp(
profile.avatarUrl, avatarHeight, avatarHeight, "crop", profile.avatarUrl, avatarHeight, avatarHeight, "crop") : null;
) : null;
// XXX: Use onMouseDown as a workaround for https://github.com/atlassian/react-beautiful-dnd/issues/273 let avatarElement = (
// instead of onClick. Otherwise we experience https://github.com/vector-im/riot-web/issues/6156 <div className="mx_GroupTile_avatar">
return <AccessibleButton className="mx_GroupTile" onMouseDown={this.onMouseDown} onClick={nop}> <BaseAvatar
name={name}
idName={this.props.groupId}
url={httpUrl}
width={avatarHeight}
height={avatarHeight} />
</div>
);
if (this.props.draggable) {
const avatarClone = avatarElement;
avatarElement = (
<Droppable droppableId="my-groups-droppable" type="draggable-TagTile"> <Droppable droppableId="my-groups-droppable" type="draggable-TagTile">
{ (droppableProvided, droppableSnapshot) => ( { (droppableProvided, droppableSnapshot) => (
<div ref={droppableProvided.innerRef}> <div ref={droppableProvided.innerRef}>
@ -99,33 +111,23 @@ const GroupTile = React.createClass({
{...provided.draggableProps} {...provided.draggableProps}
{...provided.dragHandleProps} {...provided.dragHandleProps}
> >
<div className="mx_GroupTile_avatar"> {avatarClone}
<BaseAvatar
name={name}
idName={this.props.groupId}
url={httpUrl}
width={avatarHeight}
height={avatarHeight} />
</div>
</div> </div>
{ /* Instead of a blank placeholder, use a copy of the avatar itself. */ } { /* Instead of a blank placeholder, use a copy of the avatar itself. */ }
{ provided.placeholder ? { provided.placeholder ? avatarClone : <div /> }
<div className="mx_GroupTile_avatar">
<BaseAvatar
name={name}
idName={this.props.groupId}
url={httpUrl}
width={avatarHeight}
height={avatarHeight} />
</div> :
<div />
}
</div> </div>
) } ) }
</Draggable> </Draggable>
</div> </div>
) } ) }
</Droppable> </Droppable>
);
}
// XXX: Use onMouseDown as a workaround for https://github.com/atlassian/react-beautiful-dnd/issues/273
// instead of onClick. Otherwise we experience https://github.com/vector-im/riot-web/issues/6156
return <AccessibleButton className="mx_GroupTile" onMouseDown={this.onMouseDown} onClick={nop}>
{ avatarElement }
<div className="mx_GroupTile_profile"> <div className="mx_GroupTile_profile">
<div className="mx_GroupTile_name">{ name }</div> <div className="mx_GroupTile_name">{ name }</div>
{ descElement } { descElement }

View File

@ -16,11 +16,11 @@ limitations under the License.
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import {_t} from "../../../../languageHandler"; import {_t} from "../../../../../languageHandler";
import MatrixClientPeg from "../../../../MatrixClientPeg"; import MatrixClientPeg from "../../../../../MatrixClientPeg";
import sdk from "../../../../index"; import sdk from "../../../../..";
import AccessibleButton from "../../elements/AccessibleButton"; import AccessibleButton from "../../../elements/AccessibleButton";
import Modal from "../../../../Modal"; import Modal from "../../../../../Modal";
export default class AdvancedRoomSettingsTab extends React.Component { export default class AdvancedRoomSettingsTab extends React.Component {
static propTypes = { static propTypes = {

View File

@ -16,14 +16,14 @@ limitations under the License.
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import {_t} from "../../../../languageHandler"; import {_t} from "../../../../../languageHandler";
import RoomProfileSettings from "../../room_settings/RoomProfileSettings"; import RoomProfileSettings from "../../../room_settings/RoomProfileSettings";
import MatrixClientPeg from "../../../../MatrixClientPeg"; import MatrixClientPeg from "../../../../../MatrixClientPeg";
import sdk from "../../../../index"; import sdk from "../../../../..";
import AccessibleButton from "../../elements/AccessibleButton"; import AccessibleButton from "../../../elements/AccessibleButton";
import {MatrixClient} from "matrix-js-sdk"; import {MatrixClient} from "matrix-js-sdk";
import dis from "../../../../dispatcher"; import dis from "../../../../../dispatcher";
import LabelledToggleSwitch from "../../elements/LabelledToggleSwitch"; import LabelledToggleSwitch from "../../../elements/LabelledToggleSwitch";
export default class GeneralRoomSettingsTab extends React.Component { export default class GeneralRoomSettingsTab extends React.Component {
static childContextTypes = { static childContextTypes = {

View File

@ -16,11 +16,11 @@ limitations under the License.
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import {_t, _td} from "../../../../languageHandler"; import {_t, _td} from "../../../../../languageHandler";
import MatrixClientPeg from "../../../../MatrixClientPeg"; import MatrixClientPeg from "../../../../../MatrixClientPeg";
import sdk from "../../../../index"; import sdk from "../../../../..";
import AccessibleButton from "../../elements/AccessibleButton"; import AccessibleButton from "../../../elements/AccessibleButton";
import Modal from "../../../../Modal"; import Modal from "../../../../../Modal";
const plEventsToLabels = { const plEventsToLabels = {
// These will be translated for us later. // These will be translated for us later.

View File

@ -16,11 +16,11 @@ limitations under the License.
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import {_t} from "../../../../languageHandler"; import {_t} from "../../../../../languageHandler";
import MatrixClientPeg from "../../../../MatrixClientPeg"; import MatrixClientPeg from "../../../../../MatrixClientPeg";
import sdk from "../../../../index"; import sdk from "../../../../..";
import LabelledToggleSwitch from "../../elements/LabelledToggleSwitch"; import LabelledToggleSwitch from "../../../elements/LabelledToggleSwitch";
import {SettingLevel} from "../../../../settings/SettingsStore"; import {SettingLevel} from "../../../../../settings/SettingsStore";
export default class SecurityRoomSettingsTab extends React.Component { export default class SecurityRoomSettingsTab extends React.Component {
static propTypes = { static propTypes = {
@ -188,7 +188,7 @@ export default class SecurityRoomSettingsTab extends React.Component {
if (joinRule !== 'public' && guestAccess === 'forbidden') { if (joinRule !== 'public' && guestAccess === 'forbidden') {
guestWarning = ( guestWarning = (
<div className='mx_SecurityRoomSettingsTab_warning'> <div className='mx_SecurityRoomSettingsTab_warning'>
<img src={require("../../../../../res/img/warning.svg")} width={15} height={15} /> <img src={require("../../../../../../res/img/warning.svg")} width={15} height={15} />
<span> <span>
{_t("Guests cannot join this room even if explicitly invited.")}&nbsp; {_t("Guests cannot join this room even if explicitly invited.")}&nbsp;
<a href="" onClick={this._fixGuestAccess}>{_t("Click here to fix")}</a> <a href="" onClick={this._fixGuestAccess}>{_t("Click here to fix")}</a>
@ -201,7 +201,7 @@ export default class SecurityRoomSettingsTab extends React.Component {
if (joinRule === 'public' && !hasAliases) { if (joinRule === 'public' && !hasAliases) {
aliasWarning = ( aliasWarning = (
<div className='mx_SecurityRoomSettingsTab_warning'> <div className='mx_SecurityRoomSettingsTab_warning'>
<img src={require("../../../../../res/img/warning.svg")} width={15} height={15} /> <img src={require("../../../../../../res/img/warning.svg")} width={15} height={15} />
<span> <span>
{_t("To link to this room, please add an alias.")} {_t("To link to this room, please add an alias.")}
</span> </span>

View File

@ -15,14 +15,13 @@ limitations under the License.
*/ */
import React from 'react'; import React from 'react';
import {_t} from "../../../../languageHandler"; import {_t} from "../../../../../languageHandler";
import {DragDropContext} from "react-beautiful-dnd"; import GroupUserSettings from "../../../groups/GroupUserSettings";
import GroupUserSettings from "../../groups/GroupUserSettings"; import MatrixClientPeg from "../../../../../MatrixClientPeg";
import MatrixClientPeg from "../../../../MatrixClientPeg";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import {MatrixClient} from "matrix-js-sdk"; import {MatrixClient} from "matrix-js-sdk";
export default class FlairSettingsTab extends React.Component { export default class FlairUserSettingsTab extends React.Component {
static childContextTypes = { static childContextTypes = {
matrixClient: PropTypes.instanceOf(MatrixClient), matrixClient: PropTypes.instanceOf(MatrixClient),
}; };
@ -42,9 +41,7 @@ export default class FlairSettingsTab extends React.Component {
<div className="mx_SettingsTab"> <div className="mx_SettingsTab">
<span className="mx_SettingsTab_heading">{_t("Flair")}</span> <span className="mx_SettingsTab_heading">{_t("Flair")}</span>
<div className="mx_SettingsTab_section"> <div className="mx_SettingsTab_section">
<DragDropContext>
<GroupUserSettings /> <GroupUserSettings />
</DragDropContext>
</div> </div>
</div> </div>
); );

View File

@ -15,21 +15,21 @@ limitations under the License.
*/ */
import React from 'react'; import React from 'react';
import {_t} from "../../../../languageHandler"; import {_t} from "../../../../../languageHandler";
import ProfileSettings from "../ProfileSettings"; import ProfileSettings from "../../ProfileSettings";
import EmailAddresses from "../EmailAddresses"; import EmailAddresses from "../../EmailAddresses";
import PhoneNumbers from "../PhoneNumbers"; import PhoneNumbers from "../../PhoneNumbers";
import Field from "../../elements/Field"; import Field from "../../../elements/Field";
import * as languageHandler from "../../../../languageHandler"; import * as languageHandler from "../../../../../languageHandler";
import {SettingLevel} from "../../../../settings/SettingsStore"; import {SettingLevel} from "../../../../../settings/SettingsStore";
import SettingsStore from "../../../../settings/SettingsStore"; import SettingsStore from "../../../../../settings/SettingsStore";
import LanguageDropdown from "../../elements/LanguageDropdown"; import LanguageDropdown from "../../../elements/LanguageDropdown";
import AccessibleButton from "../../elements/AccessibleButton"; import AccessibleButton from "../../../elements/AccessibleButton";
import DeactivateAccountDialog from "../../dialogs/DeactivateAccountDialog"; import DeactivateAccountDialog from "../../../dialogs/DeactivateAccountDialog";
const PlatformPeg = require("../../../../PlatformPeg"); const PlatformPeg = require("../../../../../PlatformPeg");
const sdk = require('../../../../index'); const sdk = require('../../../../..');
const Modal = require("../../../../Modal"); const Modal = require("../../../../../Modal");
const dis = require("../../../../dispatcher"); const dis = require("../../../../../dispatcher");
export default class GeneralUserSettingsTab extends React.Component { export default class GeneralUserSettingsTab extends React.Component {
constructor() { constructor() {

View File

@ -16,15 +16,15 @@ limitations under the License.
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import {_t, getCurrentLanguage} from "../../../../languageHandler"; import {_t, getCurrentLanguage} from "../../../../../languageHandler";
import MatrixClientPeg from "../../../../MatrixClientPeg"; import MatrixClientPeg from "../../../../../MatrixClientPeg";
import AccessibleButton from "../../elements/AccessibleButton"; import AccessibleButton from "../../../elements/AccessibleButton";
import SdkConfig from "../../../../SdkConfig"; import SdkConfig from "../../../../../SdkConfig";
import createRoom from "../../../../createRoom"; import createRoom from "../../../../../createRoom";
const packageJson = require('../../../../../package.json'); const packageJson = require('../../../../../../package.json');
const Modal = require("../../../../Modal"); const Modal = require("../../../../../Modal");
const sdk = require("../../../../index"); const sdk = require("../../../../..");
const PlatformPeg = require("../../../../PlatformPeg"); const PlatformPeg = require("../../../../../PlatformPeg");
// if this looks like a release, use the 'version' from package.json; else use // if this looks like a release, use the 'version' from package.json; else use
// the git sha. Prepend version with v, to look like riot-web version // the git sha. Prepend version with v, to look like riot-web version
@ -45,7 +45,7 @@ const ghVersionLabel = function(repo, token='') {
return <a target="_blank" rel="noopener" href={url}>{ token }</a>; return <a target="_blank" rel="noopener" href={url}>{ token }</a>;
}; };
export default class HelpSettingsTab extends React.Component { export default class HelpUserSettingsTab extends React.Component {
static propTypes = { static propTypes = {
closeSettingsFn: PropTypes.func.isRequired, closeSettingsFn: PropTypes.func.isRequired,
}; };
@ -117,7 +117,7 @@ export default class HelpSettingsTab extends React.Component {
} }
return ( return (
<div className='mx_SettingsTab_section mx_HelpSettingsTab_versions'> <div className='mx_SettingsTab_section mx_HelpUserSettingsTab_versions'>
<span className='mx_SettingsTab_subheading'>{_t("Legal")}</span> <span className='mx_SettingsTab_subheading'>{_t("Legal")}</span>
<div className='mx_SettingsTab_subsectionText'> <div className='mx_SettingsTab_subsectionText'>
{legalLinks} {legalLinks}
@ -190,7 +190,7 @@ export default class HelpSettingsTab extends React.Component {
} }
return ( return (
<div className="mx_SettingsTab mx_HelpSettingsTab"> <div className="mx_SettingsTab mx_HelpUserSettingsTab">
<div className="mx_SettingsTab_heading">{_t("Help & About")}</div> <div className="mx_SettingsTab_heading">{_t("Help & About")}</div>
<div className="mx_SettingsTab_section"> <div className="mx_SettingsTab_section">
<span className='mx_SettingsTab_subheading'>{_t('Bug reporting')}</span> <span className='mx_SettingsTab_subheading'>{_t('Bug reporting')}</span>
@ -203,12 +203,12 @@ export default class HelpSettingsTab extends React.Component {
"other users. They do not contain messages.", "other users. They do not contain messages.",
) )
} }
<div className='mx_HelpSettingsTab_debugButton'> <div className='mx_HelpUserSettingsTab_debugButton'>
<AccessibleButton onClick={this._onBugReport} kind='primary'> <AccessibleButton onClick={this._onBugReport} kind='primary'>
{_t("Submit debug logs")} {_t("Submit debug logs")}
</AccessibleButton> </AccessibleButton>
</div> </div>
<div className='mx_HelpSettingsTab_debugButton'> <div className='mx_HelpUserSettingsTab_debugButton'>
<AccessibleButton onClick={this._onClearCacheAndReload} kind='danger'> <AccessibleButton onClick={this._onClearCacheAndReload} kind='danger'>
{_t("Clear Cache and Reload")} {_t("Clear Cache and Reload")}
</AccessibleButton> </AccessibleButton>
@ -221,7 +221,7 @@ export default class HelpSettingsTab extends React.Component {
{faqText} {faqText}
</div> </div>
</div> </div>
<div className='mx_SettingsTab_section mx_HelpSettingsTab_versions'> <div className='mx_SettingsTab_section mx_HelpUserSettingsTab_versions'>
<span className='mx_SettingsTab_subheading'>{_t("Versions")}</span> <span className='mx_SettingsTab_subheading'>{_t("Versions")}</span>
<div className='mx_SettingsTab_subsectionText'> <div className='mx_SettingsTab_subsectionText'>
{_t("matrix-react-sdk version:")} {reactSdkVersion}<br /> {_t("matrix-react-sdk version:")} {reactSdkVersion}<br />
@ -232,7 +232,7 @@ export default class HelpSettingsTab extends React.Component {
</div> </div>
{this._renderLegal()} {this._renderLegal()}
{this._renderCredits()} {this._renderCredits()}
<div className='mx_SettingsTab_section mx_HelpSettingsTab_versions'> <div className='mx_SettingsTab_section mx_HelpUserSettingsTab_versions'>
<span className='mx_SettingsTab_subheading'>{_t("Advanced")}</span> <span className='mx_SettingsTab_subheading'>{_t("Advanced")}</span>
<div className='mx_SettingsTab_subsectionText'> <div className='mx_SettingsTab_subsectionText'>
{_t("Homeserver is")} {MatrixClientPeg.get().getHomeserverUrl()}<br /> {_t("Homeserver is")} {MatrixClientPeg.get().getHomeserverUrl()}<br />

View File

@ -15,11 +15,11 @@ limitations under the License.
*/ */
import React from 'react'; import React from 'react';
import {_t} from "../../../../languageHandler"; import {_t} from "../../../../../languageHandler";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import SettingsStore, {SettingLevel} from "../../../../settings/SettingsStore"; import SettingsStore, {SettingLevel} from "../../../../../settings/SettingsStore";
import LabelledToggleSwitch from "../../elements/LabelledToggleSwitch"; import LabelledToggleSwitch from "../../../elements/LabelledToggleSwitch";
const sdk = require("../../../../index"); const sdk = require("../../../../..");
export class LabsSettingToggle extends React.Component { export class LabsSettingToggle extends React.Component {
static propTypes = { static propTypes = {
@ -38,7 +38,7 @@ export class LabsSettingToggle extends React.Component {
} }
} }
export default class LabsSettingsTab extends React.Component { export default class LabsUserSettingsTab extends React.Component {
constructor() { constructor() {
super(); super();
} }

View File

@ -15,10 +15,10 @@ limitations under the License.
*/ */
import React from 'react'; import React from 'react';
import {_t} from "../../../../languageHandler"; import {_t} from "../../../../../languageHandler";
const sdk = require("../../../../index"); const sdk = require("../../../../..");
export default class NotificationSettingsTab extends React.Component { export default class NotificationUserSettingsTab extends React.Component {
constructor() { constructor() {
super(); super();
} }
@ -26,7 +26,7 @@ export default class NotificationSettingsTab extends React.Component {
render() { render() {
const Notifications = sdk.getComponent("views.settings.Notifications"); const Notifications = sdk.getComponent("views.settings.Notifications");
return ( return (
<div className="mx_SettingsTab mx_NotificationSettingsTab"> <div className="mx_SettingsTab mx_NotificationUserSettingsTab">
<div className="mx_SettingsTab_heading">{_t("Notifications")}</div> <div className="mx_SettingsTab_heading">{_t("Notifications")}</div>
<div className="mx_SettingsTab_section mx_SettingsTab_subsectionText"> <div className="mx_SettingsTab_section mx_SettingsTab_subsectionText">
<Notifications /> <Notifications />

View File

@ -15,15 +15,15 @@ limitations under the License.
*/ */
import React from 'react'; import React from 'react';
import {_t} from "../../../../languageHandler"; import {_t} from "../../../../../languageHandler";
import {SettingLevel} from "../../../../settings/SettingsStore"; import {SettingLevel} from "../../../../../settings/SettingsStore";
import LabelledToggleSwitch from "../../elements/LabelledToggleSwitch"; import LabelledToggleSwitch from "../../../elements/LabelledToggleSwitch";
import SettingsStore from "../../../../settings/SettingsStore"; import SettingsStore from "../../../../../settings/SettingsStore";
import Field from "../../elements/Field"; import Field from "../../../elements/Field";
const sdk = require("../../../../index"); const sdk = require("../../../../..");
const PlatformPeg = require("../../../../PlatformPeg"); const PlatformPeg = require("../../../../../PlatformPeg");
export default class PreferencesSettingsTab extends React.Component { export default class PreferencesUserSettingsTab extends React.Component {
static COMPOSER_SETTINGS = [ static COMPOSER_SETTINGS = [
'MessageComposerInput.autoReplaceEmoji', 'MessageComposerInput.autoReplaceEmoji',
'MessageComposerInput.suggestEmoji', 'MessageComposerInput.suggestEmoji',
@ -112,6 +112,7 @@ export default class PreferencesSettingsTab extends React.Component {
onChange={this._onAutoLaunchChange} onChange={this._onAutoLaunchChange}
label={_t('Start automatically after system login')} />; label={_t('Start automatically after system login')} />;
} }
let minimizeToTrayOption = null; let minimizeToTrayOption = null;
if (this.state.minimizeToTraySupported) { if (this.state.minimizeToTraySupported) {
minimizeToTrayOption = <LabelledToggleSwitch value={this.state.minimizeToTray} minimizeToTrayOption = <LabelledToggleSwitch value={this.state.minimizeToTray}
@ -120,20 +121,20 @@ export default class PreferencesSettingsTab extends React.Component {
} }
return ( return (
<div className="mx_SettingsTab mx_PreferencesSettingsTab"> <div className="mx_SettingsTab mx_PreferencesUserSettingsTab">
<div className="mx_SettingsTab_heading">{_t("Preferences")}</div> <div className="mx_SettingsTab_heading">{_t("Preferences")}</div>
<div className="mx_SettingsTab_section"> <div className="mx_SettingsTab_section">
<span className="mx_SettingsTab_subheading">{_t("Composer")}</span> <span className="mx_SettingsTab_subheading">{_t("Composer")}</span>
{this._renderGroup(PreferencesSettingsTab.COMPOSER_SETTINGS)} {this._renderGroup(PreferencesUserSettingsTab.COMPOSER_SETTINGS)}
<span className="mx_SettingsTab_subheading">{_t("Timeline")}</span> <span className="mx_SettingsTab_subheading">{_t("Timeline")}</span>
{this._renderGroup(PreferencesSettingsTab.TIMELINE_SETTINGS)} {this._renderGroup(PreferencesUserSettingsTab.TIMELINE_SETTINGS)}
<span className="mx_SettingsTab_subheading">{_t("Room list")}</span> <span className="mx_SettingsTab_subheading">{_t("Room list")}</span>
{this._renderGroup(PreferencesSettingsTab.ROOM_LIST_SETTINGS)} {this._renderGroup(PreferencesUserSettingsTab.ROOM_LIST_SETTINGS)}
<span className="mx_SettingsTab_subheading">{_t("Advanced")}</span> <span className="mx_SettingsTab_subheading">{_t("Advanced")}</span>
{this._renderGroup(PreferencesSettingsTab.ADVANCED_SETTINGS)} {this._renderGroup(PreferencesUserSettingsTab.ADVANCED_SETTINGS)}
{minimizeToTrayOption} {minimizeToTrayOption}
{autoLaunchOption} {autoLaunchOption}
<Field id={"autocompleteDelay"} label={_t('Autocomplete delay (ms)')} type='number' <Field id={"autocompleteDelay"} label={_t('Autocomplete delay (ms)')} type='number'

View File

@ -16,15 +16,15 @@ limitations under the License.
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import {_t} from "../../../../languageHandler"; import {_t} from "../../../../../languageHandler";
import {SettingLevel} from "../../../../settings/SettingsStore"; import {SettingLevel} from "../../../../../settings/SettingsStore";
import MatrixClientPeg from "../../../../MatrixClientPeg"; import MatrixClientPeg from "../../../../../MatrixClientPeg";
import * as FormattingUtils from "../../../../utils/FormattingUtils"; import * as FormattingUtils from "../../../../../utils/FormattingUtils";
import AccessibleButton from "../../elements/AccessibleButton"; import AccessibleButton from "../../../elements/AccessibleButton";
import Analytics from "../../../../Analytics"; import Analytics from "../../../../../Analytics";
import Promise from "bluebird"; import Promise from "bluebird";
import Modal from "../../../../Modal"; import Modal from "../../../../../Modal";
import sdk from "../../../../index"; import sdk from "../../../../..";
export class IgnoredUser extends React.Component { export class IgnoredUser extends React.Component {
static propTypes = { static propTypes = {
@ -38,7 +38,7 @@ export class IgnoredUser extends React.Component {
render() { render() {
return ( return (
<div className='mx_SecuritySettingsTab_ignoredUser'> <div className='mx_SecurityUserSettingsTab_ignoredUser'>
<AccessibleButton onClick={this._onUnignoreClicked} kind='primary_sm'> <AccessibleButton onClick={this._onUnignoreClicked} kind='primary_sm'>
{_t('Unignore')} {_t('Unignore')}
</AccessibleButton> </AccessibleButton>
@ -48,7 +48,7 @@ export class IgnoredUser extends React.Component {
} }
} }
export default class SecuritySettingsTab extends React.Component { export default class SecurityUserSettingsTab extends React.Component {
constructor() { constructor() {
super(); super();
@ -68,14 +68,14 @@ export default class SecuritySettingsTab extends React.Component {
_onExportE2eKeysClicked = () => { _onExportE2eKeysClicked = () => {
Modal.createTrackedDialogAsync('Export E2E Keys', '', Modal.createTrackedDialogAsync('Export E2E Keys', '',
import('../../../../async-components/views/dialogs/ExportE2eKeysDialog'), import('../../../../../async-components/views/dialogs/ExportE2eKeysDialog'),
{matrixClient: MatrixClientPeg.get()}, {matrixClient: MatrixClientPeg.get()},
); );
}; };
_onImportE2eKeysClicked = () => { _onImportE2eKeysClicked = () => {
Modal.createTrackedDialogAsync('Import E2E Keys', '', Modal.createTrackedDialogAsync('Import E2E Keys', '',
import('../../../../async-components/views/dialogs/ImportE2eKeysDialog'), import('../../../../../async-components/views/dialogs/ImportE2eKeysDialog'),
{matrixClient: MatrixClientPeg.get()}, {matrixClient: MatrixClientPeg.get()},
); );
}; };
@ -126,7 +126,7 @@ export default class SecuritySettingsTab extends React.Component {
let importExportButtons = null; let importExportButtons = null;
if (client.isCryptoEnabled()) { if (client.isCryptoEnabled()) {
importExportButtons = ( importExportButtons = (
<div className='mx_SecuritySettingsTab_importExportButtons'> <div className='mx_SecurityUserSettingsTab_importExportButtons'>
<AccessibleButton kind='primary' onClick={this._onExportE2eKeysClicked}> <AccessibleButton kind='primary' onClick={this._onExportE2eKeysClicked}>
{_t("Export E2E room keys")} {_t("Export E2E room keys")}
</AccessibleButton> </AccessibleButton>
@ -140,7 +140,7 @@ export default class SecuritySettingsTab extends React.Component {
return ( return (
<div className='mx_SettingsTab_section'> <div className='mx_SettingsTab_section'>
<span className='mx_SettingsTab_subheading'>{_t("Cryptography")}</span> <span className='mx_SettingsTab_subheading'>{_t("Cryptography")}</span>
<ul className='mx_SettingsTab_subsectionText mx_SecuritySettingsTab_deviceInfo'> <ul className='mx_SettingsTab_subsectionText mx_SecurityUserSettingsTab_deviceInfo'>
<li> <li>
<label>{_t("Device ID:")}</label> <label>{_t("Device ID:")}</label>
<span><code>{deviceId}</code></span> <span><code>{deviceId}</code></span>
@ -207,7 +207,7 @@ export default class SecuritySettingsTab extends React.Component {
); );
return ( return (
<div className="mx_SettingsTab mx_SecuritySettingsTab"> <div className="mx_SettingsTab mx_SecurityUserSettingsTab">
<div className="mx_SettingsTab_heading">{_t("Security & Privacy")}</div> <div className="mx_SettingsTab_heading">{_t("Security & Privacy")}</div>
<div className="mx_SettingsTab_section"> <div className="mx_SettingsTab_section">
<span className="mx_SettingsTab_subheading">{_t("Devices")}</span> <span className="mx_SettingsTab_subheading">{_t("Devices")}</span>

View File

@ -15,16 +15,16 @@ limitations under the License.
*/ */
import React from 'react'; import React from 'react';
import {_t} from "../../../../languageHandler"; import {_t} from "../../../../../languageHandler";
import CallMediaHandler from "../../../../CallMediaHandler"; import CallMediaHandler from "../../../../../CallMediaHandler";
import Field from "../../elements/Field"; import Field from "../../../elements/Field";
import AccessibleButton from "../../elements/AccessibleButton"; import AccessibleButton from "../../../elements/AccessibleButton";
import {SettingLevel} from "../../../../settings/SettingsStore"; import {SettingLevel} from "../../../../../settings/SettingsStore";
const Modal = require("../../../../Modal"); const Modal = require("../../../../../Modal");
const sdk = require("../../../../index"); const sdk = require("../../../../..");
const MatrixClientPeg = require("../../../../MatrixClientPeg"); const MatrixClientPeg = require("../../../../../MatrixClientPeg");
export default class VoiceSettingsTab extends React.Component { export default class VoiceUserSettingsTab extends React.Component {
constructor() { constructor() {
super(); super();
@ -103,7 +103,7 @@ export default class VoiceSettingsTab extends React.Component {
let webcamDropdown = null; let webcamDropdown = null;
if (this.state.mediaDevices === false) { if (this.state.mediaDevices === false) {
requestButton = ( requestButton = (
<div className='mx_VoiceSettingsTab_missingMediaPermissions'> <div className='mx_VoiceUserSettingsTab_missingMediaPermissions'>
<p>{_t("Missing media permissions, click the button below to request.")}</p> <p>{_t("Missing media permissions, click the button below to request.")}</p>
<AccessibleButton onClick={this._requestMediaPermissions} kind="primary"> <AccessibleButton onClick={this._requestMediaPermissions} kind="primary">
{_t("Request media permissions")} {_t("Request media permissions")}
@ -166,7 +166,7 @@ export default class VoiceSettingsTab extends React.Component {
} }
return ( return (
<div className="mx_SettingsTab mx_VoiceSettingsTab"> <div className="mx_SettingsTab mx_VoiceUserSettingsTab">
<div className="mx_SettingsTab_heading">{_t("Voice & Video")}</div> <div className="mx_SettingsTab_heading">{_t("Voice & Video")}</div>
<div className="mx_SettingsTab_section"> <div className="mx_SettingsTab_section">
{requestButton} {requestButton}

View File

@ -501,19 +501,7 @@
"Upload profile picture": "Upload profile picture", "Upload profile picture": "Upload profile picture",
"Display Name": "Display Name", "Display Name": "Display Name",
"Save": "Save", "Save": "Save",
"This room is not accessible by remote Matrix servers": "This room is not accessible by remote Matrix servers",
"Upgrade room to version %(ver)s": "Upgrade room to version %(ver)s",
"Room information": "Room information",
"Internal room ID:": "Internal room ID:",
"Room version": "Room version",
"Room version:": "Room version:",
"Developer options": "Developer options",
"Open Devtools": "Open Devtools",
"Flair": "Flair", "Flair": "Flair",
"General": "General",
"Room Addresses": "Room Addresses",
"Publish this room to the public in %(domain)s's room directory?": "Publish this room to the public in %(domain)s's room directory?",
"URL Previews": "URL Previews",
"Failed to change password. Is your password correct?": "Failed to change password. Is your password correct?", "Failed to change password. Is your password correct?": "Failed to change password. Is your password correct?",
"Success": "Success", "Success": "Success",
"Your password was successfully changed. You will not receive push notifications on other devices until you log back in to them": "Your password was successfully changed. You will not receive push notifications on other devices until you log back in to them", "Your password was successfully changed. You will not receive push notifications on other devices until you log back in to them": "Your password was successfully changed. You will not receive push notifications on other devices until you log back in to them",
@ -529,6 +517,7 @@
"Account management": "Account management", "Account management": "Account management",
"Deactivating your account is a permanent action - be careful!": "Deactivating your account is a permanent action - be careful!", "Deactivating your account is a permanent action - be careful!": "Deactivating your account is a permanent action - be careful!",
"Deactivate Account": "Deactivate Account", "Deactivate Account": "Deactivate Account",
"General": "General",
"Legal": "Legal", "Legal": "Legal",
"Credits": "Credits", "Credits": "Credits",
"For help with using Riot, click <a>here</a>.": "For help with using Riot, click <a>here</a>.", "For help with using Riot, click <a>here</a>.": "For help with using Riot, click <a>here</a>.",
@ -558,6 +547,44 @@
"Timeline": "Timeline", "Timeline": "Timeline",
"Room list": "Room list", "Room list": "Room list",
"Autocomplete delay (ms)": "Autocomplete delay (ms)", "Autocomplete delay (ms)": "Autocomplete delay (ms)",
"Unignore": "Unignore",
"<not supported>": "<not supported>",
"Import E2E room keys": "Import E2E room keys",
"Cryptography": "Cryptography",
"Device ID:": "Device ID:",
"Device key:": "Device key:",
"Ignored users": "Ignored users",
"Bulk options": "Bulk options",
"Reject all %(invitedRooms)s invites": "Reject all %(invitedRooms)s invites",
"Key backup": "Key backup",
"Security & Privacy": "Security & Privacy",
"Devices": "Devices",
"Riot collects anonymous analytics to allow us to improve the application.": "Riot collects anonymous analytics to allow us to improve the application.",
"Privacy is important to us, so we don't collect any personal or identifiable data for our analytics.": "Privacy is important to us, so we don't collect any personal or identifiable data for our analytics.",
"Learn more about how we use analytics.": "Learn more about how we use analytics.",
"No media permissions": "No media permissions",
"You may need to manually permit Riot to access your microphone/webcam": "You may need to manually permit Riot to access your microphone/webcam",
"Missing media permissions, click the button below to request.": "Missing media permissions, click the button below to request.",
"Request media permissions": "Request media permissions",
"No Audio Outputs detected": "No Audio Outputs detected",
"No Microphones detected": "No Microphones detected",
"No Webcams detected": "No Webcams detected",
"Default Device": "Default Device",
"Audio Output": "Audio Output",
"Microphone": "Microphone",
"Camera": "Camera",
"Voice & Video": "Voice & Video",
"This room is not accessible by remote Matrix servers": "This room is not accessible by remote Matrix servers",
"Upgrade room to version %(ver)s": "Upgrade room to version %(ver)s",
"Room information": "Room information",
"Internal room ID:": "Internal room ID:",
"Room version": "Room version",
"Room version:": "Room version:",
"Developer options": "Developer options",
"Open Devtools": "Open Devtools",
"Room Addresses": "Room Addresses",
"Publish this room to the public in %(domain)s's room directory?": "Publish this room to the public in %(domain)s's room directory?",
"URL Previews": "URL Previews",
"To change the room's avatar, you must be a": "To change the room's avatar, you must be a", "To change the room's avatar, you must be a": "To change the room's avatar, you must be a",
"To change the room's name, you must be a": "To change the room's name, you must be a", "To change the room's name, you must be a": "To change the room's name, you must be a",
"To change the room's main address, you must be a": "To change the room's main address, you must be a", "To change the room's main address, you must be a": "To change the room's main address, you must be a",
@ -595,38 +622,11 @@
"Members only (since the point in time of selecting this option)": "Members only (since the point in time of selecting this option)", "Members only (since the point in time of selecting this option)": "Members only (since the point in time of selecting this option)",
"Members only (since they were invited)": "Members only (since they were invited)", "Members only (since they were invited)": "Members only (since they were invited)",
"Members only (since they joined)": "Members only (since they joined)", "Members only (since they joined)": "Members only (since they joined)",
"Security & Privacy": "Security & Privacy",
"Encryption": "Encryption", "Encryption": "Encryption",
"Once enabled, encryption cannot be disabled.": "Once enabled, encryption cannot be disabled.", "Once enabled, encryption cannot be disabled.": "Once enabled, encryption cannot be disabled.",
"Encrypted": "Encrypted", "Encrypted": "Encrypted",
"Who can access this room?": "Who can access this room?", "Who can access this room?": "Who can access this room?",
"Who can read history?": "Who can read history?", "Who can read history?": "Who can read history?",
"Unignore": "Unignore",
"<not supported>": "<not supported>",
"Import E2E room keys": "Import E2E room keys",
"Cryptography": "Cryptography",
"Device ID:": "Device ID:",
"Device key:": "Device key:",
"Ignored users": "Ignored users",
"Bulk options": "Bulk options",
"Reject all %(invitedRooms)s invites": "Reject all %(invitedRooms)s invites",
"Key backup": "Key backup",
"Devices": "Devices",
"Riot collects anonymous analytics to allow us to improve the application.": "Riot collects anonymous analytics to allow us to improve the application.",
"Privacy is important to us, so we don't collect any personal or identifiable data for our analytics.": "Privacy is important to us, so we don't collect any personal or identifiable data for our analytics.",
"Learn more about how we use analytics.": "Learn more about how we use analytics.",
"No media permissions": "No media permissions",
"You may need to manually permit Riot to access your microphone/webcam": "You may need to manually permit Riot to access your microphone/webcam",
"Missing media permissions, click the button below to request.": "Missing media permissions, click the button below to request.",
"Request media permissions": "Request media permissions",
"No Audio Outputs detected": "No Audio Outputs detected",
"No Microphones detected": "No Microphones detected",
"No Webcams detected": "No Webcams detected",
"Default Device": "Default Device",
"Audio Output": "Audio Output",
"Microphone": "Microphone",
"Camera": "Camera",
"Voice & Video": "Voice & Video",
"Cannot add any more widgets": "Cannot add any more widgets", "Cannot add any more widgets": "Cannot add any more widgets",
"The maximum permitted number of widgets have already been added to this room.": "The maximum permitted number of widgets have already been added to this room.", "The maximum permitted number of widgets have already been added to this room.": "The maximum permitted number of widgets have already been added to this room.",
"Add a widget": "Add a widget", "Add a widget": "Add a widget",