2015-11-30 15:52:41 +00:00
|
|
|
/*
|
2016-01-07 04:06:39 +00:00
|
|
|
Copyright 2015, 2016 OpenMarket Ltd
|
2017-03-22 15:18:27 +00:00
|
|
|
Copyright 2017 Vector Creations Ltd
|
2015-11-30 15:52:41 +00:00
|
|
|
|
|
|
|
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.
|
|
|
|
*/
|
2017-05-02 20:17:12 +00:00
|
|
|
const React = require('react');
|
|
|
|
const ReactDOM = require('react-dom');
|
|
|
|
const sdk = require('../../index');
|
|
|
|
const MatrixClientPeg = require("../../MatrixClientPeg");
|
|
|
|
const PlatformPeg = require("../../PlatformPeg");
|
|
|
|
const Modal = require('../../Modal');
|
|
|
|
const dis = require("../../dispatcher");
|
|
|
|
const q = require('q');
|
|
|
|
const packageJson = require('../../../package.json');
|
|
|
|
const UserSettingsStore = require('../../UserSettingsStore');
|
|
|
|
const GeminiScrollbar = require('react-gemini-scrollbar');
|
|
|
|
const Email = require('../../email');
|
|
|
|
const AddThreepid = require('../../AddThreepid');
|
|
|
|
const SdkConfig = require('../../SdkConfig');
|
2017-01-24 22:41:52 +00:00
|
|
|
import AccessibleButton from '../views/elements/AccessibleButton';
|
2015-11-30 15:52:41 +00:00
|
|
|
|
2016-06-17 16:09:52 +00:00
|
|
|
// if this looks like a release, use the 'version' from package.json; else use
|
2017-04-21 02:04:34 +00:00
|
|
|
// the git sha. Prepend version with v, to look like riot-web version
|
2017-05-02 20:17:12 +00:00
|
|
|
const REACT_SDK_VERSION = 'dist' in packageJson ? packageJson.version : packageJson.gitHead || '<local>';
|
2016-06-17 16:09:52 +00:00
|
|
|
|
2017-04-21 02:04:34 +00:00
|
|
|
// Simple method to help prettify GH Release Tags and Commit Hashes.
|
2017-05-04 15:22:39 +00:00
|
|
|
const semVerRegex = /^v?(\d+\.\d+\.\d+(?:-rc.+)?)(?:-(?:\d+-g)?([0-9a-fA-F]+))?(?:-dirty)?$/i;
|
2017-05-13 14:04:20 +00:00
|
|
|
const gHVersionLabel = function(repo, token='') {
|
2017-05-02 20:12:58 +00:00
|
|
|
const match = token.match(semVerRegex);
|
2017-05-04 15:22:39 +00:00
|
|
|
let url;
|
2017-05-02 20:12:58 +00:00
|
|
|
if (match && match[1]) { // basic semVer string possibly with commit hash
|
|
|
|
url = (match.length > 1 && match[2])
|
|
|
|
? `https://github.com/${repo}/commit/${match[2]}`
|
|
|
|
: `https://github.com/${repo}/releases/tag/v${match[1]}`;
|
|
|
|
} else {
|
|
|
|
url = `https://github.com/${repo}/commit/${token.split('-')[0]}`;
|
|
|
|
}
|
|
|
|
return <a href={url}>{token}</a>;
|
|
|
|
};
|
2017-01-18 14:06:47 +00:00
|
|
|
|
2017-01-18 16:36:27 +00:00
|
|
|
// Enumerate some simple 'flip a bit' UI settings (if any).
|
|
|
|
// 'id' gives the key name in the im.vector.web.settings account data event
|
|
|
|
// 'label' is how we describe it in the UI.
|
2017-01-18 14:06:47 +00:00
|
|
|
const SETTINGS_LABELS = [
|
2017-02-27 22:17:43 +00:00
|
|
|
{
|
|
|
|
id: 'autoplayGifsAndVideos',
|
|
|
|
label: 'Autoplay GIFs and videos',
|
|
|
|
},
|
2017-04-21 20:28:28 +00:00
|
|
|
{
|
|
|
|
id: 'hideReadReceipts',
|
2017-05-02 20:17:12 +00:00
|
|
|
label: 'Hide read receipts',
|
2017-04-21 20:28:28 +00:00
|
|
|
},
|
2017-04-21 20:50:26 +00:00
|
|
|
{
|
|
|
|
id: 'dontSendTypingNotifications',
|
|
|
|
label: "Don't send typing notifications",
|
|
|
|
},
|
2017-01-18 14:06:47 +00:00
|
|
|
/*
|
|
|
|
{
|
|
|
|
id: 'alwaysShowTimestamps',
|
|
|
|
label: 'Always show message timestamps',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: 'showTwelveHourTimestamps',
|
|
|
|
label: 'Show timestamps in 12 hour format (e.g. 2:30pm)',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: 'useCompactLayout',
|
|
|
|
label: 'Use compact timeline layout',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: 'useFixedWidthFont',
|
|
|
|
label: 'Use fixed width font',
|
|
|
|
},
|
|
|
|
*/
|
|
|
|
];
|
|
|
|
|
2017-01-21 17:39:31 +00:00
|
|
|
const CRYPTO_SETTINGS_LABELS = [
|
|
|
|
{
|
|
|
|
id: 'blacklistUnverifiedDevices',
|
2017-01-22 00:28:43 +00:00
|
|
|
label: 'Never send encrypted messages to unverified devices from this device',
|
2017-01-21 17:39:31 +00:00
|
|
|
},
|
|
|
|
// XXX: this is here for documentation; the actual setting is managed via RoomSettings
|
|
|
|
// {
|
|
|
|
// id: 'blacklistUnverifiedDevicesPerRoom'
|
|
|
|
// label: 'Never send encrypted messages to unverified devices in this room',
|
|
|
|
// }
|
|
|
|
];
|
|
|
|
|
2017-01-18 14:06:47 +00:00
|
|
|
// Enumerate the available themes, with a nice human text label.
|
2017-01-18 16:36:27 +00:00
|
|
|
// 'id' gives the key name in the im.vector.web.settings account data event
|
|
|
|
// 'value' is the value for that key in the event
|
|
|
|
// 'label' is how we describe it in the UI.
|
|
|
|
//
|
2017-01-18 14:06:47 +00:00
|
|
|
// XXX: Ideally we would have a theme manifest or something and they'd be nicely
|
|
|
|
// packaged up in a single directory, and/or located at the application layer.
|
|
|
|
// But for now for expedience we just hardcode them here.
|
|
|
|
const THEMES = [
|
|
|
|
{
|
|
|
|
id: 'theme',
|
|
|
|
label: 'Light theme',
|
|
|
|
value: 'light',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: 'theme',
|
|
|
|
label: 'Dark theme',
|
|
|
|
value: 'dark',
|
2017-05-02 20:17:12 +00:00
|
|
|
},
|
2017-01-18 14:06:47 +00:00
|
|
|
];
|
|
|
|
|
|
|
|
|
2015-11-30 15:52:41 +00:00
|
|
|
module.exports = React.createClass({
|
|
|
|
displayName: 'UserSettings',
|
2015-12-18 00:37:56 +00:00
|
|
|
|
2015-12-23 11:47:56 +00:00
|
|
|
propTypes: {
|
2016-06-08 13:54:34 +00:00
|
|
|
onClose: React.PropTypes.func,
|
|
|
|
// The brand string given when creating email pushers
|
|
|
|
brand: React.PropTypes.string,
|
2016-08-05 15:13:06 +00:00
|
|
|
|
|
|
|
// True to show the 'labs' section of experimental features
|
2016-08-05 15:36:35 +00:00
|
|
|
enableLabs: React.PropTypes.bool,
|
2016-09-13 11:18:22 +00:00
|
|
|
|
2017-01-31 15:17:43 +00:00
|
|
|
// The base URL to use in the referral link. Defaults to window.location.origin.
|
|
|
|
referralBaseUrl: React.PropTypes.string,
|
|
|
|
|
2016-09-13 11:18:22 +00:00
|
|
|
// true if RightPanel is collapsed
|
|
|
|
collapsedRhs: React.PropTypes.bool,
|
2017-02-16 18:00:52 +00:00
|
|
|
|
|
|
|
// Team token for the referral link. If falsy, the referral section will
|
|
|
|
// not appear
|
|
|
|
teamToken: React.PropTypes.string,
|
2015-12-23 11:47:56 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
getDefaultProps: function() {
|
|
|
|
return {
|
2016-08-05 15:13:06 +00:00
|
|
|
onClose: function() {},
|
|
|
|
enableLabs: true,
|
2015-12-23 11:47:56 +00:00
|
|
|
};
|
2015-11-30 15:52:41 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
getInitialState: function() {
|
|
|
|
return {
|
|
|
|
avatarUrl: null,
|
2017-05-13 14:04:20 +00:00
|
|
|
threepids: [],
|
2015-12-23 16:02:18 +00:00
|
|
|
phase: "UserSettings.LOADING", // LOADING, DISPLAY
|
2016-01-19 16:36:54 +00:00
|
|
|
email_add_pending: false,
|
2017-05-13 14:04:20 +00:00
|
|
|
vectorVersion: undefined,
|
2016-12-14 16:00:50 +00:00
|
|
|
rejectingInvites: false,
|
2015-11-30 15:52:41 +00:00
|
|
|
};
|
|
|
|
},
|
|
|
|
|
|
|
|
componentWillMount: function() {
|
2016-11-08 11:43:24 +00:00
|
|
|
this._unmounted = false;
|
2017-03-16 14:56:26 +00:00
|
|
|
this._addThreepid = null;
|
2016-11-08 11:43:24 +00:00
|
|
|
|
2016-11-08 10:45:19 +00:00
|
|
|
if (PlatformPeg.get()) {
|
2016-11-11 10:05:53 +00:00
|
|
|
q().then(() => {
|
|
|
|
return PlatformPeg.get().getAppVersion();
|
|
|
|
}).done((appVersion) => {
|
2016-11-08 11:43:24 +00:00
|
|
|
if (this._unmounted) return;
|
2016-11-08 10:45:19 +00:00
|
|
|
this.setState({
|
|
|
|
vectorVersion: appVersion,
|
|
|
|
});
|
|
|
|
}, (e) => {
|
|
|
|
console.log("Failed to fetch app version", e);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-12-14 16:00:50 +00:00
|
|
|
// Bulk rejecting invites:
|
|
|
|
// /sync won't have had time to return when UserSettings re-renders from state changes, so getRooms()
|
|
|
|
// will still return rooms with invites. To get around this, add a listener for
|
|
|
|
// membership updates and kick the UI.
|
|
|
|
MatrixClientPeg.get().on("RoomMember.membership", this._onInviteStateChange);
|
|
|
|
|
2016-04-15 17:30:13 +00:00
|
|
|
dis.dispatch({
|
|
|
|
action: 'ui_opacity',
|
|
|
|
sideOpacity: 0.3,
|
|
|
|
middleOpacity: 0.3,
|
|
|
|
});
|
2015-12-23 16:52:59 +00:00
|
|
|
this._refreshFromServer();
|
2017-01-18 14:06:47 +00:00
|
|
|
|
2017-05-02 20:17:12 +00:00
|
|
|
const syncedSettings = UserSettingsStore.getSyncedSettings();
|
2017-01-18 14:06:47 +00:00
|
|
|
if (!syncedSettings.theme) {
|
|
|
|
syncedSettings.theme = 'light';
|
|
|
|
}
|
|
|
|
this._syncedSettings = syncedSettings;
|
2017-01-21 17:39:31 +00:00
|
|
|
|
|
|
|
this._localSettings = UserSettingsStore.getLocalSettings();
|
2015-11-30 15:52:41 +00:00
|
|
|
},
|
|
|
|
|
2015-12-18 00:37:56 +00:00
|
|
|
componentDidMount: function() {
|
|
|
|
this.dispatcherRef = dis.register(this.onAction);
|
2015-12-23 16:02:18 +00:00
|
|
|
this._me = MatrixClientPeg.get().credentials.userId;
|
2015-12-18 00:37:56 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
componentWillUnmount: function() {
|
2016-11-08 11:43:24 +00:00
|
|
|
this._unmounted = true;
|
2016-04-15 17:30:13 +00:00
|
|
|
dis.dispatch({
|
|
|
|
action: 'ui_opacity',
|
|
|
|
sideOpacity: 1.0,
|
|
|
|
middleOpacity: 1.0,
|
|
|
|
});
|
2015-12-18 00:37:56 +00:00
|
|
|
dis.unregister(this.dispatcherRef);
|
2017-05-02 20:17:12 +00:00
|
|
|
const cli = MatrixClientPeg.get();
|
2016-12-15 16:13:09 +00:00
|
|
|
if (cli) {
|
|
|
|
cli.removeListener("RoomMember.membership", this._onInviteStateChange);
|
|
|
|
}
|
2015-12-18 00:37:56 +00:00
|
|
|
},
|
|
|
|
|
2015-12-23 16:52:59 +00:00
|
|
|
_refreshFromServer: function() {
|
2017-05-02 20:17:12 +00:00
|
|
|
const self = this;
|
2015-12-23 16:52:59 +00:00
|
|
|
q.all([
|
2017-05-02 20:17:12 +00:00
|
|
|
UserSettingsStore.loadProfileInfo(), UserSettingsStore.loadThreePids(),
|
2015-12-23 16:52:59 +00:00
|
|
|
]).done(function(resps) {
|
2015-12-23 11:47:56 +00:00
|
|
|
self.setState({
|
2015-12-23 16:52:59 +00:00
|
|
|
avatarUrl: resps[0].avatar_url,
|
|
|
|
threepids: resps[1].threepids,
|
2015-12-23 11:47:56 +00:00
|
|
|
phase: "UserSettings.DISPLAY",
|
2015-12-18 00:37:56 +00:00
|
|
|
});
|
2015-12-23 11:47:56 +00:00
|
|
|
}, function(error) {
|
2017-05-02 20:17:12 +00:00
|
|
|
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
2017-03-12 22:59:41 +00:00
|
|
|
console.error("Failed to load user settings: " + error);
|
2015-12-23 11:47:56 +00:00
|
|
|
Modal.createDialog(ErrorDialog, {
|
2015-12-23 16:52:59 +00:00
|
|
|
title: "Can't load user settings",
|
2017-04-23 00:48:27 +00:00
|
|
|
description: ((error && error.message) ? error.message : "Server may be unavailable or overloaded"),
|
2015-12-23 11:47:56 +00:00
|
|
|
});
|
2015-12-23 16:52:59 +00:00
|
|
|
});
|
|
|
|
},
|
2015-12-18 00:37:56 +00:00
|
|
|
|
|
|
|
onAction: function(payload) {
|
|
|
|
if (payload.action === "notifier_enabled") {
|
2015-12-23 17:06:30 +00:00
|
|
|
this.forceUpdate();
|
2015-12-18 00:37:56 +00:00
|
|
|
}
|
2015-11-30 15:52:41 +00:00
|
|
|
},
|
|
|
|
|
2016-01-15 12:35:30 +00:00
|
|
|
onAvatarPickerClick: function(ev) {
|
2016-03-15 23:55:59 +00:00
|
|
|
if (MatrixClientPeg.get().isGuest()) {
|
2017-05-02 20:17:12 +00:00
|
|
|
const NeedToRegisterDialog = sdk.getComponent("dialogs.NeedToRegisterDialog");
|
2016-03-22 13:50:27 +00:00
|
|
|
Modal.createDialog(NeedToRegisterDialog, {
|
|
|
|
title: "Please Register",
|
2016-03-15 23:55:59 +00:00
|
|
|
description: "Guests can't set avatars. Please register.",
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-01-15 12:35:30 +00:00
|
|
|
if (this.refs.file_label) {
|
|
|
|
this.refs.file_label.click();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2015-12-23 16:52:59 +00:00
|
|
|
onAvatarSelected: function(ev) {
|
2017-05-02 20:17:12 +00:00
|
|
|
const self = this;
|
|
|
|
const changeAvatar = this.refs.changeAvatar;
|
2015-12-23 16:52:59 +00:00
|
|
|
if (!changeAvatar) {
|
|
|
|
console.error("No ChangeAvatar found to upload image to!");
|
|
|
|
return;
|
|
|
|
}
|
2015-12-23 17:30:25 +00:00
|
|
|
changeAvatar.onFileSelected(ev).done(function() {
|
|
|
|
// dunno if the avatar changed, re-check it.
|
|
|
|
self._refreshFromServer();
|
|
|
|
}, function(err) {
|
2017-05-02 20:17:12 +00:00
|
|
|
// const errMsg = (typeof err === "string") ? err : (err.error || "");
|
2017-03-12 22:59:41 +00:00
|
|
|
console.error("Failed to set avatar: " + err);
|
2017-05-02 20:17:12 +00:00
|
|
|
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
2015-12-23 16:52:59 +00:00
|
|
|
Modal.createDialog(ErrorDialog, {
|
2017-04-23 00:48:27 +00:00
|
|
|
title: "Failed to set avatar",
|
|
|
|
description: ((err && err.message) ? err.message : "Operation failed"),
|
2015-12-23 16:52:59 +00:00
|
|
|
});
|
2015-12-23 17:30:25 +00:00
|
|
|
});
|
2015-11-30 15:52:41 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
onLogoutClicked: function(ev) {
|
2017-05-02 20:17:12 +00:00
|
|
|
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
2017-01-24 22:18:25 +00:00
|
|
|
Modal.createDialog(QuestionDialog, {
|
|
|
|
title: "Sign out?",
|
|
|
|
description:
|
|
|
|
<div>
|
2017-04-07 22:34:11 +00:00
|
|
|
For security, logging out will delete any end-to-end encryption keys from this browser.
|
|
|
|
|
|
|
|
If you want to be able to decrypt your conversation history from future Riot sessions,
|
|
|
|
please export your room keys for safe-keeping.
|
2017-01-24 22:18:25 +00:00
|
|
|
</div>,
|
|
|
|
button: "Sign out",
|
2017-03-16 14:18:18 +00:00
|
|
|
extraButtons: [
|
2017-04-22 15:26:28 +00:00
|
|
|
<button key="export" className="mx_Dialog_primary"
|
2017-03-16 14:18:18 +00:00
|
|
|
onClick={this._onExportE2eKeysClicked}>
|
|
|
|
Export E2E room keys
|
2017-05-02 20:17:12 +00:00
|
|
|
</button>,
|
2017-03-16 14:18:18 +00:00
|
|
|
],
|
2017-01-24 22:18:25 +00:00
|
|
|
onFinished: (confirmed) => {
|
|
|
|
if (confirmed) {
|
|
|
|
dis.dispatch({action: 'logout'});
|
|
|
|
if (this.props.onFinished) {
|
|
|
|
this.props.onFinished();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
});
|
2015-11-30 15:52:41 +00:00
|
|
|
},
|
|
|
|
|
2015-12-23 15:38:28 +00:00
|
|
|
onPasswordChangeError: function(err) {
|
2017-05-02 20:17:12 +00:00
|
|
|
let errMsg = err.error || "";
|
2015-12-23 15:38:28 +00:00
|
|
|
if (err.httpStatus === 403) {
|
|
|
|
errMsg = "Failed to change password. Is your password correct?";
|
2017-05-02 20:17:12 +00:00
|
|
|
} else if (err.httpStatus) {
|
2015-12-23 15:38:28 +00:00
|
|
|
errMsg += ` (HTTP status ${err.httpStatus})`;
|
|
|
|
}
|
2017-05-02 20:17:12 +00:00
|
|
|
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
2017-03-12 22:59:41 +00:00
|
|
|
console.error("Failed to change password: " + errMsg);
|
2015-12-23 15:38:28 +00:00
|
|
|
Modal.createDialog(ErrorDialog, {
|
|
|
|
title: "Error",
|
2017-05-02 20:17:12 +00:00
|
|
|
description: errMsg,
|
2015-12-23 15:38:28 +00:00
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
onPasswordChanged: function() {
|
2017-05-02 20:17:12 +00:00
|
|
|
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
2015-12-23 15:38:28 +00:00
|
|
|
Modal.createDialog(ErrorDialog, {
|
|
|
|
title: "Success",
|
|
|
|
description: `Your password was successfully changed. You will not
|
|
|
|
receive push notifications on other devices until you
|
2017-05-02 20:17:12 +00:00
|
|
|
log back in to them.`,
|
2015-12-23 15:38:28 +00:00
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2016-01-07 17:23:32 +00:00
|
|
|
onUpgradeClicked: function() {
|
|
|
|
dis.dispatch({
|
2017-05-02 20:17:12 +00:00
|
|
|
action: "start_upgrade_registration",
|
2016-01-07 17:23:32 +00:00
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2016-01-19 16:36:54 +00:00
|
|
|
onEnableNotificationsChange: function(event) {
|
|
|
|
UserSettingsStore.setEnableNotifications(event.target.checked);
|
|
|
|
},
|
|
|
|
|
2017-03-16 14:56:26 +00:00
|
|
|
_onAddEmailEditFinished: function(value, shouldSubmit) {
|
2016-01-19 16:36:54 +00:00
|
|
|
if (!shouldSubmit) return;
|
2017-03-16 14:56:26 +00:00
|
|
|
this._addEmail();
|
|
|
|
},
|
|
|
|
|
|
|
|
_addEmail: function() {
|
2017-05-02 20:17:12 +00:00
|
|
|
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
|
|
|
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
2016-01-19 16:36:54 +00:00
|
|
|
|
2017-05-02 20:17:12 +00:00
|
|
|
const emailAddress = this.refs.add_email_input.value;
|
|
|
|
if (!Email.looksValid(emailAddress)) {
|
2016-01-19 16:36:54 +00:00
|
|
|
Modal.createDialog(ErrorDialog, {
|
|
|
|
title: "Invalid Email Address",
|
|
|
|
description: "This doesn't appear to be a valid email address",
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
2017-03-16 14:56:26 +00:00
|
|
|
this._addThreepid = new AddThreepid();
|
2016-01-19 16:36:54 +00:00
|
|
|
// we always bind emails when registering, so let's do the
|
|
|
|
// same here.
|
2017-05-02 20:17:12 +00:00
|
|
|
this._addThreepid.addEmailAddress(emailAddress, true).done(() => {
|
2016-01-19 16:36:54 +00:00
|
|
|
Modal.createDialog(QuestionDialog, {
|
|
|
|
title: "Verification Pending",
|
|
|
|
description: "Please check your email and click on the link it contains. Once this is done, click continue.",
|
|
|
|
button: 'Continue',
|
|
|
|
onFinished: this.onEmailDialogFinished,
|
|
|
|
});
|
|
|
|
}, (err) => {
|
2016-07-08 16:28:04 +00:00
|
|
|
this.setState({email_add_pending: false});
|
2017-05-02 20:17:12 +00:00
|
|
|
console.error("Unable to add email address " + emailAddress + " " + err);
|
2016-01-19 16:36:54 +00:00
|
|
|
Modal.createDialog(ErrorDialog, {
|
2017-04-23 00:48:27 +00:00
|
|
|
title: "Unable to add email address",
|
|
|
|
description: ((err && err.message) ? err.message : "Operation failed"),
|
2016-01-19 16:36:54 +00:00
|
|
|
});
|
|
|
|
});
|
2017-03-16 14:56:26 +00:00
|
|
|
ReactDOM.findDOMNode(this.refs.add_email_input).blur();
|
2016-01-19 16:36:54 +00:00
|
|
|
this.setState({email_add_pending: true});
|
|
|
|
},
|
|
|
|
|
2016-12-21 18:49:38 +00:00
|
|
|
onRemoveThreepidClicked: function(threepid) {
|
|
|
|
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
|
|
|
Modal.createDialog(QuestionDialog, {
|
|
|
|
title: "Remove Contact Information?",
|
|
|
|
description: "Remove " + threepid.address + "?",
|
|
|
|
button: 'Remove',
|
|
|
|
onFinished: (submit) => {
|
|
|
|
if (submit) {
|
|
|
|
this.setState({
|
|
|
|
phase: "UserSettings.LOADING",
|
|
|
|
});
|
|
|
|
MatrixClientPeg.get().deleteThreePid(threepid.medium, threepid.address).then(() => {
|
|
|
|
return this._refreshFromServer();
|
2016-12-22 15:26:08 +00:00
|
|
|
}).catch((err) => {
|
2016-12-21 18:49:38 +00:00
|
|
|
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
2017-03-12 22:59:41 +00:00
|
|
|
console.error("Unable to remove contact information: " + err);
|
2016-12-21 18:49:38 +00:00
|
|
|
Modal.createDialog(ErrorDialog, {
|
2017-04-23 00:48:27 +00:00
|
|
|
title: "Unable to remove contact information",
|
|
|
|
description: ((err && err.message) ? err.message : "Operation failed"),
|
2016-12-21 18:49:38 +00:00
|
|
|
});
|
|
|
|
}).done();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2016-01-19 16:36:54 +00:00
|
|
|
onEmailDialogFinished: function(ok) {
|
|
|
|
if (ok) {
|
|
|
|
this.verifyEmailAddress();
|
|
|
|
} else {
|
|
|
|
this.setState({email_add_pending: false});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
verifyEmailAddress: function() {
|
2017-03-16 14:56:26 +00:00
|
|
|
this._addThreepid.checkEmailLinkClicked().done(() => {
|
|
|
|
this._addThreepid = null;
|
2016-01-19 16:36:54 +00:00
|
|
|
this.setState({
|
|
|
|
phase: "UserSettings.LOADING",
|
|
|
|
});
|
|
|
|
this._refreshFromServer();
|
|
|
|
this.setState({email_add_pending: false});
|
|
|
|
}, (err) => {
|
2016-03-24 15:03:44 +00:00
|
|
|
this.setState({email_add_pending: false});
|
2017-05-02 20:17:12 +00:00
|
|
|
if (err.errcode === 'M_THREEPID_AUTH_FAILED') {
|
|
|
|
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
|
|
|
let message = "Unable to verify email address. ";
|
2017-01-20 14:22:27 +00:00
|
|
|
message += "Please check your email and click on the link it contains. Once this is done, click continue.";
|
2016-01-19 16:36:54 +00:00
|
|
|
Modal.createDialog(QuestionDialog, {
|
|
|
|
title: "Verification Pending",
|
|
|
|
description: message,
|
|
|
|
button: 'Continue',
|
|
|
|
onFinished: this.onEmailDialogFinished,
|
|
|
|
});
|
|
|
|
} else {
|
2017-05-02 20:17:12 +00:00
|
|
|
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
2017-03-12 22:59:41 +00:00
|
|
|
console.error("Unable to verify email address: " + err);
|
2016-01-19 16:36:54 +00:00
|
|
|
Modal.createDialog(ErrorDialog, {
|
2017-04-23 00:48:27 +00:00
|
|
|
title: "Unable to verify email address",
|
|
|
|
description: ((err && err.message) ? err.message : "Operation failed"),
|
2016-01-19 16:36:54 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2016-08-02 17:40:12 +00:00
|
|
|
_onDeactivateAccountClicked: function() {
|
|
|
|
const DeactivateAccountDialog = sdk.getComponent("dialogs.DeactivateAccountDialog");
|
|
|
|
Modal.createDialog(DeactivateAccountDialog, {});
|
|
|
|
},
|
|
|
|
|
2017-01-24 14:47:11 +00:00
|
|
|
_onBugReportClicked: function() {
|
|
|
|
const BugReportDialog = sdk.getComponent("dialogs.BugReportDialog");
|
2017-01-25 16:33:00 +00:00
|
|
|
if (!BugReportDialog) {
|
|
|
|
return;
|
|
|
|
}
|
2017-01-24 14:47:11 +00:00
|
|
|
Modal.createDialog(BugReportDialog, {});
|
|
|
|
},
|
|
|
|
|
2017-02-17 15:16:28 +00:00
|
|
|
_onClearCacheClicked: function() {
|
2017-04-10 16:39:27 +00:00
|
|
|
if (!PlatformPeg.get()) return;
|
|
|
|
|
2017-04-11 17:16:29 +00:00
|
|
|
MatrixClientPeg.get().stopClient();
|
2017-02-17 15:37:49 +00:00
|
|
|
MatrixClientPeg.get().store.deleteAllData().done(() => {
|
2017-04-10 16:39:27 +00:00
|
|
|
PlatformPeg.get().reload();
|
2017-02-17 15:16:28 +00:00
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2016-12-14 16:00:50 +00:00
|
|
|
_onInviteStateChange: function(event, member, oldMembership) {
|
|
|
|
if (member.userId === this._me && oldMembership === "invite") {
|
|
|
|
this.forceUpdate();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
_onRejectAllInvitesClicked: function(rooms, ev) {
|
|
|
|
this.setState({
|
2017-05-02 20:17:12 +00:00
|
|
|
rejectingInvites: true,
|
2016-12-14 16:00:50 +00:00
|
|
|
});
|
|
|
|
// reject the invites
|
2017-05-02 20:17:12 +00:00
|
|
|
const promises = rooms.map((room) => {
|
2016-12-14 16:00:50 +00:00
|
|
|
return MatrixClientPeg.get().leave(room.roomId);
|
|
|
|
});
|
|
|
|
// purposefully drop errors to the floor: we'll just have a non-zero number on the UI
|
|
|
|
// after trying to reject all the invites.
|
|
|
|
q.allSettled(promises).then(() => {
|
|
|
|
this.setState({
|
2017-05-02 20:17:12 +00:00
|
|
|
rejectingInvites: false,
|
2016-12-14 16:00:50 +00:00
|
|
|
});
|
2016-12-15 14:17:29 +00:00
|
|
|
}).done();
|
2016-12-14 15:01:50 +00:00
|
|
|
},
|
|
|
|
|
2017-01-20 15:12:50 +00:00
|
|
|
_onExportE2eKeysClicked: function() {
|
|
|
|
Modal.createDialogAsync(
|
|
|
|
(cb) => {
|
2017-01-24 15:57:42 +00:00
|
|
|
require.ensure(['../../async-components/views/dialogs/ExportE2eKeysDialog'], () => {
|
|
|
|
cb(require('../../async-components/views/dialogs/ExportE2eKeysDialog'));
|
|
|
|
}, "e2e-export");
|
|
|
|
}, {
|
|
|
|
matrixClient: MatrixClientPeg.get(),
|
2017-05-02 20:17:12 +00:00
|
|
|
},
|
2017-01-24 15:57:42 +00:00
|
|
|
);
|
|
|
|
},
|
|
|
|
|
|
|
|
_onImportE2eKeysClicked: function() {
|
|
|
|
Modal.createDialogAsync(
|
|
|
|
(cb) => {
|
|
|
|
require.ensure(['../../async-components/views/dialogs/ImportE2eKeysDialog'], () => {
|
|
|
|
cb(require('../../async-components/views/dialogs/ImportE2eKeysDialog'));
|
|
|
|
}, "e2e-export");
|
2017-01-20 15:12:50 +00:00
|
|
|
}, {
|
|
|
|
matrixClient: MatrixClientPeg.get(),
|
2017-05-02 20:17:12 +00:00
|
|
|
},
|
2017-01-20 15:12:50 +00:00
|
|
|
);
|
|
|
|
},
|
|
|
|
|
2017-01-31 13:17:01 +00:00
|
|
|
_renderReferral: function() {
|
2017-02-16 18:00:52 +00:00
|
|
|
const teamToken = this.props.teamToken;
|
2017-01-31 13:17:01 +00:00
|
|
|
if (!teamToken) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
if (typeof teamToken !== 'string') {
|
|
|
|
console.warn('Team token not a string');
|
|
|
|
return null;
|
|
|
|
}
|
2017-01-31 15:17:43 +00:00
|
|
|
const href = (this.props.referralBaseUrl || window.location.origin) +
|
2017-01-31 13:17:01 +00:00
|
|
|
`/#/register?referrer=${this._me}&team_token=${teamToken}`;
|
|
|
|
return (
|
|
|
|
<div>
|
|
|
|
<h3>Referral</h3>
|
|
|
|
<div className="mx_UserSettings_section">
|
|
|
|
Refer a friend to Riot: <a href={href}>{href}</a>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
},
|
|
|
|
|
2016-07-18 00:35:42 +00:00
|
|
|
_renderUserInterfaceSettings: function() {
|
|
|
|
return (
|
|
|
|
<div>
|
|
|
|
<h3>User Interface</h3>
|
|
|
|
<div className="mx_UserSettings_section">
|
2017-01-18 14:06:47 +00:00
|
|
|
{ this._renderUrlPreviewSelector() }
|
|
|
|
{ SETTINGS_LABELS.map( this._renderSyncedSetting ) }
|
|
|
|
{ THEMES.map( this._renderThemeSelector ) }
|
2016-07-18 00:35:42 +00:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
},
|
|
|
|
|
2017-01-18 14:06:47 +00:00
|
|
|
_renderUrlPreviewSelector: function() {
|
|
|
|
return <div className="mx_UserSettings_toggle">
|
|
|
|
<input id="urlPreviewsDisabled"
|
|
|
|
type="checkbox"
|
|
|
|
defaultChecked={ UserSettingsStore.getUrlPreviewsDisabled() }
|
2017-05-02 20:17:12 +00:00
|
|
|
onChange={ (e) => UserSettingsStore.setUrlPreviewsDisabled(e.target.checked) }
|
2017-01-18 14:06:47 +00:00
|
|
|
/>
|
|
|
|
<label htmlFor="urlPreviewsDisabled">
|
|
|
|
Disable inline URL previews by default
|
|
|
|
</label>
|
2017-01-20 14:22:27 +00:00
|
|
|
</div>;
|
2017-01-18 14:06:47 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
_renderSyncedSetting: function(setting) {
|
|
|
|
return <div className="mx_UserSettings_toggle" key={ setting.id }>
|
|
|
|
<input id={ setting.id }
|
|
|
|
type="checkbox"
|
|
|
|
defaultChecked={ this._syncedSettings[setting.id] }
|
2017-05-02 20:17:12 +00:00
|
|
|
onChange={ (e) => UserSettingsStore.setSyncedSetting(setting.id, e.target.checked) }
|
2017-01-18 14:06:47 +00:00
|
|
|
/>
|
|
|
|
<label htmlFor={ setting.id }>
|
|
|
|
{ setting.label }
|
|
|
|
</label>
|
2017-01-20 14:22:27 +00:00
|
|
|
</div>;
|
2017-01-18 14:06:47 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
_renderThemeSelector: function(setting) {
|
|
|
|
return <div className="mx_UserSettings_toggle" key={ setting.id + "_" + setting.value }>
|
|
|
|
<input id={ setting.id + "_" + setting.value }
|
|
|
|
type="radio"
|
|
|
|
name={ setting.id }
|
|
|
|
value={ setting.value }
|
|
|
|
defaultChecked={ this._syncedSettings[setting.id] === setting.value }
|
2017-05-02 20:17:12 +00:00
|
|
|
onChange={ (e) => {
|
2017-01-18 14:06:47 +00:00
|
|
|
if (e.target.checked) {
|
2017-01-20 14:22:27 +00:00
|
|
|
UserSettingsStore.setSyncedSetting(setting.id, setting.value);
|
2017-01-18 14:06:47 +00:00
|
|
|
}
|
|
|
|
dis.dispatch({
|
|
|
|
action: 'set_theme',
|
|
|
|
value: setting.value,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/>
|
|
|
|
<label htmlFor={ setting.id + "_" + setting.value }>
|
|
|
|
{ setting.label }
|
|
|
|
</label>
|
2017-01-20 14:22:27 +00:00
|
|
|
</div>;
|
2017-01-18 14:06:47 +00:00
|
|
|
},
|
|
|
|
|
2016-08-01 12:42:29 +00:00
|
|
|
_renderCryptoInfo: function() {
|
2016-12-05 18:33:38 +00:00
|
|
|
const client = MatrixClientPeg.get();
|
2016-12-05 20:03:43 +00:00
|
|
|
const deviceId = client.deviceId;
|
|
|
|
const identityKey = client.getDeviceEd25519Key() || "<not supported>";
|
2016-09-15 00:55:51 +00:00
|
|
|
|
2017-02-09 02:00:58 +00:00
|
|
|
let importExportButtons = null;
|
2017-01-20 15:12:50 +00:00
|
|
|
|
|
|
|
if (client.isCryptoEnabled) {
|
2017-02-09 02:00:58 +00:00
|
|
|
importExportButtons = (
|
|
|
|
<div className="mx_UserSettings_importExportButtons">
|
|
|
|
<AccessibleButton className="mx_UserSettings_button"
|
|
|
|
onClick={this._onExportE2eKeysClicked}>
|
|
|
|
Export E2E room keys
|
|
|
|
</AccessibleButton>
|
|
|
|
<AccessibleButton className="mx_UserSettings_button"
|
|
|
|
onClick={this._onImportE2eKeysClicked}>
|
|
|
|
Import E2E room keys
|
|
|
|
</AccessibleButton>
|
|
|
|
</div>
|
2017-01-24 15:57:42 +00:00
|
|
|
);
|
2017-01-20 15:12:50 +00:00
|
|
|
}
|
2016-06-08 12:09:07 +00:00
|
|
|
return (
|
|
|
|
<div>
|
|
|
|
<h3>Cryptography</h3>
|
2016-09-15 00:55:51 +00:00
|
|
|
<div className="mx_UserSettings_section mx_UserSettings_cryptoSection">
|
2016-06-08 12:09:07 +00:00
|
|
|
<ul>
|
2017-05-04 14:50:52 +00:00
|
|
|
<li><label>Device ID:</label> <span><code>{deviceId}</code></span></li>
|
|
|
|
<li><label>Device key:</label> <span><code><b>{identityKey}</b></code></span></li>
|
2016-06-08 12:09:07 +00:00
|
|
|
</ul>
|
2017-02-09 02:00:58 +00:00
|
|
|
{ importExportButtons }
|
2016-06-08 12:09:07 +00:00
|
|
|
</div>
|
2017-01-21 21:27:55 +00:00
|
|
|
<div className="mx_UserSettings_section">
|
|
|
|
{ CRYPTO_SETTINGS_LABELS.map( this._renderLocalSetting ) }
|
|
|
|
</div>
|
2016-06-08 12:09:07 +00:00
|
|
|
</div>
|
|
|
|
);
|
|
|
|
},
|
|
|
|
|
2017-01-21 17:39:31 +00:00
|
|
|
_renderLocalSetting: function(setting) {
|
|
|
|
const client = MatrixClientPeg.get();
|
|
|
|
return <div className="mx_UserSettings_toggle" key={ setting.id }>
|
|
|
|
<input id={ setting.id }
|
|
|
|
type="checkbox"
|
|
|
|
defaultChecked={ this._localSettings[setting.id] }
|
|
|
|
onChange={
|
2017-05-02 20:17:12 +00:00
|
|
|
(e) => {
|
|
|
|
UserSettingsStore.setLocalSetting(setting.id, e.target.checked);
|
2017-01-21 17:39:31 +00:00
|
|
|
if (setting.id === 'blacklistUnverifiedDevices') { // XXX: this is a bit ugly
|
|
|
|
client.setGlobalBlacklistUnverifiedDevices(e.target.checked);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/>
|
|
|
|
<label htmlFor={ setting.id }>
|
|
|
|
{ setting.label }
|
|
|
|
</label>
|
|
|
|
</div>;
|
|
|
|
},
|
|
|
|
|
2016-08-01 12:42:29 +00:00
|
|
|
_renderDevicesPanel: function() {
|
2017-05-02 20:17:12 +00:00
|
|
|
const DevicesPanel = sdk.getComponent('settings.DevicesPanel');
|
2016-08-01 12:42:29 +00:00
|
|
|
return (
|
|
|
|
<div>
|
|
|
|
<h3>Devices</h3>
|
2016-09-15 00:55:51 +00:00
|
|
|
<DevicesPanel className="mx_UserSettings_section"/>
|
2016-08-01 12:42:29 +00:00
|
|
|
</div>
|
|
|
|
);
|
|
|
|
},
|
|
|
|
|
2017-01-24 14:47:11 +00:00
|
|
|
_renderBugReport: function() {
|
2017-01-25 14:43:47 +00:00
|
|
|
if (!SdkConfig.get().bug_report_endpoint_url) {
|
2017-05-02 20:17:12 +00:00
|
|
|
return <div />;
|
2017-01-25 14:43:47 +00:00
|
|
|
}
|
2017-01-24 14:47:11 +00:00
|
|
|
return (
|
|
|
|
<div>
|
|
|
|
<h3>Bug Report</h3>
|
|
|
|
<div className="mx_UserSettings_section">
|
|
|
|
<p>Found a bug?</p>
|
|
|
|
<button className="mx_UserSettings_button danger"
|
|
|
|
onClick={this._onBugReportClicked}>Report it
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
},
|
|
|
|
|
2017-01-20 14:22:27 +00:00
|
|
|
_renderLabs: function() {
|
2016-08-05 16:18:45 +00:00
|
|
|
// default to enabled if undefined
|
2016-08-05 15:36:35 +00:00
|
|
|
if (this.props.enableLabs === false) return null;
|
2016-08-05 15:13:06 +00:00
|
|
|
|
2017-05-02 20:17:12 +00:00
|
|
|
const features = UserSettingsStore.LABS_FEATURES.map((feature) => (
|
2016-08-01 12:42:29 +00:00
|
|
|
<div key={feature.id} className="mx_UserSettings_toggle">
|
|
|
|
<input
|
|
|
|
type="checkbox"
|
|
|
|
id={feature.id}
|
|
|
|
name={feature.id}
|
2016-09-16 23:54:56 +00:00
|
|
|
defaultChecked={ UserSettingsStore.isFeatureEnabled(feature.id) }
|
2017-05-02 20:17:12 +00:00
|
|
|
onChange={(e) => {
|
2016-09-17 13:29:40 +00:00
|
|
|
if (MatrixClientPeg.get().isGuest()) {
|
|
|
|
e.target.checked = false;
|
2017-05-02 20:17:12 +00:00
|
|
|
const NeedToRegisterDialog = sdk.getComponent("dialogs.NeedToRegisterDialog");
|
2016-09-17 13:29:40 +00:00
|
|
|
Modal.createDialog(NeedToRegisterDialog, {
|
|
|
|
title: "Please Register",
|
|
|
|
description: "Guests can't use labs features. Please register.",
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-08-01 12:42:29 +00:00
|
|
|
UserSettingsStore.setFeatureEnabled(feature.id, e.target.checked);
|
|
|
|
this.forceUpdate();
|
|
|
|
}}/>
|
|
|
|
<label htmlFor={feature.id}>{feature.name}</label>
|
|
|
|
</div>
|
|
|
|
));
|
|
|
|
return (
|
|
|
|
<div>
|
|
|
|
<h3>Labs</h3>
|
|
|
|
<div className="mx_UserSettings_section">
|
|
|
|
<p>These are experimental features that may break in unexpected ways. Use with caution.</p>
|
|
|
|
{features}
|
|
|
|
</div>
|
|
|
|
</div>
|
2017-01-20 14:22:27 +00:00
|
|
|
);
|
2016-08-01 12:42:29 +00:00
|
|
|
},
|
|
|
|
|
2016-08-02 17:40:12 +00:00
|
|
|
_renderDeactivateAccount: function() {
|
2016-08-03 10:34:31 +00:00
|
|
|
// We can't deactivate a guest account.
|
|
|
|
if (MatrixClientPeg.get().isGuest()) return null;
|
|
|
|
|
2016-08-02 17:40:12 +00:00
|
|
|
return <div>
|
2016-08-02 17:42:02 +00:00
|
|
|
<h3>Deactivate Account</h3>
|
2016-08-02 17:40:12 +00:00
|
|
|
<div className="mx_UserSettings_section">
|
2017-01-13 16:25:26 +00:00
|
|
|
<AccessibleButton className="mx_UserSettings_button danger"
|
2016-08-02 17:40:12 +00:00
|
|
|
onClick={this._onDeactivateAccountClicked}>Deactivate my account
|
2017-01-13 16:25:26 +00:00
|
|
|
</AccessibleButton>
|
2016-08-02 17:40:12 +00:00
|
|
|
</div>
|
|
|
|
</div>;
|
|
|
|
},
|
|
|
|
|
2017-02-17 15:16:28 +00:00
|
|
|
_renderClearCache: function() {
|
|
|
|
return <div>
|
|
|
|
<h3>Clear Cache</h3>
|
|
|
|
<div className="mx_UserSettings_section">
|
|
|
|
<AccessibleButton className="mx_UserSettings_button danger"
|
|
|
|
onClick={this._onClearCacheClicked}>
|
2017-02-17 15:37:49 +00:00
|
|
|
Clear Cache and Reload
|
2017-02-17 15:16:28 +00:00
|
|
|
</AccessibleButton>
|
|
|
|
</div>
|
|
|
|
</div>;
|
|
|
|
},
|
|
|
|
|
2016-12-14 15:01:50 +00:00
|
|
|
_renderBulkOptions: function() {
|
2017-05-02 20:17:12 +00:00
|
|
|
const invitedRooms = MatrixClientPeg.get().getRooms().filter((r) => {
|
2016-12-14 15:01:50 +00:00
|
|
|
return r.hasMembershipState(this._me, "invite");
|
|
|
|
});
|
|
|
|
if (invitedRooms.length === 0) {
|
|
|
|
return null;
|
|
|
|
}
|
2016-12-14 16:00:50 +00:00
|
|
|
|
2017-05-02 20:17:12 +00:00
|
|
|
const Spinner = sdk.getComponent("elements.Spinner");
|
2016-12-14 16:00:50 +00:00
|
|
|
|
|
|
|
let reject = <Spinner />;
|
|
|
|
if (!this.state.rejectingInvites) {
|
2016-12-14 16:04:20 +00:00
|
|
|
// bind() the invited rooms so any new invites that may come in as this button is clicked
|
|
|
|
// don't inadvertently get rejected as well.
|
2016-12-14 16:00:50 +00:00
|
|
|
reject = (
|
2017-01-13 16:25:26 +00:00
|
|
|
<AccessibleButton className="mx_UserSettings_button danger"
|
2016-12-14 16:00:50 +00:00
|
|
|
onClick={this._onRejectAllInvitesClicked.bind(this, invitedRooms)}>
|
|
|
|
Reject all {invitedRooms.length} invites
|
2017-01-13 16:25:26 +00:00
|
|
|
</AccessibleButton>
|
2016-12-14 16:00:50 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2016-12-14 15:01:50 +00:00
|
|
|
return <div>
|
|
|
|
<h3>Bulk Options</h3>
|
|
|
|
<div className="mx_UserSettings_section">
|
2016-12-14 16:00:50 +00:00
|
|
|
{reject}
|
2016-12-14 15:01:50 +00:00
|
|
|
</div>
|
|
|
|
</div>;
|
|
|
|
},
|
|
|
|
|
2017-04-18 18:55:08 +00:00
|
|
|
_showSpoiler: function(event) {
|
|
|
|
const target = event.target;
|
2017-05-02 20:17:12 +00:00
|
|
|
target.innerHTML = target.getAttribute('data-spoiler');
|
2017-04-18 18:55:08 +00:00
|
|
|
|
|
|
|
const range = document.createRange();
|
|
|
|
range.selectNodeContents(target);
|
|
|
|
|
|
|
|
const selection = window.getSelection();
|
|
|
|
selection.removeAllRanges();
|
|
|
|
selection.addRange(range);
|
|
|
|
},
|
|
|
|
|
2016-12-21 18:56:50 +00:00
|
|
|
nameForMedium: function(medium) {
|
2017-05-02 20:17:12 +00:00
|
|
|
if (medium === 'msisdn') return 'Phone';
|
2016-12-21 18:56:50 +00:00
|
|
|
return medium[0].toUpperCase() + medium.slice(1);
|
|
|
|
},
|
|
|
|
|
2017-03-16 15:16:24 +00:00
|
|
|
presentableTextForThreepid: function(threepid) {
|
2017-05-02 20:17:12 +00:00
|
|
|
if (threepid.medium === 'msisdn') {
|
2017-03-16 15:16:24 +00:00
|
|
|
return '+' + threepid.address;
|
|
|
|
} else {
|
|
|
|
return threepid.address;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2015-11-30 15:52:41 +00:00
|
|
|
render: function() {
|
2017-05-02 20:17:12 +00:00
|
|
|
const Loader = sdk.getComponent("elements.Spinner");
|
2015-12-18 00:37:56 +00:00
|
|
|
switch (this.state.phase) {
|
2015-12-23 11:47:56 +00:00
|
|
|
case "UserSettings.LOADING":
|
2015-12-23 16:02:18 +00:00
|
|
|
return (
|
|
|
|
<Loader />
|
|
|
|
);
|
2015-12-23 11:47:56 +00:00
|
|
|
case "UserSettings.DISPLAY":
|
|
|
|
break; // quit the switch to return the common state
|
|
|
|
default:
|
|
|
|
throw new Error("Unknown state.phase => " + this.state.phase);
|
|
|
|
}
|
|
|
|
// can only get here if phase is UserSettings.DISPLAY
|
2017-05-02 20:17:12 +00:00
|
|
|
const SimpleRoomHeader = sdk.getComponent('rooms.SimpleRoomHeader');
|
|
|
|
const ChangeDisplayName = sdk.getComponent("views.settings.ChangeDisplayName");
|
|
|
|
const ChangePassword = sdk.getComponent("views.settings.ChangePassword");
|
|
|
|
const ChangeAvatar = sdk.getComponent('settings.ChangeAvatar');
|
|
|
|
const Notifications = sdk.getComponent("settings.Notifications");
|
|
|
|
const EditableText = sdk.getComponent('elements.EditableText');
|
|
|
|
|
|
|
|
const avatarUrl = (
|
2015-12-23 16:52:59 +00:00
|
|
|
this.state.avatarUrl ? MatrixClientPeg.get().mxcUrlToHttp(this.state.avatarUrl) : null
|
|
|
|
);
|
2015-12-23 15:38:28 +00:00
|
|
|
|
2017-05-02 20:17:12 +00:00
|
|
|
const threepidsSection = this.state.threepids.map((val, pidIndex) => {
|
2016-12-22 15:03:24 +00:00
|
|
|
const id = "3pid-" + val.address;
|
2016-01-19 16:36:54 +00:00
|
|
|
return (
|
|
|
|
<div className="mx_UserSettings_profileTableRow" key={pidIndex}>
|
|
|
|
<div className="mx_UserSettings_profileLabelCell">
|
2016-12-21 18:56:50 +00:00
|
|
|
<label htmlFor={id}>{this.nameForMedium(val.medium)}</label>
|
2016-01-19 16:36:54 +00:00
|
|
|
</div>
|
|
|
|
<div className="mx_UserSettings_profileInputCell">
|
2017-03-16 15:16:24 +00:00
|
|
|
<input type="text" key={val.address} id={id}
|
|
|
|
value={this.presentableTextForThreepid(val)} disabled
|
|
|
|
/>
|
2016-01-19 16:36:54 +00:00
|
|
|
</div>
|
2017-01-20 21:00:22 +00:00
|
|
|
<div className="mx_UserSettings_threepidButton mx_filterFlipColor">
|
|
|
|
<img src="img/cancel-small.svg" width="14" height="14" alt="Remove" onClick={this.onRemoveThreepidClicked.bind(this, val)} />
|
2016-12-21 18:49:38 +00:00
|
|
|
</div>
|
2016-01-19 16:36:54 +00:00
|
|
|
</div>
|
|
|
|
);
|
|
|
|
});
|
2017-03-16 14:56:26 +00:00
|
|
|
let addEmailSection;
|
2016-01-19 16:36:54 +00:00
|
|
|
if (this.state.email_add_pending) {
|
2017-03-16 14:56:26 +00:00
|
|
|
addEmailSection = <Loader key="_email_add_spinner" />;
|
2016-03-16 10:33:20 +00:00
|
|
|
} else if (!MatrixClientPeg.get().isGuest()) {
|
2017-03-16 14:56:26 +00:00
|
|
|
addEmailSection = (
|
|
|
|
<div className="mx_UserSettings_profileTableRow" key="_newEmail">
|
2016-01-19 16:36:54 +00:00
|
|
|
<div className="mx_UserSettings_profileLabelCell">
|
2017-05-15 02:03:17 +00:00
|
|
|
<label>Email</label>
|
2016-01-19 16:36:54 +00:00
|
|
|
</div>
|
2016-01-24 18:15:08 +00:00
|
|
|
<div className="mx_UserSettings_profileInputCell">
|
|
|
|
<EditableText
|
2017-03-16 14:56:26 +00:00
|
|
|
ref="add_email_input"
|
2016-01-24 18:15:08 +00:00
|
|
|
className="mx_UserSettings_editable"
|
|
|
|
placeholderClassName="mx_UserSettings_threepidPlaceholder"
|
|
|
|
placeholder={ "Add email address" }
|
|
|
|
blurToCancel={ false }
|
2017-03-16 14:56:26 +00:00
|
|
|
onValueChanged={ this._onAddEmailEditFinished } />
|
|
|
|
</div>
|
|
|
|
<div className="mx_UserSettings_threepidButton mx_filterFlipColor">
|
|
|
|
<img src="img/plus.svg" width="14" height="14" alt="Add" onClick={this._addEmail} />
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
2017-03-22 15:18:27 +00:00
|
|
|
const AddPhoneNumber = sdk.getComponent('views.settings.AddPhoneNumber');
|
|
|
|
const addMsisdnSection = (
|
|
|
|
<AddPhoneNumber key="_addMsisdn" onThreepidAdded={this._refreshFromServer} />
|
|
|
|
);
|
2017-03-16 14:56:26 +00:00
|
|
|
threepidsSection.push(addEmailSection);
|
|
|
|
threepidsSection.push(addMsisdnSection);
|
2016-01-19 16:36:54 +00:00
|
|
|
|
2017-05-02 20:17:12 +00:00
|
|
|
let accountJsx;
|
2016-01-07 17:23:32 +00:00
|
|
|
|
|
|
|
if (MatrixClientPeg.get().isGuest()) {
|
|
|
|
accountJsx = (
|
|
|
|
<div className="mx_UserSettings_button" onClick={this.onUpgradeClicked}>
|
2016-01-15 12:35:30 +00:00
|
|
|
Create an account
|
2016-01-07 17:23:32 +00:00
|
|
|
</div>
|
|
|
|
);
|
2017-05-02 20:17:12 +00:00
|
|
|
} else {
|
2016-01-07 17:23:32 +00:00
|
|
|
accountJsx = (
|
|
|
|
<ChangePassword
|
|
|
|
className="mx_UserSettings_accountTable"
|
|
|
|
rowClassName="mx_UserSettings_profileTableRow"
|
|
|
|
rowLabelClassName="mx_UserSettings_profileLabelCell"
|
|
|
|
rowInputClassName="mx_UserSettings_profileInputCell"
|
2016-01-20 17:09:46 +00:00
|
|
|
buttonClassName="mx_UserSettings_button mx_UserSettings_changePasswordButton"
|
2016-01-07 17:23:32 +00:00
|
|
|
onError={this.onPasswordChangeError}
|
|
|
|
onFinished={this.onPasswordChanged} />
|
|
|
|
);
|
|
|
|
}
|
2017-05-02 20:17:12 +00:00
|
|
|
let notificationArea;
|
2016-04-12 15:17:04 +00:00
|
|
|
if (!MatrixClientPeg.get().isGuest() && this.state.threepids !== undefined) {
|
2017-05-02 20:17:12 +00:00
|
|
|
notificationArea = (<div>
|
2016-04-15 17:30:13 +00:00
|
|
|
<h3>Notifications</h3>
|
2016-02-10 11:48:35 +00:00
|
|
|
|
|
|
|
<div className="mx_UserSettings_section">
|
2016-06-08 13:54:34 +00:00
|
|
|
<Notifications threepids={this.state.threepids} brand={this.props.brand} />
|
2016-02-10 11:48:35 +00:00
|
|
|
</div>
|
|
|
|
</div>);
|
|
|
|
}
|
2016-01-07 17:23:32 +00:00
|
|
|
|
2017-04-21 02:04:34 +00:00
|
|
|
const olmVersion = MatrixClientPeg.get().olmVersion;
|
2016-09-15 10:31:54 +00:00
|
|
|
// If the olmVersion is not defined then either crypto is disabled, or
|
|
|
|
// we are using a version old version of olm. We assume the former.
|
2017-04-21 02:04:34 +00:00
|
|
|
let olmVersionString = "<not-enabled>";
|
2016-09-15 10:31:54 +00:00
|
|
|
if (olmVersion !== undefined) {
|
2017-05-02 20:12:58 +00:00
|
|
|
olmVersionString = `${olmVersion[0]}.${olmVersion[1]}.${olmVersion[2]}`;
|
2016-09-15 10:31:54 +00:00
|
|
|
}
|
|
|
|
|
2015-12-23 11:47:56 +00:00
|
|
|
return (
|
|
|
|
<div className="mx_UserSettings">
|
2016-09-13 11:18:22 +00:00
|
|
|
<SimpleRoomHeader
|
|
|
|
title="Settings"
|
|
|
|
collapsedRhs={ this.props.collapsedRhs }
|
|
|
|
onCancelClick={ this.props.onClose }
|
|
|
|
/>
|
2015-12-23 11:47:56 +00:00
|
|
|
|
2016-04-20 11:25:19 +00:00
|
|
|
<GeminiScrollbar className="mx_UserSettings_body"
|
|
|
|
autoshow={true}>
|
2016-01-15 13:11:14 +00:00
|
|
|
|
2016-04-15 17:30:13 +00:00
|
|
|
<h3>Profile</h3>
|
2015-12-23 11:47:56 +00:00
|
|
|
|
|
|
|
<div className="mx_UserSettings_section">
|
|
|
|
<div className="mx_UserSettings_profileTable">
|
|
|
|
<div className="mx_UserSettings_profileTableRow">
|
|
|
|
<div className="mx_UserSettings_profileLabelCell">
|
|
|
|
<label htmlFor="displayName">Display name</label>
|
|
|
|
</div>
|
|
|
|
<div className="mx_UserSettings_profileInputCell">
|
2015-12-23 14:14:25 +00:00
|
|
|
<ChangeDisplayName />
|
2015-12-23 11:47:56 +00:00
|
|
|
</div>
|
|
|
|
</div>
|
2016-01-19 16:36:54 +00:00
|
|
|
{threepidsSection}
|
2015-12-23 15:38:28 +00:00
|
|
|
</div>
|
2015-11-30 15:52:41 +00:00
|
|
|
|
2015-12-23 11:47:56 +00:00
|
|
|
<div className="mx_UserSettings_avatarPicker">
|
2016-01-15 16:33:34 +00:00
|
|
|
<div onClick={ this.onAvatarPickerClick }>
|
|
|
|
<ChangeAvatar ref="changeAvatar" initialAvatarUrl={avatarUrl}
|
|
|
|
showUploadSection={false} className="mx_UserSettings_avatarPicker_img"/>
|
|
|
|
</div>
|
2015-12-23 16:52:59 +00:00
|
|
|
<div className="mx_UserSettings_avatarPicker_edit">
|
2016-01-15 12:35:30 +00:00
|
|
|
<label htmlFor="avatarInput" ref="file_label">
|
2017-01-20 21:00:22 +00:00
|
|
|
<img src="img/camera.svg" className="mx_filterFlipColor"
|
2015-12-23 16:52:59 +00:00
|
|
|
alt="Upload avatar" title="Upload avatar"
|
2016-01-15 12:35:30 +00:00
|
|
|
width="17" height="15" />
|
2015-12-23 16:52:59 +00:00
|
|
|
</label>
|
|
|
|
<input id="avatarInput" type="file" onChange={this.onAvatarSelected}/>
|
2015-11-30 15:52:41 +00:00
|
|
|
</div>
|
2015-12-23 11:47:56 +00:00
|
|
|
</div>
|
|
|
|
</div>
|
2015-12-18 00:37:56 +00:00
|
|
|
|
2016-04-15 17:30:13 +00:00
|
|
|
<h3>Account</h3>
|
2015-12-23 15:38:28 +00:00
|
|
|
|
|
|
|
<div className="mx_UserSettings_section">
|
2016-04-15 17:30:13 +00:00
|
|
|
|
2017-01-13 16:25:26 +00:00
|
|
|
<AccessibleButton className="mx_UserSettings_logout mx_UserSettings_button" onClick={this.onLogoutClicked}>
|
2016-09-29 16:38:52 +00:00
|
|
|
Sign out
|
2017-01-13 16:25:26 +00:00
|
|
|
</AccessibleButton>
|
2016-01-15 12:35:30 +00:00
|
|
|
|
|
|
|
{accountJsx}
|
2015-12-23 11:47:56 +00:00
|
|
|
</div>
|
2015-12-18 00:37:56 +00:00
|
|
|
|
2017-01-31 13:17:01 +00:00
|
|
|
{this._renderReferral()}
|
|
|
|
|
2017-05-02 20:17:12 +00:00
|
|
|
{notificationArea}
|
2015-12-18 00:37:56 +00:00
|
|
|
|
2016-07-18 00:35:42 +00:00
|
|
|
{this._renderUserInterfaceSettings()}
|
2016-06-13 16:34:12 +00:00
|
|
|
{this._renderLabs()}
|
2016-08-01 12:42:29 +00:00
|
|
|
{this._renderDevicesPanel()}
|
|
|
|
{this._renderCryptoInfo()}
|
2016-12-14 15:01:50 +00:00
|
|
|
{this._renderBulkOptions()}
|
2017-01-24 14:47:11 +00:00
|
|
|
{this._renderBugReport()}
|
2016-06-13 16:34:12 +00:00
|
|
|
|
2016-04-15 17:30:13 +00:00
|
|
|
<h3>Advanced</h3>
|
2015-12-23 11:47:56 +00:00
|
|
|
|
|
|
|
<div className="mx_UserSettings_section">
|
|
|
|
<div className="mx_UserSettings_advanced">
|
2015-12-23 16:02:18 +00:00
|
|
|
Logged in as {this._me}
|
2015-11-30 15:52:41 +00:00
|
|
|
</div>
|
2017-04-18 18:55:08 +00:00
|
|
|
<div className="mx_UserSettings_advanced">
|
2017-05-02 20:17:12 +00:00
|
|
|
Access Token: <span className="mx_UserSettings_advanced_spoiler"
|
|
|
|
onClick={this._showSpoiler}
|
|
|
|
data-spoiler={ MatrixClientPeg.get().getAccessToken() }
|
|
|
|
><click to reveal></span>
|
2017-04-18 18:55:08 +00:00
|
|
|
</div>
|
2016-05-18 10:42:51 +00:00
|
|
|
<div className="mx_UserSettings_advanced">
|
|
|
|
Homeserver is { MatrixClientPeg.get().getHomeserverUrl() }
|
|
|
|
</div>
|
|
|
|
<div className="mx_UserSettings_advanced">
|
|
|
|
Identity Server is { MatrixClientPeg.get().getIdentityServerUrl() }
|
|
|
|
</div>
|
2015-12-23 16:02:18 +00:00
|
|
|
<div className="mx_UserSettings_advanced">
|
2017-04-21 02:04:34 +00:00
|
|
|
matrix-react-sdk version: {(REACT_SDK_VERSION !== '<local>')
|
2017-05-02 20:12:58 +00:00
|
|
|
? gHVersionLabel('matrix-org/matrix-react-sdk', REACT_SDK_VERSION)
|
2017-04-21 02:04:34 +00:00
|
|
|
: REACT_SDK_VERSION
|
|
|
|
}<br/>
|
2017-05-13 14:04:20 +00:00
|
|
|
riot-web version: {(this.state.vectorVersion !== undefined)
|
2017-05-02 20:12:58 +00:00
|
|
|
? gHVersionLabel('vector-im/riot-web', this.state.vectorVersion)
|
2017-04-21 02:04:34 +00:00
|
|
|
: 'unknown'
|
|
|
|
}<br/>
|
2016-09-15 10:31:54 +00:00
|
|
|
olm version: {olmVersionString}<br/>
|
2015-12-23 11:47:56 +00:00
|
|
|
</div>
|
|
|
|
</div>
|
2016-01-15 13:11:14 +00:00
|
|
|
|
2017-02-17 15:16:28 +00:00
|
|
|
{this._renderClearCache()}
|
|
|
|
|
2016-08-02 17:40:12 +00:00
|
|
|
{this._renderDeactivateAccount()}
|
|
|
|
|
2016-01-15 13:11:14 +00:00
|
|
|
</GeminiScrollbar>
|
2015-12-23 11:47:56 +00:00
|
|
|
</div>
|
|
|
|
);
|
2017-05-02 20:17:12 +00:00
|
|
|
},
|
2015-11-30 15:52:41 +00:00
|
|
|
});
|