Merge branch 'develop' into new-guest-access

Conflicts:
	src/component-index.js
This commit is contained in:
Luke Barnard 2017-05-11 13:22:30 +01:00
commit 5151264f60
33 changed files with 718 additions and 568 deletions

View file

@ -395,9 +395,10 @@ module.exports = React.createClass({
this.notifyNewScreen('forgot_password');
break;
case 'leave_room':
const roomToLeave = MatrixClientPeg.get().getRoom(payload.room_id);
Modal.createDialog(QuestionDialog, {
title: "Leave room",
description: "Are you sure you want to leave the room?",
description: <span>Are you sure you want to leave the room <i>{roomToLeave.name}</i>?</span>,
onFinished: (should_leave) => {
if (should_leave) {
const d = MatrixClientPeg.get().leave(payload.room_id);

View file

@ -354,7 +354,6 @@ module.exports = React.createClass({
<MemberEventListSummary
key={key}
events={summarisedEvents}
data-scroll-token={eventId}
onToggle={this._onWidgetLoad} // Update scroll state
>
{eventTiles}
@ -473,7 +472,7 @@ module.exports = React.createClass({
ret.push(
<li key={eventId}
ref={this._collectEventNode.bind(this, eventId)}
data-scroll-token={scrollToken}>
data-scroll-tokens={scrollToken}>
<EventTile mxEvent={mxEv} continuation={continuation}
isRedacted={mxEv.isRedacted()}
onWidgetLoad={this._onWidgetLoad}

View file

@ -275,6 +275,7 @@ module.exports = React.createClass({
this._updateConfCallNotification();
window.addEventListener('beforeunload', this.onPageUnload);
window.addEventListener('resize', this.onResize);
this.onResize();
@ -357,6 +358,7 @@ module.exports = React.createClass({
MatrixClientPeg.get().removeListener("accountData", this.onAccountData);
}
window.removeEventListener('beforeunload', this.onPageUnload);
window.removeEventListener('resize', this.onResize);
document.removeEventListener("keydown", this.onKeyDown);
@ -369,6 +371,17 @@ module.exports = React.createClass({
// Tinter.tint(); // reset colourscheme
},
onPageUnload(event) {
if (ContentMessages.getCurrentUploads().length > 0) {
return event.returnValue =
'You seem to be uploading files, are you sure you want to quit?';
} else if (this._getCallForRoom() && this.state.callState !== 'ended') {
return event.returnValue =
'You seem to be in a call, are you sure you want to quit?';
}
},
onKeyDown: function(ev) {
let handled = false;
const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;

View file

@ -46,9 +46,13 @@ if (DEBUG_SCROLL) {
* It also provides a hook which allows parents to provide more list elements
* when we get close to the start or end of the list.
*
* Each child element should have a 'data-scroll-token'. This token is used to
* serialise the scroll state, and returned as the 'trackedScrollToken'
* attribute by getScrollState().
* Each child element should have a 'data-scroll-tokens'. This string of
* comma-separated tokens may contain a single token or many, where many indicates
* that the element contains elements that have scroll tokens themselves. The first
* token in 'data-scroll-tokens' is used to serialise the scroll state, and returned
* as the 'trackedScrollToken' attribute by getScrollState().
*
* IMPORTANT: INDIVIDUAL TOKENS WITHIN 'data-scroll-tokens' MUST NOT CONTAIN COMMAS.
*
* Some notes about the implementation:
*
@ -349,8 +353,8 @@ module.exports = React.createClass({
// Subtract height of tile as if it were unpaginated
excessHeight -= tile.clientHeight;
// The tile may not have a scroll token, so guard it
if (tile.dataset.scrollToken) {
markerScrollToken = tile.dataset.scrollToken;
if (tile.dataset.scrollTokens) {
markerScrollToken = tile.dataset.scrollTokens.split(',')[0];
}
if (tile.clientHeight > excessHeight) {
break;
@ -419,7 +423,8 @@ module.exports = React.createClass({
* scroll. false if we are tracking a particular child.
*
* string trackedScrollToken: undefined if stuckAtBottom is true; if it is
* false, the data-scroll-token of the child which we are tracking.
* false, the first token in data-scroll-tokens of the child which we are
* tracking.
*
* number pixelOffset: undefined if stuckAtBottom is true; if it is false,
* the number of pixels the bottom of the tracked child is above the
@ -551,8 +556,10 @@ module.exports = React.createClass({
var messages = this.refs.itemlist.children;
for (var i = messages.length-1; i >= 0; --i) {
var m = messages[i];
if (!m.dataset.scrollToken) continue;
if (m.dataset.scrollToken == scrollToken) {
// 'data-scroll-tokens' is a DOMString of comma-separated scroll tokens
// There might only be one scroll token
if (m.dataset.scrollTokens &&
m.dataset.scrollTokens.split(',').indexOf(scrollToken) !== -1) {
node = m;
break;
}
@ -568,7 +575,7 @@ module.exports = React.createClass({
var boundingRect = node.getBoundingClientRect();
var scrollDelta = boundingRect.bottom + pixelOffset - wrapperRect.bottom;
debuglog("ScrollPanel: scrolling to token '" + node.dataset.scrollToken + "'+" +
debuglog("ScrollPanel: scrolling to token '" + scrollToken + "'+" +
pixelOffset + " (delta: "+scrollDelta+")");
if(scrollDelta != 0) {
@ -591,12 +598,12 @@ module.exports = React.createClass({
for (var i = messages.length-1; i >= 0; --i) {
var node = messages[i];
if (!node.dataset.scrollToken) continue;
if (!node.dataset.scrollTokens) continue;
var boundingRect = node.getBoundingClientRect();
newScrollState = {
stuckAtBottom: false,
trackedScrollToken: node.dataset.scrollToken,
trackedScrollToken: node.dataset.scrollTokens.split(',')[0],
pixelOffset: wrapperRect.bottom - boundingRect.bottom,
};
// If the bottom of the panel intersects the ClientRect of node, use this node
@ -608,7 +615,7 @@ module.exports = React.createClass({
break;
}
}
// This is only false if there were no nodes with `node.dataset.scrollToken` set.
// This is only false if there were no nodes with `node.dataset.scrollTokens` set.
if (newScrollState) {
this.scrollState = newScrollState;
debuglog("ScrollPanel: saved scroll state", this.scrollState);

View file

@ -170,7 +170,7 @@ var TimelinePanel = React.createClass({
forwardPaginating: false,
// cache of matrixClient.getSyncState() (but from the 'sync' event)
clientSyncState: null,
clientSyncState: MatrixClientPeg.get().getSyncState(),
};
},
@ -503,7 +503,9 @@ var TimelinePanel = React.createClass({
// This happens on user_activity_end which is delayed, and it's
// very possible have logged out within that timeframe, so check
// we still have a client.
if (!MatrixClientPeg.get()) return;
const cli = MatrixClientPeg.get();
// if no client or client is guest don't send RR
if (!cli || cli.isGuest()) return;
var currentReadUpToEventId = this._getCurrentReadReceipt(true);
var currentReadUpToEventIndex = this._indexForEventId(currentReadUpToEventId);

View file

@ -14,31 +14,40 @@ 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.
*/
var React = require('react');
var ReactDOM = require('react-dom');
var sdk = require('../../index');
var MatrixClientPeg = require("../../MatrixClientPeg");
var PlatformPeg = require("../../PlatformPeg");
var Modal = require('../../Modal');
var dis = require("../../dispatcher");
var q = require('q');
var package_json = require('../../../package.json');
var UserSettingsStore = require('../../UserSettingsStore');
var GeminiScrollbar = require('react-gemini-scrollbar');
var Email = require('../../email');
var AddThreepid = require('../../AddThreepid');
var SdkConfig = require('../../SdkConfig');
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');
import AccessibleButton from '../views/elements/AccessibleButton';
// 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
const REACT_SDK_VERSION = 'dist' in package_json ? `v${package_json.version}` : package_json.gitHead || '<local>';
const REACT_SDK_VERSION = 'dist' in packageJson ? packageJson.version : packageJson.gitHead || '<local>';
// Simple method to help prettify GH Release Tags and Commit Hashes.
const GHVersionUrl = function(repo, token) {
const uriTail = (token.startsWith('v') && token.includes('.')) ? `releases/tag/${token}` : `commit/${token}`;
return `https://github.com/${repo}/${uriTail}`;
}
const semVerRegex = /^v?(\d+\.\d+\.\d+(?:-rc.+)?)(?:-(?:\d+-g)?([0-9a-fA-F]+))?(?:-dirty)?$/i;
const gHVersionLabel = function(repo, token) {
const match = token.match(semVerRegex);
let url;
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>;
};
// Enumerate some simple 'flip a bit' UI settings (if any).
// 'id' gives the key name in the im.vector.web.settings account data event
@ -50,7 +59,7 @@ const SETTINGS_LABELS = [
},
{
id: 'hideReadReceipts',
label: 'Hide read receipts'
label: 'Hide read receipts',
},
{
id: 'dontSendTypingNotifications',
@ -106,7 +115,7 @@ const THEMES = [
id: 'theme',
label: 'Dark theme',
value: 'dark',
}
},
];
@ -180,7 +189,7 @@ module.exports = React.createClass({
});
this._refreshFromServer();
var syncedSettings = UserSettingsStore.getSyncedSettings();
const syncedSettings = UserSettingsStore.getSyncedSettings();
if (!syncedSettings.theme) {
syncedSettings.theme = 'light';
}
@ -202,16 +211,16 @@ module.exports = React.createClass({
middleOpacity: 1.0,
});
dis.unregister(this.dispatcherRef);
let cli = MatrixClientPeg.get();
const cli = MatrixClientPeg.get();
if (cli) {
cli.removeListener("RoomMember.membership", this._onInviteStateChange);
}
},
_refreshFromServer: function() {
var self = this;
const self = this;
q.all([
UserSettingsStore.loadProfileInfo(), UserSettingsStore.loadThreePids()
UserSettingsStore.loadProfileInfo(), UserSettingsStore.loadThreePids(),
]).done(function(resps) {
self.setState({
avatarUrl: resps[0].avatar_url,
@ -219,7 +228,7 @@ module.exports = React.createClass({
phase: "UserSettings.DISPLAY",
});
}, function(error) {
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
console.error("Failed to load user settings: " + error);
Modal.createDialog(ErrorDialog, {
title: "Can't load user settings",
@ -236,7 +245,7 @@ module.exports = React.createClass({
onAvatarPickerClick: function(ev) {
if (MatrixClientPeg.get().isGuest()) {
var NeedToRegisterDialog = sdk.getComponent("dialogs.NeedToRegisterDialog");
const NeedToRegisterDialog = sdk.getComponent("dialogs.NeedToRegisterDialog");
Modal.createDialog(NeedToRegisterDialog, {
title: "Please Register",
description: "Guests can't set avatars. Please register.",
@ -250,8 +259,8 @@ module.exports = React.createClass({
},
onAvatarSelected: function(ev) {
var self = this;
var changeAvatar = this.refs.changeAvatar;
const self = this;
const changeAvatar = this.refs.changeAvatar;
if (!changeAvatar) {
console.error("No ChangeAvatar found to upload image to!");
return;
@ -260,9 +269,9 @@ module.exports = React.createClass({
// dunno if the avatar changed, re-check it.
self._refreshFromServer();
}, function(err) {
var errMsg = (typeof err === "string") ? err : (err.error || "");
// const errMsg = (typeof err === "string") ? err : (err.error || "");
console.error("Failed to set avatar: " + err);
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
title: "Failed to set avatar",
description: ((err && err.message) ? err.message : "Operation failed"),
@ -271,7 +280,7 @@ module.exports = React.createClass({
},
onLogoutClicked: function(ev) {
var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
Modal.createDialog(QuestionDialog, {
title: "Sign out?",
description:
@ -286,7 +295,7 @@ module.exports = React.createClass({
<button key="export" className="mx_Dialog_primary"
onClick={this._onExportE2eKeysClicked}>
Export E2E room keys
</button>
</button>,
],
onFinished: (confirmed) => {
if (confirmed) {
@ -300,34 +309,33 @@ module.exports = React.createClass({
},
onPasswordChangeError: function(err) {
var errMsg = err.error || "";
let errMsg = err.error || "";
if (err.httpStatus === 403) {
errMsg = "Failed to change password. Is your password correct?";
}
else if (err.httpStatus) {
} else if (err.httpStatus) {
errMsg += ` (HTTP status ${err.httpStatus})`;
}
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
console.error("Failed to change password: " + errMsg);
Modal.createDialog(ErrorDialog, {
title: "Error",
description: errMsg
description: errMsg,
});
},
onPasswordChanged: function() {
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createDialog(ErrorDialog, {
title: "Success",
description: `Your password was successfully changed. You will not
receive push notifications on other devices until you
log back in to them.`
log back in to them.`,
});
},
onUpgradeClicked: function() {
dis.dispatch({
action: "start_upgrade_registration"
action: "start_upgrade_registration",
});
},
@ -341,11 +349,11 @@ module.exports = React.createClass({
},
_addEmail: function() {
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
var email_address = this.refs.add_email_input.value;
if (!Email.looksValid(email_address)) {
const emailAddress = this.refs.add_email_input.value;
if (!Email.looksValid(emailAddress)) {
Modal.createDialog(ErrorDialog, {
title: "Invalid Email Address",
description: "This doesn't appear to be a valid email address",
@ -355,7 +363,7 @@ module.exports = React.createClass({
this._addThreepid = new AddThreepid();
// we always bind emails when registering, so let's do the
// same here.
this._addThreepid.addEmailAddress(email_address, true).done(() => {
this._addThreepid.addEmailAddress(emailAddress, true).done(() => {
Modal.createDialog(QuestionDialog, {
title: "Verification Pending",
description: "Please check your email and click on the link it contains. Once this is done, click continue.",
@ -364,7 +372,7 @@ module.exports = React.createClass({
});
}, (err) => {
this.setState({email_add_pending: false});
console.error("Unable to add email address " + email_address + " " + err);
console.error("Unable to add email address " + emailAddress + " " + err);
Modal.createDialog(ErrorDialog, {
title: "Unable to add email address",
description: ((err && err.message) ? err.message : "Operation failed"),
@ -418,9 +426,9 @@ module.exports = React.createClass({
this.setState({email_add_pending: false});
}, (err) => {
this.setState({email_add_pending: false});
if (err.errcode == 'M_THREEPID_AUTH_FAILED') {
var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
var message = "Unable to verify email address. ";
if (err.errcode === 'M_THREEPID_AUTH_FAILED') {
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
let message = "Unable to verify email address. ";
message += "Please check your email and click on the link it contains. Once this is done, click continue.";
Modal.createDialog(QuestionDialog, {
title: "Verification Pending",
@ -429,7 +437,7 @@ module.exports = React.createClass({
onFinished: this.onEmailDialogFinished,
});
} else {
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
console.error("Unable to verify email address: " + err);
Modal.createDialog(ErrorDialog, {
title: "Unable to verify email address",
@ -469,17 +477,17 @@ module.exports = React.createClass({
_onRejectAllInvitesClicked: function(rooms, ev) {
this.setState({
rejectingInvites: true
rejectingInvites: true,
});
// reject the invites
let promises = rooms.map((room) => {
const promises = rooms.map((room) => {
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({
rejectingInvites: false
rejectingInvites: false,
});
}).done();
},
@ -492,7 +500,7 @@ module.exports = React.createClass({
}, "e2e-export");
}, {
matrixClient: MatrixClientPeg.get(),
}
},
);
},
@ -504,7 +512,7 @@ module.exports = React.createClass({
}, "e2e-export");
}, {
matrixClient: MatrixClientPeg.get(),
}
},
);
},
@ -530,8 +538,6 @@ module.exports = React.createClass({
},
_renderUserInterfaceSettings: function() {
var client = MatrixClientPeg.get();
return (
<div>
<h3>User Interface</h3>
@ -549,7 +555,7 @@ module.exports = React.createClass({
<input id="urlPreviewsDisabled"
type="checkbox"
defaultChecked={ UserSettingsStore.getUrlPreviewsDisabled() }
onChange={ e => UserSettingsStore.setUrlPreviewsDisabled(e.target.checked) }
onChange={ (e) => UserSettingsStore.setUrlPreviewsDisabled(e.target.checked) }
/>
<label htmlFor="urlPreviewsDisabled">
Disable inline URL previews by default
@ -562,7 +568,7 @@ module.exports = React.createClass({
<input id={ setting.id }
type="checkbox"
defaultChecked={ this._syncedSettings[setting.id] }
onChange={ e => UserSettingsStore.setSyncedSetting(setting.id, e.target.checked) }
onChange={ (e) => UserSettingsStore.setSyncedSetting(setting.id, e.target.checked) }
/>
<label htmlFor={ setting.id }>
{ setting.label }
@ -577,7 +583,7 @@ module.exports = React.createClass({
name={ setting.id }
value={ setting.value }
defaultChecked={ this._syncedSettings[setting.id] === setting.value }
onChange={ e => {
onChange={ (e) => {
if (e.target.checked) {
UserSettingsStore.setSyncedSetting(setting.id, setting.value);
}
@ -639,8 +645,8 @@ module.exports = React.createClass({
type="checkbox"
defaultChecked={ this._localSettings[setting.id] }
onChange={
e => {
UserSettingsStore.setLocalSetting(setting.id, e.target.checked)
(e) => {
UserSettingsStore.setLocalSetting(setting.id, e.target.checked);
if (setting.id === 'blacklistUnverifiedDevices') { // XXX: this is a bit ugly
client.setGlobalBlacklistUnverifiedDevices(e.target.checked);
}
@ -654,7 +660,7 @@ module.exports = React.createClass({
},
_renderDevicesPanel: function() {
var DevicesPanel = sdk.getComponent('settings.DevicesPanel');
const DevicesPanel = sdk.getComponent('settings.DevicesPanel');
return (
<div>
<h3>Devices</h3>
@ -665,7 +671,7 @@ module.exports = React.createClass({
_renderBugReport: function() {
if (!SdkConfig.get().bug_report_endpoint_url) {
return <div />
return <div />;
}
return (
<div>
@ -684,17 +690,17 @@ module.exports = React.createClass({
// default to enabled if undefined
if (this.props.enableLabs === false) return null;
let features = UserSettingsStore.LABS_FEATURES.map(feature => (
const features = UserSettingsStore.LABS_FEATURES.map((feature) => (
<div key={feature.id} className="mx_UserSettings_toggle">
<input
type="checkbox"
id={feature.id}
name={feature.id}
defaultChecked={ UserSettingsStore.isFeatureEnabled(feature.id) }
onChange={e => {
onChange={(e) => {
if (MatrixClientPeg.get().isGuest()) {
e.target.checked = false;
var NeedToRegisterDialog = sdk.getComponent("dialogs.NeedToRegisterDialog");
const NeedToRegisterDialog = sdk.getComponent("dialogs.NeedToRegisterDialog");
Modal.createDialog(NeedToRegisterDialog, {
title: "Please Register",
description: "Guests can't use labs features. Please register.",
@ -746,14 +752,14 @@ module.exports = React.createClass({
},
_renderBulkOptions: function() {
let invitedRooms = MatrixClientPeg.get().getRooms().filter((r) => {
const invitedRooms = MatrixClientPeg.get().getRooms().filter((r) => {
return r.hasMembershipState(this._me, "invite");
});
if (invitedRooms.length === 0) {
return null;
}
let Spinner = sdk.getComponent("elements.Spinner");
const Spinner = sdk.getComponent("elements.Spinner");
let reject = <Spinner />;
if (!this.state.rejectingInvites) {
@ -777,9 +783,7 @@ module.exports = React.createClass({
_showSpoiler: function(event) {
const target = event.target;
const hidden = target.getAttribute('data-spoiler');
target.innerHTML = hidden;
target.innerHTML = target.getAttribute('data-spoiler');
const range = document.createRange();
range.selectNodeContents(target);
@ -790,12 +794,12 @@ module.exports = React.createClass({
},
nameForMedium: function(medium) {
if (medium == 'msisdn') return 'Phone';
if (medium === 'msisdn') return 'Phone';
return medium[0].toUpperCase() + medium.slice(1);
},
presentableTextForThreepid: function(threepid) {
if (threepid.medium == 'msisdn') {
if (threepid.medium === 'msisdn') {
return '+' + threepid.address;
} else {
return threepid.address;
@ -803,7 +807,7 @@ module.exports = React.createClass({
},
render: function() {
var Loader = sdk.getComponent("elements.Spinner");
const Loader = sdk.getComponent("elements.Spinner");
switch (this.state.phase) {
case "UserSettings.LOADING":
return (
@ -815,18 +819,18 @@ module.exports = React.createClass({
throw new Error("Unknown state.phase => " + this.state.phase);
}
// can only get here if phase is UserSettings.DISPLAY
var SimpleRoomHeader = sdk.getComponent('rooms.SimpleRoomHeader');
var ChangeDisplayName = sdk.getComponent("views.settings.ChangeDisplayName");
var ChangePassword = sdk.getComponent("views.settings.ChangePassword");
var ChangeAvatar = sdk.getComponent('settings.ChangeAvatar');
var Notifications = sdk.getComponent("settings.Notifications");
var EditableText = sdk.getComponent('elements.EditableText');
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');
var avatarUrl = (
const avatarUrl = (
this.state.avatarUrl ? MatrixClientPeg.get().mxcUrlToHttp(this.state.avatarUrl) : null
);
var threepidsSection = this.state.threepids.map((val, pidIndex) => {
const threepidsSection = this.state.threepids.map((val, pidIndex) => {
const id = "3pid-" + val.address;
return (
<div className="mx_UserSettings_profileTableRow" key={pidIndex}>
@ -874,7 +878,7 @@ module.exports = React.createClass({
threepidsSection.push(addEmailSection);
threepidsSection.push(addMsisdnSection);
var accountJsx;
let accountJsx;
if (MatrixClientPeg.get().isGuest()) {
accountJsx = (
@ -882,8 +886,7 @@ module.exports = React.createClass({
Create an account
</div>
);
}
else {
} else {
accountJsx = (
<ChangePassword
className="mx_UserSettings_accountTable"
@ -895,9 +898,9 @@ module.exports = React.createClass({
onFinished={this.onPasswordChanged} />
);
}
var notification_area;
let notificationArea;
if (!MatrixClientPeg.get().isGuest() && this.state.threepids !== undefined) {
notification_area = (<div>
notificationArea = (<div>
<h3>Notifications</h3>
<div className="mx_UserSettings_section">
@ -911,7 +914,7 @@ module.exports = React.createClass({
// we are using a version old version of olm. We assume the former.
let olmVersionString = "<not-enabled>";
if (olmVersion !== undefined) {
olmVersionString = `v${olmVersion[0]}.${olmVersion[1]}.${olmVersion[2]}`;
olmVersionString = `${olmVersion[0]}.${olmVersion[1]}.${olmVersion[2]}`;
}
return (
@ -969,7 +972,7 @@ module.exports = React.createClass({
{this._renderReferral()}
{notification_area}
{notificationArea}
{this._renderUserInterfaceSettings()}
{this._renderLabs()}
@ -985,7 +988,10 @@ module.exports = React.createClass({
Logged in as {this._me}
</div>
<div className="mx_UserSettings_advanced">
Access Token: <span className="mx_UserSettings_advanced_spoiler" onClick={this._showSpoiler} data-spoiler={ MatrixClientPeg.get().getAccessToken() }>&lt;click to reveal&gt;</span>
Access Token: <span className="mx_UserSettings_advanced_spoiler"
onClick={this._showSpoiler}
data-spoiler={ MatrixClientPeg.get().getAccessToken() }
>&lt;click to reveal&gt;</span>
</div>
<div className="mx_UserSettings_advanced">
Homeserver is { MatrixClientPeg.get().getHomeserverUrl() }
@ -995,11 +1001,11 @@ module.exports = React.createClass({
</div>
<div className="mx_UserSettings_advanced">
matrix-react-sdk version: {(REACT_SDK_VERSION !== '<local>')
? <a href={ GHVersionUrl('matrix-org/matrix-react-sdk', REACT_SDK_VERSION) }>{REACT_SDK_VERSION}</a>
? gHVersionLabel('matrix-org/matrix-react-sdk', REACT_SDK_VERSION)
: REACT_SDK_VERSION
}<br/>
riot-web version: {(this.state.vectorVersion !== null)
? <a href={ GHVersionUrl('vector-im/riot-web', this.state.vectorVersion.split('-')[0]) }>{this.state.vectorVersion}</a>
? gHVersionLabel('vector-im/riot-web', this.state.vectorVersion)
: 'unknown'
}<br/>
olm version: {olmVersionString}<br/>
@ -1013,5 +1019,5 @@ module.exports = React.createClass({
</GeminiScrollbar>
</div>
);
}
},
});

View file

@ -23,6 +23,9 @@ import url from 'url';
import sdk from '../../../index';
import Login from '../../../Login';
// For validating phone numbers without country codes
const PHONE_NUMBER_REGEX = /^[0-9\(\)\-\s]*$/;
/**
* A wire component which glues together login UI components and Login logic
*/
@ -125,7 +128,16 @@ module.exports = React.createClass({
},
onPhoneNumberChanged: function(phoneNumber) {
this.setState({ phoneNumber: phoneNumber });
// Validate the phone number entered
if (!PHONE_NUMBER_REGEX.test(phoneNumber)) {
this.setState({ errorText: 'The phone number entered looks invalid' });
return;
}
this.setState({
phoneNumber: phoneNumber,
errorText: null,
});
},
onServerConfigChange: function(config) {