Implement the Security & Privacy tab of new room settings
parent
0cce912cf6
commit
a2f516c461
|
@ -143,6 +143,7 @@
|
||||||
@import "./views/settings/tabs/_GeneralUserSettingsTab.scss";
|
@import "./views/settings/tabs/_GeneralUserSettingsTab.scss";
|
||||||
@import "./views/settings/tabs/_HelpSettingsTab.scss";
|
@import "./views/settings/tabs/_HelpSettingsTab.scss";
|
||||||
@import "./views/settings/tabs/_PreferencesSettingsTab.scss";
|
@import "./views/settings/tabs/_PreferencesSettingsTab.scss";
|
||||||
|
@import "./views/settings/tabs/_SecurityRoomSettingsTab.scss";
|
||||||
@import "./views/settings/tabs/_SecuritySettingsTab.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/_VoiceSettingsTab.scss";
|
||||||
|
|
|
@ -21,10 +21,12 @@ limitations under the License.
|
||||||
border-radius: 14px;
|
border-radius: 14px;
|
||||||
background-color: $togglesw-off-color;
|
background-color: $togglesw-off-color;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_ToggleSwitch_enabled {
|
.mx_ToggleSwitch_enabled {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_ToggleSwitch.mx_ToggleSwitch_on {
|
.mx_ToggleSwitch.mx_ToggleSwitch_on {
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.mx_SecurityRoomSettingsTab label {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_SecurityRoomSettingsTab_warning {
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
img {
|
||||||
|
vertical-align: middle;
|
||||||
|
margin-right: 5px;
|
||||||
|
margin-left: 3px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_SecurityRoomSettingsTab_encryptionSection {
|
||||||
|
margin-bottom: 25px;
|
||||||
|
}
|
|
@ -21,6 +21,7 @@ import {_t, _td} from "../../../languageHandler";
|
||||||
import AccessibleButton from "../elements/AccessibleButton";
|
import AccessibleButton from "../elements/AccessibleButton";
|
||||||
import dis from '../../../dispatcher';
|
import dis from '../../../dispatcher';
|
||||||
import GeneralRoomSettingsTab from "../settings/tabs/GeneralRoomSettingsTab";
|
import GeneralRoomSettingsTab from "../settings/tabs/GeneralRoomSettingsTab";
|
||||||
|
import SecurityRoomSettingsTab from "../settings/tabs/SecurityRoomSettingsTab";
|
||||||
|
|
||||||
// TODO: Ditch this whole component
|
// TODO: Ditch this whole component
|
||||||
export class TempTab extends React.Component {
|
export class TempTab extends React.Component {
|
||||||
|
@ -68,7 +69,7 @@ export default class RoomSettingsDialog extends React.Component {
|
||||||
tabs.push(new Tab(
|
tabs.push(new Tab(
|
||||||
_td("Security & Privacy"),
|
_td("Security & Privacy"),
|
||||||
"mx_RoomSettingsDialog_securityIcon",
|
"mx_RoomSettingsDialog_securityIcon",
|
||||||
<div>Security Test</div>,
|
<SecurityRoomSettingsTab roomId={this.props.roomId} />,
|
||||||
));
|
));
|
||||||
tabs.push(new Tab(
|
tabs.push(new Tab(
|
||||||
_td("Roles & Permissions"),
|
_td("Roles & Permissions"),
|
||||||
|
|
|
@ -28,6 +28,9 @@ export default class LabelledToggleSwitch extends React.Component {
|
||||||
|
|
||||||
// The translated label for the switch
|
// The translated label for the switch
|
||||||
label: PropTypes.string.isRequired,
|
label: PropTypes.string.isRequired,
|
||||||
|
|
||||||
|
// Whether or not to disable the toggle switch
|
||||||
|
disabled: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -35,7 +38,8 @@ export default class LabelledToggleSwitch extends React.Component {
|
||||||
return (
|
return (
|
||||||
<div className="mx_SettingsFlag">
|
<div className="mx_SettingsFlag">
|
||||||
<span className="mx_SettingsFlag_label">{this.props.label}</span>
|
<span className="mx_SettingsFlag_label">{this.props.label}</span>
|
||||||
<ToggleSwitch checked={this.props.value} onChange={this.props.onChange} />
|
<ToggleSwitch checked={this.props.value} disabled={this.props.disabled}
|
||||||
|
onChange={this.props.onChange} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,279 @@
|
||||||
|
/*
|
||||||
|
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 {_t} from "../../../../languageHandler";
|
||||||
|
import RoomProfileSettings from "../../room_settings/RoomProfileSettings";
|
||||||
|
import MatrixClientPeg from "../../../../MatrixClientPeg";
|
||||||
|
import sdk from "../../../../index";
|
||||||
|
import AccessibleButton from "../../elements/AccessibleButton";
|
||||||
|
import {MatrixClient} from "matrix-js-sdk";
|
||||||
|
import dis from "../../../../dispatcher";
|
||||||
|
import LabelledToggleSwitch from "../../elements/LabelledToggleSwitch";
|
||||||
|
import {SettingLevel} from "../../../../settings/SettingsStore";
|
||||||
|
import Modal from "../../../../Modal";
|
||||||
|
|
||||||
|
export default class SecurityRoomSettingsTab extends React.Component {
|
||||||
|
static propTypes = {
|
||||||
|
roomId: PropTypes.string.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
componentWillMount(): void {
|
||||||
|
MatrixClientPeg.get().on("RoomState.events", this._onStateEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount(): void {
|
||||||
|
MatrixClientPeg.get().removeListener("RoomState.events", this._onStateEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
_onStateEvent = (e) => {
|
||||||
|
const refreshWhenTypes = ['m.room.join_rules', 'm.room.guest_access', 'm.room.history_visibility'];
|
||||||
|
if (refreshWhenTypes.includes(e.getType())) this.forceUpdate();
|
||||||
|
};
|
||||||
|
|
||||||
|
_onEncryptionChange = (e) => {
|
||||||
|
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||||
|
Modal.createTrackedDialog('E2E Enable Warning', '', QuestionDialog, {
|
||||||
|
title: _t('Warning!'),
|
||||||
|
description: (
|
||||||
|
<div>
|
||||||
|
<p>{ _t('End-to-end encryption is in beta and may not be reliable') }.</p>
|
||||||
|
<p>{ _t('You should not yet trust it to secure data') }.</p>
|
||||||
|
<p>{ _t('Devices will not yet be able to decrypt history from before they joined the room') }.</p>
|
||||||
|
<p>{ _t('Once encryption is enabled for a room it cannot be turned off again (for now)') }.</p>
|
||||||
|
<p>{ _t('Encrypted messages will not be visible on clients that do not yet implement encryption') }.</p>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
onFinished: (confirm)=>{
|
||||||
|
if (confirm) {
|
||||||
|
return MatrixClientPeg.get().sendStateEvent(
|
||||||
|
this.props.roomId, "m.room.encryption",
|
||||||
|
{ algorithm: "m.megolm.v1.aes-sha2" },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
_fixGuestAccess = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
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"}, "");
|
||||||
|
};
|
||||||
|
|
||||||
|
_onRoomAccessRadioToggle = (ev) => {
|
||||||
|
// join_rule
|
||||||
|
// INVITE | PUBLIC
|
||||||
|
// ----------------------+----------------
|
||||||
|
// guest CAN_JOIN | inv_only | pub_with_guest
|
||||||
|
// access ----------------------+----------------
|
||||||
|
// FORBIDDEN | inv_only | pub_no_guest
|
||||||
|
// ----------------------+----------------
|
||||||
|
|
||||||
|
// we always set guests can_join here as it makes no sense to have
|
||||||
|
// an invite-only room that guests can't join. If you explicitly
|
||||||
|
// invite them, you clearly want them to join, whether they're a
|
||||||
|
// guest or not. In practice, guest_access should probably have
|
||||||
|
// been implemented as part of the join_rules enum.
|
||||||
|
let joinRule = "invite";
|
||||||
|
let guestAccess = "can_join";
|
||||||
|
|
||||||
|
switch (ev.target.value) {
|
||||||
|
case "invite_only":
|
||||||
|
// no change - use defaults above
|
||||||
|
break;
|
||||||
|
case "public_no_guests":
|
||||||
|
joinRule = "public";
|
||||||
|
guestAccess = "forbidden";
|
||||||
|
break;
|
||||||
|
case "public_with_guests":
|
||||||
|
joinRule = "public";
|
||||||
|
guestAccess = "can_join";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
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}, "");
|
||||||
|
};
|
||||||
|
|
||||||
|
_onHistoryRadioToggle = (ev) => {
|
||||||
|
MatrixClientPeg.get().sendStateEvent(this.props.roomId, "m.room.history_visibility", {
|
||||||
|
history_visibility: ev.target.value,
|
||||||
|
}, "");
|
||||||
|
};
|
||||||
|
|
||||||
|
_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 aliasEvents = room.currentState.getStateEvents("m.room.aliases") || [];
|
||||||
|
const hasAliases = aliasEvents.includes((ev) => (ev.getContent().aliases || []).length);
|
||||||
|
|
||||||
|
const canChangeAccess = room.currentState.mayClientSendStateEvent("m.room.join_rules", client)
|
||||||
|
&& room.currentState.mayClientSendStateEvent("m.room.guest_access", client);
|
||||||
|
|
||||||
|
let guestWarning = null;
|
||||||
|
if (joinRule !== 'public' && guestAccess === 'forbidden') {
|
||||||
|
guestWarning = (
|
||||||
|
<div className='mx_SecurityRoomSettingsTab_warning'>
|
||||||
|
<img src={require("../../../../../res/img/warning.svg")} width={15} height={15} />
|
||||||
|
<span>
|
||||||
|
{_t("Guests cannot join this room even if explicitly invited.")}
|
||||||
|
<a href="" onClick={this._fixGuestAccess}>{_t("Click here to fix")}</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let aliasWarning = null;
|
||||||
|
if (joinRule === 'public' && !hasAliases) {
|
||||||
|
aliasWarning = (
|
||||||
|
<div className='mx_SecurityRoomSettingsTab_warning'>
|
||||||
|
<img src={require("../../../../../res/img/warning.svg")} width={15} height={15} />
|
||||||
|
<span>
|
||||||
|
{_t("To link to this room, please add an alias.")}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{guestWarning}
|
||||||
|
{aliasWarning}
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="roomVis" value="invite_only"
|
||||||
|
disabled={!canChangeAccess}
|
||||||
|
onChange={this._onRoomAccessRadioToggle}
|
||||||
|
checked={joinRule !== "public"} />
|
||||||
|
{_t('Only people who have been invited')}
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="roomVis" value="public_no_guests"
|
||||||
|
disabled={!canChangeAccess}
|
||||||
|
onChange={this._onRoomAccessRadioToggle}
|
||||||
|
checked={joinRule === "public" && guestAccess !== "can_join"} />
|
||||||
|
{_t('Anyone who knows the room\'s link, apart from guests')}
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="roomVis" value="public_with_guests"
|
||||||
|
disabled={!canChangeAccess}
|
||||||
|
onChange={this._onRoomAccessRadioToggle}
|
||||||
|
checked={joinRule === "public" && guestAccess === "can_join"} />
|
||||||
|
{_t("Anyone who knows the room's link, including guests")}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
_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 canChangeHistory = state.mayClientSendStateEvent('m.room.history_visibility', client);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
{_t('Changes to who can read history will only apply to future messages in this room. ' +
|
||||||
|
'The visibility of existing history will be unchanged.')}
|
||||||
|
</div>
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="historyVis" value="world_readable"
|
||||||
|
disabled={!canChangeHistory}
|
||||||
|
checked={history === "world_readable"}
|
||||||
|
onChange={this._onHistoryRadioToggle} />
|
||||||
|
{_t("Anyone")}
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="historyVis" value="shared"
|
||||||
|
disabled={!canChangeHistory}
|
||||||
|
checked={history === "shared"}
|
||||||
|
onChange={this._onHistoryRadioToggle} />
|
||||||
|
{_t('Members only (since the point in time of selecting this option)')}
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<input type="radio" name="historyVis" value="invited"
|
||||||
|
disabled={!canChangeHistory}
|
||||||
|
checked={history === "invited"}
|
||||||
|
onChange={this._onHistoryRadioToggle} />
|
||||||
|
{_t('Members only (since they were invited)')}
|
||||||
|
</label>
|
||||||
|
<label >
|
||||||
|
<input type="radio" name="historyVis" value="joined"
|
||||||
|
disabled={!canChangeHistory}
|
||||||
|
checked={history === "joined"}
|
||||||
|
onChange={this._onHistoryRadioToggle} />
|
||||||
|
{_t('Members only (since they joined)')}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const SettingsFlag = sdk.getComponent("elements.SettingsFlag");
|
||||||
|
|
||||||
|
const client = MatrixClientPeg.get();
|
||||||
|
const room = client.getRoom(this.props.roomId);
|
||||||
|
const isEncrypted = client.isRoomEncrypted(this.props.roomId);
|
||||||
|
const hasEncryptionPermission = room.currentState.mayClientSendStateEvent("m.room.encryption", client);
|
||||||
|
const canEnableEncryption = !isEncrypted && hasEncryptionPermission;
|
||||||
|
|
||||||
|
let encryptionSection = (
|
||||||
|
<div>
|
||||||
|
<div className='mx_SettingsTab_subsectionText'>
|
||||||
|
<span>{_t("Once enabled, encryption cannot be disabled.")}</span>
|
||||||
|
</div>
|
||||||
|
<LabelledToggleSwitch value={isEncrypted} onChange={this._onEncryptionChange}
|
||||||
|
label={_t("Encrypted")} disabled={!canEnableEncryption} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
let encryptionSettings = null;
|
||||||
|
if (isEncrypted) {
|
||||||
|
encryptionSettings = <SettingsFlag name="blacklistUnverifiedDevices" level={SettingLevel.ROOM_DEVICE}
|
||||||
|
roomId={this.props.roomId} />
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mx_SettingsTab mx_SecurityRoomSettingsTab">
|
||||||
|
<div className="mx_SettingsTab_heading">{_t("Security & Privacy")}</div>
|
||||||
|
|
||||||
|
<span className='mx_SettingsTab_subheading'>{_t("Encryption")}</span>
|
||||||
|
<div className='mx_SettingsTab_section mx_SecurityRoomSettingsTab_encryptionSection'>
|
||||||
|
{encryptionSection}
|
||||||
|
{encryptionSettings}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<span className='mx_SettingsTab_subheading'>{_t("Who can access this room?")}</span>
|
||||||
|
<div className='mx_SettingsTab_section mx_SettingsTab_subsectionText'>
|
||||||
|
{this._renderRoomAccess()}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<span className='mx_SettingsTab_subheading'>{_t("Who can read history?")}</span>
|
||||||
|
<div className='mx_SettingsTab_section mx_SettingsTab_subsectionText'>
|
||||||
|
{this._renderHistory()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -481,6 +481,28 @@
|
||||||
"Room list": "Room list",
|
"Room list": "Room list",
|
||||||
"Timeline": "Timeline",
|
"Timeline": "Timeline",
|
||||||
"Autocomplete delay (ms)": "Autocomplete delay (ms)",
|
"Autocomplete delay (ms)": "Autocomplete delay (ms)",
|
||||||
|
"End-to-end encryption is in beta and may not be reliable": "End-to-end encryption is in beta and may not be reliable",
|
||||||
|
"You should not yet trust it to secure data": "You should not yet trust it to secure data",
|
||||||
|
"Devices will not yet be able to decrypt history from before they joined the room": "Devices will not yet be able to decrypt history from before they joined the room",
|
||||||
|
"Once encryption is enabled for a room it cannot be turned off again (for now)": "Once encryption is enabled for a room it cannot be turned off again (for now)",
|
||||||
|
"Encrypted messages will not be visible on clients that do not yet implement encryption": "Encrypted messages will not be visible on clients that do not yet implement encryption",
|
||||||
|
"Guests cannot join this room even if explicitly invited.": "Guests cannot join this room even if explicitly invited.",
|
||||||
|
"Click here to fix": "Click here to fix",
|
||||||
|
"To link to this room, please add an alias.": "To link to this room, please add an alias.",
|
||||||
|
"Only people who have been invited": "Only people who have been invited",
|
||||||
|
"Anyone who knows the room's link, apart from guests": "Anyone who knows the room's link, apart from guests",
|
||||||
|
"Anyone who knows the room's link, including guests": "Anyone who knows the room's link, including guests",
|
||||||
|
"Changes to who can read history will only apply to future messages in this room. The visibility of existing history will be unchanged.": "Changes to who can read history will only apply to future messages in this room. The visibility of existing history will be unchanged.",
|
||||||
|
"Anyone": "Anyone",
|
||||||
|
"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 joined)": "Members only (since they joined)",
|
||||||
|
"Once enabled, encryption cannot be disabled.": "Once enabled, encryption cannot be disabled.",
|
||||||
|
"Encrypted": "Encrypted",
|
||||||
|
"Security & Privacy": "Security & Privacy",
|
||||||
|
"Encryption": "Encryption",
|
||||||
|
"Who can access this room?": "Who can access this room?",
|
||||||
|
"Who can read history?": "Who can read history?",
|
||||||
"Unignore": "Unignore",
|
"Unignore": "Unignore",
|
||||||
"<not supported>": "<not supported>",
|
"<not supported>": "<not supported>",
|
||||||
"Import E2E room keys": "Import E2E room keys",
|
"Import E2E room keys": "Import E2E room keys",
|
||||||
|
@ -491,7 +513,6 @@
|
||||||
"Bulk options": "Bulk options",
|
"Bulk options": "Bulk options",
|
||||||
"Reject all %(invitedRooms)s invites": "Reject all %(invitedRooms)s invites",
|
"Reject all %(invitedRooms)s invites": "Reject all %(invitedRooms)s invites",
|
||||||
"Key backup": "Key backup",
|
"Key backup": "Key backup",
|
||||||
"Security & Privacy": "Security & Privacy",
|
|
||||||
"Devices": "Devices",
|
"Devices": "Devices",
|
||||||
"Riot collects anonymous analytics to allow us to improve the application.": "Riot collects anonymous analytics to allow us to improve the application.",
|
"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.",
|
"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.",
|
||||||
|
@ -700,11 +721,6 @@
|
||||||
"The visibility of existing history will be unchanged": "The visibility of existing history will be unchanged",
|
"The visibility of existing history will be unchanged": "The visibility of existing history will be unchanged",
|
||||||
"unknown error code": "unknown error code",
|
"unknown error code": "unknown error code",
|
||||||
"Failed to forget room %(errCode)s": "Failed to forget room %(errCode)s",
|
"Failed to forget room %(errCode)s": "Failed to forget room %(errCode)s",
|
||||||
"End-to-end encryption is in beta and may not be reliable": "End-to-end encryption is in beta and may not be reliable",
|
|
||||||
"You should not yet trust it to secure data": "You should not yet trust it to secure data",
|
|
||||||
"Devices will not yet be able to decrypt history from before they joined the room": "Devices will not yet be able to decrypt history from before they joined the room",
|
|
||||||
"Once encryption is enabled for a room it cannot be turned off again (for now)": "Once encryption is enabled for a room it cannot be turned off again (for now)",
|
|
||||||
"Encrypted messages will not be visible on clients that do not yet implement encryption": "Encrypted messages will not be visible on clients that do not yet implement encryption",
|
|
||||||
"Enable encryption": "Enable encryption",
|
"Enable encryption": "Enable encryption",
|
||||||
"(warning: cannot be disabled again!)": "(warning: cannot be disabled again!)",
|
"(warning: cannot be disabled again!)": "(warning: cannot be disabled again!)",
|
||||||
"Encryption is enabled in this room": "Encryption is enabled in this room",
|
"Encryption is enabled in this room": "Encryption is enabled in this room",
|
||||||
|
@ -726,21 +742,10 @@
|
||||||
"Favourite": "Favourite",
|
"Favourite": "Favourite",
|
||||||
"Tagged as: ": "Tagged as: ",
|
"Tagged as: ": "Tagged as: ",
|
||||||
"To link to a room it must have <a>an address</a>.": "To link to a room it must have <a>an address</a>.",
|
"To link to a room it must have <a>an address</a>.": "To link to a room it must have <a>an address</a>.",
|
||||||
"Guests cannot join this room even if explicitly invited.": "Guests cannot join this room even if explicitly invited.",
|
|
||||||
"Click here to fix": "Click here to fix",
|
|
||||||
"To send events of type <eventType/>, you must be a": "To send events of type <eventType/>, you must be a",
|
"To send events of type <eventType/>, you must be a": "To send events of type <eventType/>, you must be a",
|
||||||
"Upgrade room to version %(ver)s": "Upgrade room to version %(ver)s",
|
"Upgrade room to version %(ver)s": "Upgrade room to version %(ver)s",
|
||||||
"Open Devtools": "Open Devtools",
|
"Open Devtools": "Open Devtools",
|
||||||
"Who can access this room?": "Who can access this room?",
|
|
||||||
"Only people who have been invited": "Only people who have been invited",
|
|
||||||
"Anyone who knows the room's link, apart from guests": "Anyone who knows the room's link, apart from guests",
|
|
||||||
"Anyone who knows the room's link, including guests": "Anyone who knows the room's link, including guests",
|
|
||||||
"Publish this room to the public in %(domain)s's room directory?": "Publish this room to the public in %(domain)s's room directory?",
|
"Publish this room to the public in %(domain)s's room directory?": "Publish this room to the public in %(domain)s's room directory?",
|
||||||
"Who can read history?": "Who can read history?",
|
|
||||||
"Anyone": "Anyone",
|
|
||||||
"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 joined)": "Members only (since they joined)",
|
|
||||||
"Permissions": "Permissions",
|
"Permissions": "Permissions",
|
||||||
"Internal room ID: ": "Internal room ID: ",
|
"Internal room ID: ": "Internal room ID: ",
|
||||||
"Room version number: ": "Room version number: ",
|
"Room version number: ": "Room version number: ",
|
||||||
|
|
Loading…
Reference in New Issue