Add an in-room reminder to set up key recovery

This adds an in-room reminder above the message timeline to set up Secure
Message Recovery so that your keys will be backed up. If you try to ignore it,
an additional dialog is shown to confirm.

Fixes vector-im/riot-web#7783.

Signed-off-by: J. Ryan Stinnett <jryans@gmail.com>
pull/21833/head
J. Ryan Stinnett 2018-12-06 15:39:59 -06:00
parent d40225bf55
commit a92d2902c4
11 changed files with 259 additions and 2 deletions

View File

@ -101,6 +101,7 @@
@import "./views/rooms/_RoomHeader.scss";
@import "./views/rooms/_RoomList.scss";
@import "./views/rooms/_RoomPreviewBar.scss";
@import "./views/rooms/_RoomRecoveryReminder.scss";
@import "./views/rooms/_RoomSettings.scss";
@import "./views/rooms/_RoomTile.scss";
@import "./views/rooms/_RoomTooltip.scss";

View File

@ -0,0 +1,43 @@
/*
Copyright 2018 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_RoomRecoveryReminder {
display: flex;
flex-direction: column;
text-align: center;
background-color: $room-warning-bg-color;
padding: 20px;
border: 1px solid $primary-hairline-color;
border-bottom: unset;
}
.mx_RoomRecoveryReminder_header {
font-weight: bold;
margin-bottom: 1em;
}
.mx_RoomRecoveryReminder_body {
margin-bottom: 1em;
}
.mx_RoomRecoveryReminder_button {
@mixin mx_DialogButton;
margin: 0 10px;
}
.mx_RoomRecoveryReminder_button.mx_RoomRecoveryReminder_secondary {
@mixin mx_DialogButton_secondary;
}

View File

@ -100,6 +100,8 @@ $voip-accept-color: #80f480;
$rte-bg-color: #353535;
$rte-code-bg-color: #000;
$room-warning-bg-color: #2d2d2d;
// ********************
$roomtile-name-color: rgba(186, 186, 186, 0.8);
@ -169,6 +171,14 @@ $progressbar-color: #000;
outline: none;
}
@define-mixin mx_DialogButton_secondary {
// flip colours for the secondary ones
font-weight: 600;
border: 1px solid $accent-color ! important;
color: $accent-color;
background-color: $accent-fg-color;
}
// Nasty hacks to apply a filter to arbitrary monochrome artwork to make it
// better match the theme. Typically applied to dark grey 'off' buttons or
// light grey 'on' buttons.

View File

@ -155,6 +155,8 @@ $imagebody-giflabel-border: rgba(0, 0, 0, 0.2);
// unused?
$progressbar-color: #000;
$room-warning-bg-color: #fff8e3;
// ***** Mixins! *****
@define-mixin mx_DialogButton {
@ -187,3 +189,11 @@ $progressbar-color: #000;
font-size: 15px;
padding: 0px 1.5em 0px 1.5em;
}
@define-mixin mx_DialogButton_secondary {
// flip colours for the secondary ones
font-weight: 600;
border: 1px solid $accent-color ! important;
color: $accent-color;
background-color: $accent-fg-color;
}

View File

@ -251,7 +251,7 @@ export default React.createClass({
/>
<p>{_t(
"If you don't want encrypted message history to be availble on other devices, "+
"If you don't want encrypted message history to be available on other devices, "+
"<button>opt out</button>.",
{},
{

View File

@ -0,0 +1,70 @@
/*
Copyright 2018 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 sdk from "../../../../index";
import { _t } from "../../../../languageHandler";
export default class IgnoreRecoveryReminderDialog extends React.PureComponent {
static propTypes = {
onDontAskAgain: PropTypes.func.isRequired,
onFinished: PropTypes.func.isRequired,
onSetup: PropTypes.func.isRequired,
}
onDontAskAgainClick = () => {
this.props.onFinished();
this.props.onDontAskAgain();
}
onSetupClick = () => {
this.props.onFinished();
this.props.onSetup();
}
render() {
const BaseDialog = sdk.getComponent("views.dialogs.BaseDialog");
const DialogButtons = sdk.getComponent("views.elements.DialogButtons");
return (
<BaseDialog className="mx_IgnoreRecoveryReminderDialog"
onFinished={this.props.onFinished}
title={_t("Are you sure?")}
>
<div>
<p>{_t(
"Without setting up Secure Message Recovery, " +
"you'll lose your secure message history when you " +
"log out.",
)}</p>
<p>{_t(
"If you don't want to set this up now, you can later " +
"in Settings.",
)}</p>
<div className="mx_Dialog_buttons">
<DialogButtons
primaryButton={_t("Set up")}
onPrimaryButtonClick={this.onSetupClick}
cancelButton={_t("Don't ask again")}
onCancel={this.onDontAskAgainClick}
/>
</div>
</div>
</BaseDialog>
);
}
}

View File

@ -607,6 +607,20 @@ module.exports = React.createClass({
}
},
async onRoomRecoveryReminderFinished(backupCreated) {
// If the user cancelled the key backup dialog, it suggests they don't
// want to be reminded anymore.
if (!backupCreated) {
await SettingsStore.setValue(
"showRoomRecoveryReminder",
null,
SettingLevel.ACCOUNT,
false,
);
}
this.forceUpdate();
},
canResetTimeline: function() {
if (!this.refs.messagePanel) {
return true;
@ -1521,6 +1535,7 @@ module.exports = React.createClass({
const Loader = sdk.getComponent("elements.Spinner");
const TimelinePanel = sdk.getComponent("structures.TimelinePanel");
const RoomUpgradeWarningBar = sdk.getComponent("rooms.RoomUpgradeWarningBar");
const RoomRecoveryReminder = sdk.getComponent("rooms.RoomRecoveryReminder");
if (!this.state.room) {
if (this.state.roomLoading || this.state.peekLoading) {
@ -1655,6 +1670,13 @@ module.exports = React.createClass({
this.state.room.userMayUpgradeRoom(MatrixClientPeg.get().credentials.userId)
);
const showRoomRecoveryReminder = (
SettingsStore.isFeatureEnabled("feature_keybackup") &&
SettingsStore.getValue("showRoomRecoveryReminder") &&
MatrixClientPeg.get().isRoomEncrypted(this.state.room.roomId) &&
!MatrixClientPeg.get().getKeyBackupEnabled()
);
let aux = null;
let hideCancel = false;
if (this.state.editingRoomSettings) {
@ -1669,6 +1691,9 @@ module.exports = React.createClass({
} else if (showRoomUpgradeBar) {
aux = <RoomUpgradeWarningBar room={this.state.room} />;
hideCancel = true;
} else if (showRoomRecoveryReminder) {
aux = <RoomRecoveryReminder onFinished={this.onRoomRecoveryReminderFinished} />;
hideCancel = true;
} else if (this.state.showingPinned) {
hideCancel = true; // has own cancel
aux = <PinnedEventsPanel room={this.state.room} onCancelClick={this.onPinnedClick} />;

View File

@ -64,6 +64,7 @@ const SIMPLE_SETTINGS = [
{ id: "urlPreviewsEnabled" },
{ id: "autoplayGifsAndVideos" },
{ id: "alwaysShowEncryptionIcons" },
{ id: "showRoomRecoveryReminder" },
{ id: "hideReadReceipts" },
{ id: "dontSendTypingNotifications" },
{ id: "alwaysShowTimestamps" },

View File

@ -0,0 +1,85 @@
/*
Copyright 2018 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 sdk from "../../../index";
import { _t } from "../../../languageHandler";
import Modal from "../../../Modal";
export default class RoomRecoveryReminder extends React.PureComponent {
static propTypes = {
onFinished: PropTypes.func.isRequired,
}
showKeyBackupDialog = () => {
Modal.createTrackedDialogAsync("Key Backup", "Key Backup",
import("../../../async-components/views/dialogs/keybackup/CreateKeyBackupDialog"),
{
onFinished: this.props.onFinished,
},
);
}
onDontAskAgainClick = () => {
// When you choose "Don't ask again" from the room reminder, we show a
// dialog to confirm the choice.
Modal.createTrackedDialogAsync("Ignore Recovery Reminder", "Ignore Recovery Reminder",
import("../../../async-components/views/dialogs/keybackup/IgnoreRecoveryReminderDialog"),
{
onDontAskAgain: () => {
// Report false to the caller, who should prevent the
// reminder from appearing in the future.
this.props.onFinished(false);
},
onSetup: () => {
this.showKeyBackupDialog();
},
},
);
}
onSetupClick = () => {
this.showKeyBackupDialog();
}
render() {
const AccessibleButton = sdk.getComponent("views.elements.AccessibleButton");
return (
<div className="mx_RoomRecoveryReminder">
<div className="mx_RoomRecoveryReminder_header">{_t(
"Secure Message Recovery",
)}</div>
<div className="mx_RoomRecoveryReminder_body">{_t(
"If you log out or use another device, you'll lose your " +
"secure message history. To prevent this, set up Secure " +
"Message Recovery.",
)}</div>
<div className="mx_RoomRecoveryReminder_buttons">
<AccessibleButton className="mx_RoomRecoveryReminder_button mx_RoomRecoveryReminder_secondary"
onClick={this.onDontAskAgainClick}>
{ _t("Don't ask again") }
</AccessibleButton>
<AccessibleButton className="mx_RoomRecoveryReminder_button"
onClick={this.onSetupClick}>
{ _t("Set up") }
</AccessibleButton>
</div>
</div>
);
}
}

View File

@ -268,6 +268,7 @@
"Always show message timestamps": "Always show message timestamps",
"Autoplay GIFs and videos": "Autoplay GIFs and videos",
"Always show encryption icons": "Always show encryption icons",
"Show a reminder to enable Secure Message Recovery in encrypted rooms": "Show a reminder to enable Secure Message Recovery in encrypted rooms",
"Enable automatic language detection for syntax highlighting": "Enable automatic language detection for syntax highlighting",
"Hide avatars in user and room mentions": "Hide avatars in user and room mentions",
"Disable big emoji in chat": "Disable big emoji in chat",
@ -562,6 +563,10 @@
"You are trying to access a room.": "You are trying to access a room.",
"<a>Click here</a> to join the discussion!": "<a>Click here</a> to join the discussion!",
"This is a preview of this room. Room interactions have been disabled": "This is a preview of this room. Room interactions have been disabled",
"Secure Message Recovery": "Secure Message Recovery",
"If you log out or use another device, you'll lose your secure message history. To prevent this, set up Secure Message Recovery.": "If you log out or use another device, you'll lose your secure message history. To prevent this, set up Secure Message Recovery.",
"Don't ask again": "Don't ask again",
"Set up": "Set up",
"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 main address, you must be a": "To change the room's main address, you must be a",
@ -1352,7 +1357,7 @@
"Secure your encrypted message history with a Recovery Passphrase.": "Secure your encrypted message history with a Recovery Passphrase.",
"You'll need it if you log out or lose access to this device.": "You'll need it if you log out or lose access to this device.",
"Enter a passphrase...": "Enter a passphrase...",
"If you don't want encrypted message history to be availble on other devices, <button>opt out</button>.": "If you don't want encrypted message history to be availble on other devices, <button>opt out</button>.",
"If you don't want encrypted message history to be available on other devices, <button>opt out</button>.": "If you don't want encrypted message history to be available on other devices, <button>opt out</button>.",
"Or, if you don't want to create a Recovery Passphrase, skip this step and <button>download a recovery key</button>.": "Or, if you don't want to create a Recovery Passphrase, skip this step and <button>download a recovery key</button>.",
"That matches!": "That matches!",
"That doesn't match.": "That doesn't match.",
@ -1384,6 +1389,8 @@
"Create Key Backup": "Create Key Backup",
"Unable to create key backup": "Unable to create key backup",
"Retry": "Retry",
"Without setting up Secure Message Recovery, you'll lose your secure message history when you log out.": "Without setting up Secure Message Recovery, you'll lose your secure message history when you log out.",
"If you don't want to set this up now, you can later in Settings.": "If you don't want to set this up now, you can later in Settings.",
"Failed to set direct chat tag": "Failed to set direct chat tag",
"Failed to remove tag %(tagName)s from room": "Failed to remove tag %(tagName)s from room",
"Failed to add tag %(tagName)s to room": "Failed to add tag %(tagName)s to room"

View File

@ -151,6 +151,11 @@ export const SETTINGS = {
displayName: _td('Always show encryption icons'),
default: true,
},
"showRoomRecoveryReminder": {
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
displayName: _td('Show a reminder to enable Secure Message Recovery in encrypted rooms'),
default: true,
},
"enableSyntaxHighlightLanguageDetection": {
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
displayName: _td('Enable automatic language detection for syntax highlighting'),