- {_t('Autocomplete Delay (ms):')}
+ { _t('Autocomplete Delay (ms):') }
{ _t("Disable inline URL previews by default") }
@@ -712,13 +759,13 @@ module.exports = React.createClass({
if (setting.fn) setting.fn(e.target.checked);
};
- return
-
+
-
+
{ _t(setting.label) }
;
@@ -737,16 +784,16 @@ module.exports = React.createClass({
value: setting.value,
});
};
- return
-
+
-
- { setting.label }
+
+ { _t(setting.label) }
;
},
@@ -782,10 +829,10 @@ module.exports = React.createClass({
{ _t("Cryptography") }
- {_t("Device ID:")}
- {deviceId}
- {_t("Device key:")}
- {identityKey}
+ { _t("Device ID:") }
+ { deviceId }
+ { _t("Device key:") }
+ { identityKey }
{ importExportButtons }
@@ -796,6 +843,26 @@ module.exports = React.createClass({
);
},
+ _renderIgnoredUsers: function() {
+ if (this.state.ignoredUsers.length > 0) {
+ const updateHandler = this._refreshIgnoredUsers;
+ return (
+
+
{ _t("Ignored Users") }
+
+
+ { this.state.ignoredUsers.map(function(userId) {
+ return ( );
+ }) }
+
+
+
+ );
+ } else return (
);
+ },
+
_renderLocalSetting: function(setting) {
// TODO: this ought to be a separate component so that we don't need
// to rebind the onChange each time we render
@@ -804,13 +871,13 @@ module.exports = React.createClass({
if (setting.fn) setting.fn(e.target.checked);
};
- return
-
+
-
+
{ _t(setting.label) }
;
@@ -820,8 +887,8 @@ module.exports = React.createClass({
const DevicesPanel = sdk.getComponent('settings.DevicesPanel');
return (
-
{_t("Devices")}
-
+ { _t("Devices") }
+
);
},
@@ -836,7 +903,7 @@ module.exports = React.createClass({
{ _t("Found a bug?") }
{_t('Report it')}
+ onClick={this._onBugReportClicked}>{ _t('Report it') }
@@ -844,13 +911,13 @@ module.exports = React.createClass({
},
_renderAnalyticsControl: function() {
- if (!SdkConfig.get().piwik) return
;
+ if (!SdkConfig.get().piwik) return
;
return
{ _t('Analytics') }
- {_t('Riot collects anonymous analytics to allow us to improve the application.')}
- {ANALYTICS_SETTINGS_LABELS.map( this._renderLocalSetting )}
+ { _t('Riot collects anonymous analytics to allow us to improve the application.') }
+ { ANALYTICS_SETTINGS_LABELS.map( this._renderLocalSetting ) }
;
},
@@ -880,10 +947,10 @@ module.exports = React.createClass({
type="checkbox"
id={feature.id}
name={feature.id}
- defaultChecked={ UserSettingsStore.isFeatureEnabled(feature.id) }
- onChange={ onChange }
+ defaultChecked={UserSettingsStore.isFeatureEnabled(feature.id)}
+ onChange={onChange}
/>
- {feature.name}
+ { feature.name }
);
});
@@ -897,7 +964,7 @@ module.exports = React.createClass({
{ _t("Labs") }
{ _t("These are experimental features that may break in unexpected ways") }. { _t("Use with caution") }.
- {features}
+ { features }
);
@@ -930,10 +997,10 @@ module.exports = React.createClass({
const platform = PlatformPeg.get();
if ('canSelfUpdate' in platform && platform.canSelfUpdate() && 'startUpdateCheck' in platform) {
return
-
{_t('Updates')}
+
{ _t('Updates') }
- {_t('Check for update')}
+ { _t('Check for update') }
;
@@ -959,7 +1026,7 @@ module.exports = React.createClass({
reject = (
- {_t("Reject all %(invitedRooms)s invites", {invitedRooms: invitedRooms.length})}
+ { _t("Reject all %(invitedRooms)s invites", {invitedRooms: invitedRooms.length}) }
);
}
@@ -967,7 +1034,7 @@ module.exports = React.createClass({
return
{ _t("Bulk Options") }
- {reject}
+ { reject }
;
},
@@ -985,7 +1052,7 @@ module.exports = React.createClass({
defaultChecked={settings['auto-launch']}
onChange={this._onAutoLaunchChanged}
/>
- {_t('Start automatically after system login')}
+ { _t('Start automatically after system login') }
;
@@ -997,7 +1064,7 @@ module.exports = React.createClass({
},
_mapWebRtcDevicesToSpans: function(devices) {
- return devices.map((device) => {device.label} );
+ return devices.map((device) => { device.label } );
},
_setAudioInput: function(deviceId) {
@@ -1033,15 +1100,15 @@ module.exports = React.createClass({
if (this.state.mediaDevices === false) {
return (
- {_t('Missing Media Permissions, click here to request.')}
+ { _t('Missing Media Permissions, click here to request.') }
);
} else if (!this.state.mediaDevices) return;
const Dropdown = sdk.getComponent('elements.Dropdown');
- let microphoneDropdown = {_t('No Microphones detected')}
;
- let webcamDropdown = {_t('No Webcams detected')}
;
+ let microphoneDropdown = { _t('No Microphones detected') }
;
+ let webcamDropdown = { _t('No Webcams detected') }
;
const defaultOption = {
deviceId: '',
@@ -1058,12 +1125,12 @@ module.exports = React.createClass({
}
microphoneDropdown =
-
{_t('Microphone')}
+ { _t('Microphone') }
- {this._mapWebRtcDevicesToSpans(audioInputs)}
+ { this._mapWebRtcDevicesToSpans(audioInputs) }
;
}
@@ -1078,25 +1145,25 @@ module.exports = React.createClass({
}
webcamDropdown =
-
{_t('Camera')}
+ { _t('Camera') }
- {this._mapWebRtcDevicesToSpans(videoInputs)}
+ { this._mapWebRtcDevicesToSpans(videoInputs) }
;
}
return
- {microphoneDropdown}
- {webcamDropdown}
+ { microphoneDropdown }
+ { webcamDropdown }
;
},
_renderWebRtcSettings: function() {
return
-
{_t('VoIP')}
+
{ _t('VoIP') }
{ WEBRTC_SETTINGS_LABELS.map(this._renderLocalSetting) }
{ this._renderWebRtcDeviceSettings() }
@@ -1162,7 +1229,7 @@ module.exports = React.createClass({
return (
- {this.nameForMedium(val.medium)}
+ { this.nameForMedium(val.medium) }
-
@@ -1183,16 +1250,16 @@ module.exports = React.createClass({
addEmailSection = (
- {_t('Email')}
+ { _t('Email') }
+ placeholder={_t("Add email address")}
+ blurToCancel={false}
+ onValueChanged={this._onAddEmailEditFinished} />
@@ -1240,8 +1307,8 @@ module.exports = React.createClass({
return (
- {threepidsSection}
+ { threepidsSection }
-
+
+ showUploadSection={false} className="mx_UserSettings_avatarPicker_img" />
@@ -1290,36 +1357,37 @@ module.exports = React.createClass({
: null
}
- {accountJsx}
+ { accountJsx }
- {this._renderReferral()}
+ { this._renderReferral() }
- {notificationArea}
+ { notificationArea }
- {this._renderUserInterfaceSettings()}
- {this._renderLabs()}
- {this._renderWebRtcSettings()}
- {this._renderDevicesPanel()}
- {this._renderCryptoInfo()}
- {this._renderBulkOptions()}
- {this._renderBugReport()}
+ { this._renderUserInterfaceSettings() }
+ { this._renderLabs() }
+ { this._renderWebRtcSettings() }
+ { this._renderDevicesPanel() }
+ { this._renderCryptoInfo() }
+ { this._renderIgnoredUsers() }
+ { this._renderBulkOptions() }
+ { this._renderBugReport() }
- {PlatformPeg.get().isElectron() && this._renderElectronSettings()}
+ { PlatformPeg.get().isElectron() && this._renderElectronSettings() }
- {this._renderAnalyticsControl()}
+ { this._renderAnalyticsControl() }
{ _t("Advanced") }
- { _t("Logged in as:") } {this._me}
+ { _t("Logged in as:") } { this._me }
- {_t('Access Token:')}
+ { _t('Access Token:') }
+ data-spoiler={MatrixClientPeg.get().getAccessToken()}>
<{ _t("click to reveal") }>
@@ -1330,23 +1398,23 @@ module.exports = React.createClass({
{ _t("Identity Server is") } { MatrixClientPeg.get().getIdentityServerUrl() }
- {_t('matrix-react-sdk version:')} {(REACT_SDK_VERSION !== '')
+ { _t('matrix-react-sdk version:') } { (REACT_SDK_VERSION !== '')
? gHVersionLabel('matrix-org/matrix-react-sdk', REACT_SDK_VERSION)
: REACT_SDK_VERSION
- }
- {_t('riot-web version:')} {(this.state.vectorVersion !== undefined)
+ }
+ { _t('riot-web version:') } { (this.state.vectorVersion !== undefined)
? gHVersionLabel('vector-im/riot-web', this.state.vectorVersion)
: 'unknown'
- }
- { _t("olm version:") } {olmVersionString}
+ }
+ { _t("olm version:") } { olmVersionString }
- {this._renderCheckUpdate()}
+ { this._renderCheckUpdate() }
- {this._renderClearCache()}
+ { this._renderClearCache() }
- {this._renderDeactivateAccount()}
+ { this._renderDeactivateAccount() }
diff --git a/src/components/structures/login/ForgotPassword.js b/src/components/structures/login/ForgotPassword.js
index 1b259a08fd..3e76291d20 100644
--- a/src/components/structures/login/ForgotPassword.js
+++ b/src/components/structures/login/ForgotPassword.js
@@ -17,13 +17,13 @@ limitations under the License.
'use strict';
-var React = require('react');
+const React = require('react');
import { _t } from '../../../languageHandler';
-var sdk = require('../../../index');
-var Modal = require("../../../Modal");
-var MatrixClientPeg = require('../../../MatrixClientPeg');
+const sdk = require('../../../index');
+const Modal = require("../../../Modal");
+const MatrixClientPeg = require('../../../MatrixClientPeg');
-var PasswordReset = require("../../../PasswordReset");
+const PasswordReset = require("../../../PasswordReset");
module.exports = React.createClass({
displayName: 'ForgotPassword',
@@ -35,30 +35,30 @@ module.exports = React.createClass({
customIsUrl: React.PropTypes.string,
onLoginClick: React.PropTypes.func,
onRegisterClick: React.PropTypes.func,
- onComplete: React.PropTypes.func.isRequired
+ onComplete: React.PropTypes.func.isRequired,
},
getInitialState: function() {
return {
enteredHomeserverUrl: this.props.customHsUrl || this.props.defaultHsUrl,
enteredIdentityServerUrl: this.props.customIsUrl || this.props.defaultIsUrl,
- progress: null
+ progress: null,
};
},
submitPasswordReset: function(hsUrl, identityUrl, email, password) {
this.setState({
- progress: "sending_email"
+ progress: "sending_email",
});
this.reset = new PasswordReset(hsUrl, identityUrl);
this.reset.resetPassword(email, password).done(() => {
this.setState({
- progress: "sent_email"
+ progress: "sent_email",
});
}, (err) => {
this.showErrorDialog(_t('Failed to send email') + ": " + err.message);
this.setState({
- progress: null
+ progress: null,
});
});
},
@@ -81,15 +81,12 @@ module.exports = React.createClass({
if (!this.state.email) {
this.showErrorDialog(_t('The email address linked to your account must be entered.'));
- }
- else if (!this.state.password || !this.state.password2) {
+ } else if (!this.state.password || !this.state.password2) {
this.showErrorDialog(_t('A new password must be entered.'));
- }
- else if (this.state.password !== this.state.password2) {
+ } else if (this.state.password !== this.state.password2) {
this.showErrorDialog(_t('New passwords must match each other.'));
- }
- else {
- var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
+ } else {
+ const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
Modal.createTrackedDialog('Forgot Password Warning', '', QuestionDialog, {
title: _t('Warning!'),
description:
@@ -99,7 +96,7 @@ module.exports = React.createClass({
'end-to-end encryption keys on all devices, ' +
'making encrypted chat history unreadable, ' +
'unless you first export your room keys and re-import ' +
- 'them afterwards. In future this will be improved.'
+ 'them afterwards. In future this will be improved.',
) }
,
button: _t('Continue'),
@@ -107,13 +104,13 @@ module.exports = React.createClass({
{ _t('Export E2E room keys') }
-
+ ,
],
onFinished: (confirmed) => {
if (confirmed) {
this.submitPasswordReset(
this.state.enteredHomeserverUrl, this.state.enteredIdentityServerUrl,
- this.state.email, this.state.password
+ this.state.email, this.state.password,
);
}
},
@@ -133,7 +130,7 @@ module.exports = React.createClass({
onInputChanged: function(stateKey, ev) {
this.setState({
- [stateKey]: ev.target.value
+ [stateKey]: ev.target.value,
});
},
@@ -149,7 +146,7 @@ module.exports = React.createClass({
},
showErrorDialog: function(body, title) {
- var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
+ const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog('Forgot Password Error', '', ErrorDialog, {
title: title,
description: body,
@@ -157,37 +154,34 @@ module.exports = React.createClass({
},
render: function() {
- var LoginHeader = sdk.getComponent("login.LoginHeader");
- var LoginFooter = sdk.getComponent("login.LoginFooter");
- var ServerConfig = sdk.getComponent("login.ServerConfig");
- var Spinner = sdk.getComponent("elements.Spinner");
+ const LoginHeader = sdk.getComponent("login.LoginHeader");
+ const LoginFooter = sdk.getComponent("login.LoginFooter");
+ const ServerConfig = sdk.getComponent("login.ServerConfig");
+ const Spinner = sdk.getComponent("elements.Spinner");
- var resetPasswordJsx;
+ let resetPasswordJsx;
if (this.state.progress === "sending_email") {
resetPasswordJsx = ;
- }
- else if (this.state.progress === "sent_email") {
+ } else if (this.state.progress === "sent_email") {
resetPasswordJsx = (
- { _t('An email has been sent to') } {this.state.email}. { _t("Once you've followed the link it contains, click below") }.
+ { _t('An email has been sent to') } { this.state.email }. { _t("Once you've followed the link it contains, click below") }.
+ value={_t('I have verified my email address')} />
);
- }
- else if (this.state.progress === "complete") {
+ } else if (this.state.progress === "complete") {
resetPasswordJsx = (
{ _t('Your password has been reset') }.
{ _t('You have been logged out of all devices and will no longer receive push notifications. To re-enable notifications, sign in again on each device') }.
+ value={_t('Return to login screen')} />
);
- }
- else {
+ } else {
resetPasswordJsx = (
@@ -199,21 +193,21 @@ module.exports = React.createClass({
name="reset_email" // define a name so browser's password autofill gets less confused
value={this.state.email}
onChange={this.onInputChanged.bind(this, "email")}
- placeholder={ _t('Email address') } autoFocus />
+ placeholder={_t('Email address')} autoFocus />
+ placeholder={_t('New password')} />
+ placeholder={_t('Confirm your new password')} />
-
+
+ delayTimeMs={0} />
- {_t('Return to login screen')}
+ { _t('Return to login screen') }
{ _t('Create an account') }
@@ -242,9 +236,9 @@ module.exports = React.createClass({
- {resetPasswordJsx}
+ { resetPasswordJsx }
);
- }
+ },
});
diff --git a/src/components/structures/login/Login.js b/src/components/structures/login/Login.js
index a6c0a70c66..8ee6eafad4 100644
--- a/src/components/structures/login/Login.js
+++ b/src/components/structures/login/Login.js
@@ -134,7 +134,7 @@ module.exports = React.createClass({
},
_onLoginAsGuestClick: function() {
- var self = this;
+ const self = this;
self.setState({
busy: true,
errorText: null,
@@ -156,7 +156,7 @@ module.exports = React.createClass({
});
}).finally(function() {
self.setState({
- busy: false
+ busy: false,
});
}).done();
},
@@ -183,8 +183,8 @@ module.exports = React.createClass({
},
onServerConfigChange: function(config) {
- var self = this;
- let newState = {
+ const self = this;
+ const newState = {
errorText: null, // reset err messages
};
if (config.hsUrl !== undefined) {
@@ -199,13 +199,13 @@ module.exports = React.createClass({
},
_initLoginLogic: function(hsUrl, isUrl) {
- var self = this;
+ const self = this;
hsUrl = hsUrl || this.state.enteredHomeserverUrl;
isUrl = isUrl || this.state.enteredIdentityServerUrl;
- var fallbackHsUrl = hsUrl == this.props.defaultHsUrl ? this.props.fallbackHsUrl : null;
+ const fallbackHsUrl = hsUrl == this.props.defaultHsUrl ? this.props.fallbackHsUrl : null;
- var loginLogic = new Login(hsUrl, isUrl, fallbackHsUrl, {
+ const loginLogic = new Login(hsUrl, isUrl, fallbackHsUrl, {
defaultDeviceDisplayName: this.props.defaultDeviceDisplayName,
});
this._loginLogic = loginLogic;
@@ -259,15 +259,15 @@ module.exports = React.createClass({
{ _tJsx("Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. " +
"Either use HTTPS or enable unsafe scripts .",
/(.*?)<\/a>/,
- (sub) => { return { sub } ; }
- )}
+ (sub) => { return { sub } ; },
+ ) }
;
} else {
errorText =
{ _tJsx("Can't connect to homeserver - please check your connectivity, ensure your homeserver's SSL certificate is trusted, and that a browser extension is not blocking requests.",
/(.*?)<\/a>/,
- (sub) => { return { sub } ; }
- )}
+ (sub) => { return { sub } ; },
+ ) }
;
}
}
@@ -290,6 +290,7 @@ module.exports = React.createClass({
onPhoneNumberChanged={this.onPhoneNumberChanged}
onForgotPasswordClick={this.props.onForgotPasswordClick}
loginIncorrect={this.state.loginIncorrect}
+ hsUrl={this.state.enteredHomeserverUrl}
/>
);
case 'm.login.cas':
@@ -303,7 +304,7 @@ module.exports = React.createClass({
}
return (
- { _t('Sorry, this homeserver is using a login which is not recognised ')}({step})
+ { _t('Sorry, this homeserver is using a login which is not recognised ') }({ step })
);
}
@@ -333,19 +334,19 @@ module.exports = React.createClass({
const ServerConfig = sdk.getComponent("login.ServerConfig");
const loader = this.state.busy ?
: null;
- var loginAsGuestJsx;
+ let loginAsGuestJsx;
if (this.props.enableGuest) {
loginAsGuestJsx =
- { _t('Login as guest')}
+ { _t('Login as guest') }
;
}
- var returnToAppJsx;
+ let returnToAppJsx;
if (this.props.onCancelClick) {
returnToAppJsx =
- { _t('Return to app')}
+ { _t('Return to app') }
;
}
@@ -354,7 +355,7 @@ module.exports = React.createClass({
-
{ _t('Sign in')}
+ { _t('Sign in') }
{ loader }
{ this.componentForStep(this.state.currentFlow) }
@@ -365,12 +366,12 @@ module.exports = React.createClass({
defaultHsUrl={this.props.defaultHsUrl}
defaultIsUrl={this.props.defaultIsUrl}
onServerConfigChange={this.onServerConfigChange}
- delayTimeMs={1000}/>
+ delayTimeMs={1000} />
{ this.state.errorText }
- { _t('Create an account')}
+ { _t('Create an account') }
{ loginAsGuestJsx }
{ returnToAppJsx }
@@ -380,5 +381,5 @@ module.exports = React.createClass({
);
- }
+ },
});
diff --git a/src/components/structures/login/PostRegistration.js b/src/components/structures/login/PostRegistration.js
index c27eee98d4..194995150c 100644
--- a/src/components/structures/login/PostRegistration.js
+++ b/src/components/structures/login/PostRegistration.js
@@ -25,14 +25,14 @@ module.exports = React.createClass({
displayName: 'PostRegistration',
propTypes: {
- onComplete: React.PropTypes.func.isRequired
+ onComplete: React.PropTypes.func.isRequired,
},
getInitialState: function() {
return {
avatarUrl: null,
errorString: null,
- busy: false
+ busy: false,
};
},
@@ -40,26 +40,26 @@ module.exports = React.createClass({
// There is some assymetry between ChangeDisplayName and ChangeAvatar,
// as ChangeDisplayName will auto-get the name but ChangeAvatar expects
// the URL to be passed to you (because it's also used for room avatars).
- var cli = MatrixClientPeg.get();
+ const cli = MatrixClientPeg.get();
this.setState({busy: true});
- var self = this;
+ const self = this;
cli.getProfileInfo(cli.credentials.userId).done(function(result) {
self.setState({
avatarUrl: MatrixClientPeg.get().mxcUrlToHttp(result.avatar_url),
- busy: false
+ busy: false,
});
}, function(error) {
self.setState({
errorString: _t("Failed to fetch avatar URL"),
- busy: false
+ busy: false,
});
});
},
render: function() {
- var ChangeDisplayName = sdk.getComponent('settings.ChangeDisplayName');
- var ChangeAvatar = sdk.getComponent('settings.ChangeAvatar');
- var LoginHeader = sdk.getComponent('login.LoginHeader');
+ const ChangeDisplayName = sdk.getComponent('settings.ChangeDisplayName');
+ const ChangeAvatar = sdk.getComponent('settings.ChangeAvatar');
+ const LoginHeader = sdk.getComponent('login.LoginHeader');
return (
@@ -71,10 +71,10 @@ module.exports = React.createClass({
{ _t('Continue') }
- {this.state.errorString}
+ { this.state.errorString }
);
- }
+ },
});
diff --git a/src/components/structures/login/Registration.js b/src/components/structures/login/Registration.js
index fe05ba4cfd..db488ea237 100644
--- a/src/components/structures/login/Registration.js
+++ b/src/components/structures/login/Registration.js
@@ -57,7 +57,7 @@ module.exports = React.createClass({
// registration shouldn't know or care how login is done.
onLoginClick: React.PropTypes.func.isRequired,
- onCancelClick: React.PropTypes.func
+ onCancelClick: React.PropTypes.func,
},
getInitialState: function() {
@@ -121,7 +121,7 @@ module.exports = React.createClass({
},
onServerConfigChange: function(config) {
- let newState = {};
+ const newState = {};
if (config.hsUrl !== undefined) {
newState.hsUrl = config.hsUrl;
}
@@ -195,7 +195,7 @@ module.exports = React.createClass({
this._rtsClient.getTeam(teamToken).then((team) => {
console.log(
- `User successfully registered with team ${team.name}`
+ `User successfully registered with team ${team.name}`,
);
if (!team.rooms) {
return;
@@ -223,7 +223,7 @@ module.exports = React.createClass({
deviceId: response.device_id,
homeserverUrl: this._matrixClient.getHomeserverUrl(),
identityServerUrl: this._matrixClient.getIdentityServerUrl(),
- accessToken: response.access_token
+ accessToken: response.access_token,
}, teamToken);
}).then((cli) => {
return this._setupPushers(cli);
@@ -253,7 +253,7 @@ module.exports = React.createClass({
},
onFormValidationFailed: function(errCode) {
- var errMsg;
+ let errMsg;
switch (errCode) {
case "RegistrationForm.ERR_PASSWORD_MISSING":
errMsg = _t('Missing password.');
@@ -282,7 +282,7 @@ module.exports = React.createClass({
break;
}
this.setState({
- errorText: errMsg
+ errorText: errMsg,
});
},
@@ -316,7 +316,7 @@ module.exports = React.createClass({
emailAddress: this.state.formVals.email,
phoneCountry: this.state.formVals.phoneCountry,
phoneNumber: this.state.formVals.phoneNumber,
- }
+ };
},
render: function() {
@@ -346,7 +346,7 @@ module.exports = React.createClass({
} else {
let errorSection;
if (this.state.errorText) {
- errorSection =
{this.state.errorText}
;
+ errorSection =
{ this.state.errorText }
;
}
registerBody = (
@@ -362,7 +362,7 @@ module.exports = React.createClass({
onRegisterClick={this.onFormSubmit}
onTeamSelected={this.onTeamSelected}
/>
- {errorSection}
+ { errorSection }
- {_t('Return to app')}
+ { _t('Return to app') }
);
}
@@ -393,15 +393,15 @@ module.exports = React.createClass({
this.state.teamSelected.domain + "/icon.png" :
null}
/>
- {_t('Create an account')}
- {registerBody}
+ { _t('Create an account') }
+ { registerBody }
- {_t('I already have an account')}
+ { _t('I already have an account') }
- {returnToAppJsx}
+ { returnToAppJsx }
);
- }
+ },
});
diff --git a/src/components/views/avatars/BaseAvatar.js b/src/components/views/avatars/BaseAvatar.js
index a4443430f4..addb637796 100644
--- a/src/components/views/avatars/BaseAvatar.js
+++ b/src/components/views/avatars/BaseAvatar.js
@@ -14,10 +14,8 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-'use strict';
-
-var React = require('react');
-var AvatarLogic = require("../../../Avatar");
+import React from 'react';
+import AvatarLogic from '../../../Avatar';
import sdk from '../../../index';
import AccessibleButton from '../elements/AccessibleButton';
@@ -34,7 +32,7 @@ module.exports = React.createClass({
height: React.PropTypes.number,
// XXX resizeMethod not actually used.
resizeMethod: React.PropTypes.string,
- defaultToInitialLetter: React.PropTypes.bool // true to add default url
+ defaultToInitialLetter: React.PropTypes.bool, // true to add default url
},
getDefaultProps: function() {
@@ -42,7 +40,7 @@ module.exports = React.createClass({
width: 40,
height: 40,
resizeMethod: 'crop',
- defaultToInitialLetter: true
+ defaultToInitialLetter: true,
};
},
@@ -52,15 +50,14 @@ module.exports = React.createClass({
componentWillReceiveProps: function(nextProps) {
// work out if we need to call setState (if the image URLs array has changed)
- var newState = this._getState(nextProps);
- var newImageUrls = newState.imageUrls;
- var oldImageUrls = this.state.imageUrls;
+ const newState = this._getState(nextProps);
+ const newImageUrls = newState.imageUrls;
+ const oldImageUrls = this.state.imageUrls;
if (newImageUrls.length !== oldImageUrls.length) {
this.setState(newState); // detected a new entry
- }
- else {
+ } else {
// check each one to see if they are the same
- for (var i = 0; i < newImageUrls.length; i++) {
+ for (let i = 0; i < newImageUrls.length; i++) {
if (oldImageUrls[i] !== newImageUrls[i]) {
this.setState(newState); // detected a diff
break;
@@ -73,31 +70,31 @@ module.exports = React.createClass({
// work out the full set of urls to try to load. This is formed like so:
// imageUrls: [ props.url, props.urls, default image ]
- var urls = props.urls || [];
+ const urls = props.urls || [];
if (props.url) {
urls.unshift(props.url); // put in urls[0]
}
- var defaultImageUrl = null;
+ let defaultImageUrl = null;
if (props.defaultToInitialLetter) {
defaultImageUrl = AvatarLogic.defaultAvatarUrlForString(
- props.idName || props.name
+ props.idName || props.name,
);
urls.push(defaultImageUrl); // lowest priority
}
return {
imageUrls: urls,
defaultImageUrl: defaultImageUrl,
- urlsIndex: 0
+ urlsIndex: 0,
};
},
onError: function(ev) {
- var nextIndex = this.state.urlsIndex + 1;
+ const nextIndex = this.state.urlsIndex + 1;
if (nextIndex < this.state.imageUrls.length) {
// try the next one
this.setState({
- urlsIndex: nextIndex
+ urlsIndex: nextIndex,
});
}
},
@@ -111,32 +108,32 @@ module.exports = React.createClass({
return undefined;
}
- var idx = 0;
- var initial = name[0];
+ let idx = 0;
+ const initial = name[0];
if ((initial === '@' || initial === '#') && name[1]) {
idx++;
}
// string.codePointAt(0) would do this, but that isn't supported by
// some browsers (notably PhantomJS).
- var chars = 1;
- var first = name.charCodeAt(idx);
+ let chars = 1;
+ const first = name.charCodeAt(idx);
// check if it’s the start of a surrogate pair
if (first >= 0xD800 && first <= 0xDBFF && name[idx+1]) {
- var second = name.charCodeAt(idx+1);
+ const second = name.charCodeAt(idx+1);
if (second >= 0xDC00 && second <= 0xDFFF) {
chars++;
}
}
- var firstChar = name.substring(idx, idx+chars);
+ const firstChar = name.substring(idx, idx+chars);
return firstChar.toUpperCase();
},
render: function() {
const EmojiText = sdk.getComponent('elements.EmojiText');
- var imageUrl = this.state.imageUrls[this.state.urlsIndex];
+ const imageUrl = this.state.imageUrls[this.state.urlsIndex];
const {
name, idName, title, url, urls, width, height, resizeMethod,
@@ -152,7 +149,7 @@ module.exports = React.createClass({
width: width + "px",
lineHeight: height + "px" }}
>
- {initialLetter}
+ { initialLetter }
);
const imgNode = (
@@ -165,15 +162,15 @@ module.exports = React.createClass({
- {textNode}
- {imgNode}
+ { textNode }
+ { imgNode }
);
} else {
return (
- {textNode}
- {imgNode}
+ { textNode }
+ { imgNode }
);
}
@@ -198,5 +195,5 @@ module.exports = React.createClass({
{...otherProps} />
);
}
- }
+ },
});
diff --git a/src/components/views/avatars/MemberAvatar.js b/src/components/views/avatars/MemberAvatar.js
index 9fb522a5f1..89047cd69c 100644
--- a/src/components/views/avatars/MemberAvatar.js
+++ b/src/components/views/avatars/MemberAvatar.js
@@ -16,9 +16,9 @@ limitations under the License.
'use strict';
-var React = require('react');
-var Avatar = require('../../../Avatar');
-var sdk = require("../../../index");
+const React = require('react');
+const Avatar = require('../../../Avatar');
+const sdk = require("../../../index");
const dispatcher = require("../../../dispatcher");
module.exports = React.createClass({
@@ -63,14 +63,14 @@ module.exports = React.createClass({
imageUrl: Avatar.avatarUrlForMember(props.member,
props.width,
props.height,
- props.resizeMethod)
+ props.resizeMethod),
};
},
render: function() {
- var BaseAvatar = sdk.getComponent("avatars.BaseAvatar");
+ const BaseAvatar = sdk.getComponent("avatars.BaseAvatar");
- var {member, onClick, viewUserOnClick, ...otherProps} = this.props;
+ let {member, onClick, viewUserOnClick, ...otherProps} = this.props;
if (viewUserOnClick) {
onClick = () => {
@@ -83,7 +83,7 @@ module.exports = React.createClass({
return (
+ idName={member.userId} url={this.state.imageUrl} onClick={onClick} />
);
- }
+ },
});
diff --git a/src/components/views/avatars/RoomAvatar.js b/src/components/views/avatars/RoomAvatar.js
index a18a52b3c0..11554b2379 100644
--- a/src/components/views/avatars/RoomAvatar.js
+++ b/src/components/views/avatars/RoomAvatar.js
@@ -13,11 +13,10 @@ 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 ContentRepo = require("matrix-js-sdk").ContentRepo;
-var MatrixClientPeg = require('../../../MatrixClientPeg');
-var Avatar = require('../../../Avatar');
-var sdk = require("../../../index");
+import React from "react";
+import {ContentRepo} from "matrix-js-sdk";
+import MatrixClientPeg from "../../../MatrixClientPeg";
+import sdk from "../../../index";
module.exports = React.createClass({
displayName: 'RoomAvatar',
@@ -30,7 +29,7 @@ module.exports = React.createClass({
oobData: React.PropTypes.object,
width: React.PropTypes.number,
height: React.PropTypes.number,
- resizeMethod: React.PropTypes.string
+ resizeMethod: React.PropTypes.string,
},
getDefaultProps: function() {
@@ -44,13 +43,13 @@ module.exports = React.createClass({
getInitialState: function() {
return {
- urls: this.getImageUrls(this.props)
+ urls: this.getImageUrls(this.props),
};
},
componentWillReceiveProps: function(newProps) {
this.setState({
- urls: this.getImageUrls(newProps)
+ urls: this.getImageUrls(newProps),
});
},
@@ -61,11 +60,10 @@ module.exports = React.createClass({
props.oobData.avatarUrl,
Math.floor(props.width * window.devicePixelRatio),
Math.floor(props.height * window.devicePixelRatio),
- props.resizeMethod
+ props.resizeMethod,
), // highest priority
this.getRoomAvatarUrl(props),
- this.getOneToOneAvatar(props),
- this.getFallbackAvatar(props) // lowest priority
+ this.getOneToOneAvatar(props), // lowest priority
].filter(function(url) {
return (url != null && url != "");
});
@@ -79,17 +77,17 @@ module.exports = React.createClass({
Math.floor(props.width * window.devicePixelRatio),
Math.floor(props.height * window.devicePixelRatio),
props.resizeMethod,
- false
+ false,
);
},
getOneToOneAvatar: function(props) {
if (!props.room) return null;
- var mlist = props.room.currentState.members;
- var userIds = [];
+ const mlist = props.room.currentState.members;
+ const userIds = [];
// for .. in optimisation to return early if there are >2 keys
- for (var uid in mlist) {
+ for (const uid in mlist) {
if (mlist.hasOwnProperty(uid)) {
userIds.push(uid);
}
@@ -99,7 +97,7 @@ module.exports = React.createClass({
}
if (userIds.length == 2) {
- var theOtherGuy = null;
+ let theOtherGuy = null;
if (mlist[userIds[0]].userId == MatrixClientPeg.get().credentials.userId) {
theOtherGuy = mlist[userIds[1]];
} else {
@@ -110,7 +108,7 @@ module.exports = React.createClass({
Math.floor(props.width * window.devicePixelRatio),
Math.floor(props.height * window.devicePixelRatio),
props.resizeMethod,
- false
+ false,
);
} else if (userIds.length == 1) {
return mlist[userIds[0]].getAvatarUrl(
@@ -118,37 +116,24 @@ module.exports = React.createClass({
Math.floor(props.width * window.devicePixelRatio),
Math.floor(props.height * window.devicePixelRatio),
props.resizeMethod,
- false
+ false,
);
} else {
return null;
}
},
- getFallbackAvatar: function(props) {
- let roomId = null;
- if (props.oobData && props.oobData.roomId) {
- roomId = this.props.oobData.roomId;
- } else if (props.room) {
- roomId = props.room.roomId;
- } else {
- return null;
- }
-
- return Avatar.defaultAvatarUrlForString(roomId);
- },
-
render: function() {
- var BaseAvatar = sdk.getComponent("avatars.BaseAvatar");
+ const BaseAvatar = sdk.getComponent("avatars.BaseAvatar");
- var {room, oobData, ...otherProps} = this.props;
+ const {room, oobData, ...otherProps} = this.props;
- var roomName = room ? room.name : oobData.name;
+ const roomName = room ? room.name : oobData.name;
return (
);
- }
+ },
});
diff --git a/src/components/views/create_room/CreateRoomButton.js b/src/components/views/create_room/CreateRoomButton.js
index 8f0368d690..8a5f00d942 100644
--- a/src/components/views/create_room/CreateRoomButton.js
+++ b/src/components/views/create_room/CreateRoomButton.js
@@ -36,7 +36,7 @@ module.exports = React.createClass({
render: function() {
return (
- {_t("Create Room")}
+ { _t("Create Room") }
);
- }
+ },
});
diff --git a/src/components/views/create_room/Presets.js b/src/components/views/create_room/Presets.js
index c4d7ca0cdb..2073896d87 100644
--- a/src/components/views/create_room/Presets.js
+++ b/src/components/views/create_room/Presets.js
@@ -16,10 +16,10 @@ limitations under the License.
'use strict';
-var React = require('react');
+const React = require('react');
import { _t } from '../../../languageHandler';
-var Presets = {
+const Presets = {
PrivateChat: "private_chat",
PublicChat: "public_chat",
Custom: "custom",
@@ -29,7 +29,7 @@ module.exports = React.createClass({
displayName: 'CreateRoomPresets',
propTypes: {
onChange: React.PropTypes.func,
- preset: React.PropTypes.string
+ preset: React.PropTypes.string,
},
Presets: Presets,
@@ -47,10 +47,10 @@ module.exports = React.createClass({
render: function() {
return (
- {_t("Private Chat")}
- {_t("Public Chat")}
- {_t("Custom")}
+ { _t("Private Chat") }
+ { _t("Public Chat") }
+ { _t("Custom") }
);
- }
+ },
});
diff --git a/src/components/views/create_room/RoomAlias.js b/src/components/views/create_room/RoomAlias.js
index e1cb7e4094..d4228a8bca 100644
--- a/src/components/views/create_room/RoomAlias.js
+++ b/src/components/views/create_room/RoomAlias.js
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-var React = require('react');
+const React = require('react');
import { _t } from '../../../languageHandler';
module.exports = React.createClass({
@@ -35,10 +35,10 @@ module.exports = React.createClass({
},
getAliasLocalpart: function() {
- var room_alias = this.props.alias;
+ let room_alias = this.props.alias;
if (room_alias && this.props.homeserver) {
- var suffix = ":" + this.props.homeserver;
+ const suffix = ":" + this.props.homeserver;
if (room_alias.startsWith("#") && room_alias.endsWith(suffix)) {
room_alias = room_alias.slice(1, -suffix.length);
}
@@ -52,22 +52,22 @@ module.exports = React.createClass({
},
onFocus: function(ev) {
- var target = ev.target;
- var curr_val = ev.target.value;
+ const target = ev.target;
+ const curr_val = ev.target.value;
if (this.props.homeserver) {
if (curr_val == "") {
- var self = this;
+ const self = this;
setTimeout(function() {
target.value = "#:" + self.props.homeserver;
target.setSelectionRange(1, 1);
}, 0);
} else {
- var suffix = ":" + this.props.homeserver;
+ const suffix = ":" + this.props.homeserver;
setTimeout(function() {
target.setSelectionRange(
curr_val.startsWith("#") ? 1 : 0,
- curr_val.endsWith(suffix) ? (target.value.length - suffix.length) : target.value.length
+ curr_val.endsWith(suffix) ? (target.value.length - suffix.length) : target.value.length,
);
}, 0);
}
@@ -75,7 +75,7 @@ module.exports = React.createClass({
},
onBlur: function(ev) {
- var curr_val = ev.target.value;
+ const curr_val = ev.target.value;
if (this.props.homeserver) {
if (curr_val == "#:" + this.props.homeserver) {
@@ -84,8 +84,8 @@ module.exports = React.createClass({
}
if (curr_val != "") {
- var new_val = ev.target.value;
- var suffix = ":" + this.props.homeserver;
+ let new_val = ev.target.value;
+ const suffix = ":" + this.props.homeserver;
if (!curr_val.startsWith("#")) new_val = "#" + new_val;
if (!curr_val.endsWith(suffix)) new_val = new_val + suffix;
ev.target.value = new_val;
@@ -97,7 +97,7 @@ module.exports = React.createClass({
return (
+ value={this.props.alias} />
);
- }
+ },
});
diff --git a/src/components/views/dialogs/UserPickerDialog.js b/src/components/views/dialogs/AddressPickerDialog.js
similarity index 67%
rename from src/components/views/dialogs/UserPickerDialog.js
rename to src/components/views/dialogs/AddressPickerDialog.js
index b2fdd7035d..2637f9d466 100644
--- a/src/components/views/dialogs/UserPickerDialog.js
+++ b/src/components/views/dialogs/AddressPickerDialog.js
@@ -23,12 +23,13 @@ import MatrixClientPeg from '../../../MatrixClientPeg';
import AccessibleButton from '../elements/AccessibleButton';
import Promise from 'bluebird';
import { addressTypes, getAddressType } from '../../../UserAddress.js';
+import GroupStoreCache from '../../../stores/GroupStoreCache';
const TRUNCATE_QUERY_LIST = 40;
const QUERY_USER_DIRECTORY_DEBOUNCE_MS = 200;
module.exports = React.createClass({
- displayName: "UserPickerDialog",
+ displayName: "AddressPickerDialog",
propTypes: {
title: PropTypes.string.isRequired,
@@ -40,6 +41,12 @@ module.exports = React.createClass({
focus: PropTypes.bool,
validAddressTypes: PropTypes.arrayOf(PropTypes.oneOf(addressTypes)),
onFinished: PropTypes.func.isRequired,
+ groupId: PropTypes.string,
+ // The type of entity to search for. Default: 'user'.
+ pickerType: PropTypes.oneOf(['user', 'room']),
+ // Whether the current user should be included in the addresses returned. Only
+ // applicable when pickerType is `user`. Default: false.
+ includeSelf: PropTypes.bool,
},
getDefaultProps: function() {
@@ -47,6 +54,8 @@ module.exports = React.createClass({
value: "",
focus: true,
validAddressTypes: addressTypes,
+ pickerType: 'user',
+ includeSelf: false,
};
},
@@ -140,10 +149,22 @@ module.exports = React.createClass({
// Only do search if there is something to search
if (query.length > 0 && query != '@' && query.length >= 2) {
this.queryChangedDebouncer = setTimeout(() => {
- if (this.state.serverSupportsUserDirectory) {
- this._doUserDirectorySearch(query);
+ if (this.props.pickerType === 'user') {
+ if (this.props.groupId) {
+ this._doNaiveGroupSearch(query);
+ } else if (this.state.serverSupportsUserDirectory) {
+ this._doUserDirectorySearch(query);
+ } else {
+ this._doLocalSearch(query);
+ }
+ } else if (this.props.pickerType === 'room') {
+ if (this.props.groupId) {
+ this._doNaiveGroupRoomSearch(query);
+ } else {
+ this._doRoomSearch(query);
+ }
} else {
- this._doLocalSearch(query);
+ console.error('Unknown pickerType', this.props.pickerType);
}
}, QUERY_USER_DIRECTORY_DEBOUNCE_MS);
} else {
@@ -185,6 +206,94 @@ module.exports = React.createClass({
if (this._cancelThreepidLookup) this._cancelThreepidLookup();
},
+ _doNaiveGroupSearch: function(query) {
+ const lowerCaseQuery = query.toLowerCase();
+ this.setState({
+ busy: true,
+ query,
+ searchError: null,
+ });
+ MatrixClientPeg.get().getGroupUsers(this.props.groupId).then((resp) => {
+ const results = [];
+ resp.chunk.forEach((u) => {
+ const userIdMatch = u.user_id.toLowerCase().includes(lowerCaseQuery);
+ const displayNameMatch = (u.displayname || '').toLowerCase().includes(lowerCaseQuery);
+ if (!(userIdMatch || displayNameMatch)) {
+ return;
+ }
+ results.push({
+ user_id: u.user_id,
+ avatar_url: u.avatar_url,
+ display_name: u.displayname,
+ });
+ });
+ this._processResults(results, query);
+ }).catch((err) => {
+ console.error('Error whilst searching group rooms: ', err);
+ this.setState({
+ searchError: err.errcode ? err.message : _t('Something went wrong!'),
+ });
+ }).done(() => {
+ this.setState({
+ busy: false,
+ });
+ });
+ },
+
+ _doNaiveGroupRoomSearch: function(query) {
+ const lowerCaseQuery = query.toLowerCase();
+ const groupStore = GroupStoreCache.getGroupStore(MatrixClientPeg.get(), this.props.groupId);
+ const results = [];
+ groupStore.getGroupRooms().forEach((r) => {
+ const nameMatch = (r.name || '').toLowerCase().includes(lowerCaseQuery);
+ const topicMatch = (r.topic || '').toLowerCase().includes(lowerCaseQuery);
+ const aliasMatch = (r.canonical_alias || '').toLowerCase().includes(lowerCaseQuery);
+ if (!(nameMatch || topicMatch || aliasMatch)) {
+ return;
+ }
+ results.push({
+ room_id: r.room_id,
+ avatar_url: r.avatar_url,
+ name: r.name || r.canonical_alias,
+ });
+ });
+ this._processResults(results, query);
+ this.setState({
+ busy: false,
+ });
+ },
+
+ _doRoomSearch: function(query) {
+ const lowerCaseQuery = query.toLowerCase();
+ const rooms = MatrixClientPeg.get().getRooms();
+ const results = [];
+ rooms.forEach((room) => {
+ const nameEvent = room.currentState.getStateEvents('m.room.name', '');
+ const topicEvent = room.currentState.getStateEvents('m.room.topic', '');
+ const name = nameEvent ? nameEvent.getContent().name : '';
+ const canonicalAlias = room.getCanonicalAlias();
+ const topic = topicEvent ? topicEvent.getContent().topic : '';
+
+ const nameMatch = (name || '').toLowerCase().includes(lowerCaseQuery);
+ const aliasMatch = (canonicalAlias || '').toLowerCase().includes(lowerCaseQuery);
+ const topicMatch = (topic || '').toLowerCase().includes(lowerCaseQuery);
+ if (!(nameMatch || topicMatch || aliasMatch)) {
+ return;
+ }
+ const avatarEvent = room.currentState.getStateEvents('m.room.avatar', '');
+ const avatarUrl = avatarEvent ? avatarEvent.getContent().url : undefined;
+ results.push({
+ room_id: room.roomId,
+ avatar_url: avatarUrl,
+ name: name || canonicalAlias,
+ });
+ });
+ this._processResults(results, query);
+ this.setState({
+ busy: false,
+ });
+ },
+
_doUserDirectorySearch: function(query) {
this.setState({
busy: true,
@@ -245,17 +354,30 @@ module.exports = React.createClass({
_processResults: function(results, query) {
const queryList = [];
- results.forEach((user) => {
- if (user.user_id === MatrixClientPeg.get().credentials.userId) {
+ results.forEach((result) => {
+ if (result.room_id) {
+ queryList.push({
+ addressType: 'mx-room-id',
+ address: result.room_id,
+ displayName: result.name,
+ avatarMxc: result.avatar_url,
+ isKnown: true,
+ });
return;
}
+ if (!this.props.includeSelf &&
+ result.user_id === MatrixClientPeg.get().credentials.userId
+ ) {
+ return;
+ }
+
// Return objects, structure of which is defined
// by UserAddressType
queryList.push({
- addressType: 'mx',
- address: user.user_id,
- displayName: user.display_name,
- avatarMxc: user.avatar_url,
+ addressType: 'mx-user-id',
+ address: result.user_id,
+ displayName: result.display_name,
+ avatarMxc: result.avatar_url,
isKnown: true,
});
});
@@ -291,16 +413,23 @@ module.exports = React.createClass({
address: addressText,
isKnown: false,
};
- if (addrType == null) {
+ if (!this.props.validAddressTypes.includes(addrType)) {
this.setState({ error: true });
return null;
- } else if (addrType == 'mx') {
+ } else if (addrType == 'mx-user-id') {
const user = MatrixClientPeg.get().getUser(addrObj.address);
if (user) {
addrObj.displayName = user.displayName;
addrObj.avatarMxc = user.avatarUrl;
addrObj.isKnown = true;
}
+ } else if (addrType == 'mx-room-id') {
+ const room = MatrixClientPeg.get().getRoom(addrObj.address);
+ if (room) {
+ addrObj.displayName = room.name;
+ addrObj.avatarMxc = room.avatarUrl;
+ addrObj.isKnown = true;
+ }
}
const userList = this.state.userList.slice();
@@ -360,7 +489,7 @@ module.exports = React.createClass({
const AddressTile = sdk.getComponent("elements.AddressTile");
for (let i = 0; i < this.state.userList.length; i++) {
query.push(
- ,
+ ,
);
}
}
@@ -382,23 +511,36 @@ module.exports = React.createClass({
let error;
let addressSelector;
if (this.state.error) {
+ let tryUsing = '';
+ const validTypeDescriptions = this.props.validAddressTypes.map((t) => {
+ return {
+ 'mx-user-id': _t("Matrix ID"),
+ 'mx-room-id': _t("Matrix Room ID"),
+ 'email': _t("email address"),
+ }[t];
+ });
+ tryUsing = _t("Try using one of the following valid address types: %(validTypesList)s.", {
+ validTypesList: validTypeDescriptions.join(", "),
+ });
error =
- {_t("You have entered an invalid contact. Try using their Matrix ID or email address.")}
+ { _t("You have entered an invalid address.") }
+
+ { tryUsing }
;
} else if (this.state.searchError) {
- error = {this.state.searchError}
;
+ error = { this.state.searchError }
;
} else if (
this.state.query.length > 0 &&
this.state.queryList.length === 0 &&
!this.state.busy
) {
- error = {_t("No results")}
;
+ error = { _t("No results") }
;
} else {
addressSelector = (
{this.addressSelector = ref;}}
- addressList={ this.state.queryList }
- onSelected={ this.onSelected }
- truncateAt={ TRUNCATE_QUERY_LIST }
+ addressList={this.state.queryList}
+ onSelected={this.onSelected}
+ truncateAt={TRUNCATE_QUERY_LIST}
/>
);
}
@@ -406,7 +548,7 @@ module.exports = React.createClass({
return (
- {this.props.title}
+ { this.props.title }
@@ -422,7 +564,7 @@ module.exports = React.createClass({
- {this.props.button}
+ { this.props.button }
diff --git a/src/components/views/dialogs/ChatCreateOrReuseDialog.js b/src/components/views/dialogs/ChatCreateOrReuseDialog.js
index c3b0915ab4..e0578f3b53 100644
--- a/src/components/views/dialogs/ChatCreateOrReuseDialog.js
+++ b/src/components/views/dialogs/ChatCreateOrReuseDialog.js
@@ -155,7 +155,7 @@ export default class ChatCreateOrReuseDialog extends React.Component {
width={48} height={48}
/>
- {this.state.profile.displayName || this.props.userId}
+ { this.state.profile.displayName || this.props.userId }
;
}
@@ -177,7 +177,7 @@ export default class ChatCreateOrReuseDialog extends React.Component {
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
return (
{ content }
diff --git a/src/components/views/dialogs/ConfirmRedactDialog.js b/src/components/views/dialogs/ConfirmRedactDialog.js
index 7922b7b289..7b5a9f776b 100644
--- a/src/components/views/dialogs/ConfirmRedactDialog.js
+++ b/src/components/views/dialogs/ConfirmRedactDialog.js
@@ -52,20 +52,20 @@ export default React.createClass({
return (
- {_t("Are you sure you wish to remove (delete) this event? " +
- "Note that if you delete a room name or topic change, it could undo the change.")}
+ { _t("Are you sure you wish to remove (delete) this event? " +
+ "Note that if you delete a room name or topic change, it could undo the change.") }
- {_t("Remove")}
+ { _t("Remove") }
- {_t("Cancel")}
+ { _t("Cancel") }
diff --git a/src/components/views/dialogs/ConfirmUserActionDialog.js b/src/components/views/dialogs/ConfirmUserActionDialog.js
index b10df3ccef..9091d8975e 100644
--- a/src/components/views/dialogs/ConfirmUserActionDialog.js
+++ b/src/components/views/dialogs/ConfirmUserActionDialog.js
@@ -18,6 +18,7 @@ import React from 'react';
import sdk from '../../../index';
import { _t } from '../../../languageHandler';
import classnames from 'classnames';
+import { GroupMemberType } from '../../../groups';
/*
* A dialog for confirming an operation on another user.
@@ -30,7 +31,10 @@ import classnames from 'classnames';
export default React.createClass({
displayName: 'ConfirmUserActionDialog',
propTypes: {
- member: React.PropTypes.object.isRequired, // matrix-js-sdk member object
+ // matrix-js-sdk (room) member object. Supply either this or 'groupMember'
+ member: React.PropTypes.object,
+ // group member object. Supply either this or 'member'
+ groupMember: GroupMemberType,
action: React.PropTypes.string.isRequired, // eg. 'Ban'
// Whether to display a text field for a reason
@@ -69,6 +73,7 @@ export default React.createClass({
render: function() {
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
const MemberAvatar = sdk.getComponent("views.avatars.MemberAvatar");
+ const BaseAvatar = sdk.getComponent("views.avatars.BaseAvatar");
const title = _t("%(actionVerb)s this person?", { actionVerb: this.props.action});
const confirmButtonClass = classnames({
@@ -83,7 +88,7 @@ export default React.createClass({
@@ -91,24 +96,38 @@ export default React.createClass({
);
}
+ let avatar;
+ let name;
+ let userId;
+ if (this.props.member) {
+ avatar = ;
+ name = this.props.member.name;
+ userId = this.props.member.userId;
+ } else {
+ // we don't get this info from the API yet
+ avatar = ;
+ name = this.props.groupMember.userId;
+ userId = this.props.groupMember.userId;
+ }
+
return (
-
+ { avatar }
-
{this.props.member.name}
-
{this.props.member.userId}
+
{ name }
+
{ userId }
- {reasonBox}
+ { reasonBox }
- {this.props.action}
+ { this.props.action }
diff --git a/src/components/views/dialogs/CreateGroupDialog.js b/src/components/views/dialogs/CreateGroupDialog.js
index 23194f20a5..07ebb13aad 100644
--- a/src/components/views/dialogs/CreateGroupDialog.js
+++ b/src/components/views/dialogs/CreateGroupDialog.js
@@ -142,8 +142,8 @@ export default React.createClass({
// rather than displaying what the server gives us, but synapse doesn't give
// any yet.
createErrorNode =
-
{_t('Room creation failed')}
-
{this.state.createError.message}
+
{ _t('Room creation failed') }
+
{ this.state.createError.message }
;
}
@@ -156,7 +156,7 @@ export default React.createClass({
- {_t('Group Name')}
+ { _t('Group Name') }
- {_t('Group ID')}
+ { _t('Group ID') }
- {this.state.groupIdError}
+ { this.state.groupIdError }
- {createErrorNode}
+ { createErrorNode }
diff --git a/src/components/views/dialogs/CreateRoomDialog.js b/src/components/views/dialogs/CreateRoomDialog.js
new file mode 100644
index 0000000000..f7be47b3eb
--- /dev/null
+++ b/src/components/views/dialogs/CreateRoomDialog.js
@@ -0,0 +1,81 @@
+/*
+Copyright 2017 Michael Telatynski <7t3chguy@gmail.com>
+
+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 sdk from '../../../index';
+import SdkConfig from '../../../SdkConfig';
+import { _t } from '../../../languageHandler';
+
+export default React.createClass({
+ displayName: 'CreateRoomDialog',
+ propTypes: {
+ onFinished: React.PropTypes.func.isRequired,
+ },
+
+ componentDidMount: function() {
+ const config = SdkConfig.get();
+ // Dialog shows inverse of m.federate (noFederate) strict false check to skip undefined check (default = true)
+ this.defaultNoFederate = config.default_federate === false;
+ },
+
+ onOk: function() {
+ this.props.onFinished(true, this.refs.textinput.value, this.refs.checkbox.checked);
+ },
+
+ onCancel: function() {
+ this.props.onFinished(false);
+ },
+
+ render: function() {
+ const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
+ return (
+
+
+
+ { _t('Room name (optional)') }
+
+
+
+
+
+
+
+ { _t('Advanced options') }
+
+
+
+ { _t('Block users on other matrix homeservers from joining this room') }
+
+ ({ _t('This setting cannot be changed later!') })
+
+
+
+
+
+
+ { _t('Cancel') }
+
+
+ { _t('Create Room') }
+
+
+
+ );
+ },
+});
diff --git a/src/components/views/dialogs/DeactivateAccountDialog.js b/src/components/views/dialogs/DeactivateAccountDialog.js
index 0ee264b69b..c45e072d72 100644
--- a/src/components/views/dialogs/DeactivateAccountDialog.js
+++ b/src/components/views/dialogs/DeactivateAccountDialog.js
@@ -83,7 +83,7 @@ export default class DeactivateAccountDialog extends React.Component {
let error = null;
if (this.state.errStr) {
error =
- {this.state.errStr}
+ { this.state.errStr }
;
passwordBoxClass = 'error';
}
@@ -94,30 +94,30 @@ export default class DeactivateAccountDialog extends React.Component {
let cancelButton = null;
if (!this.state.busy) {
cancelButton =
- {_t("Cancel")}
+ { _t("Cancel") }
;
}
return (
- {_t("Deactivate Account")}
+ { _t("Deactivate Account") }
-
{_t("This will make your account permanently unusable. You will not be able to re-register the same user ID.")}
+
{ _t("This will make your account permanently unusable. You will not be able to re-register the same user ID.") }
-
{_t("This action is irreversible.")}
+
{ _t("This action is irreversible.") }
-
{_t("To continue, please enter your password.")}
+
{ _t("To continue, please enter your password.") }
-
{_t("Password")}:
+
{ _t("Password") }:
{this._passwordField = e;}}
className={passwordBoxClass}
/>
- {error}
+ { error }
- {okLabel}
+ { okLabel }
- {cancelButton}
+ { cancelButton }
);
diff --git a/src/components/views/dialogs/DeviceVerifyDialog.js b/src/components/views/dialogs/DeviceVerifyDialog.js
index 613fe540a4..ba31d2a8c2 100644
--- a/src/components/views/dialogs/DeviceVerifyDialog.js
+++ b/src/components/views/dialogs/DeviceVerifyDialog.js
@@ -28,25 +28,25 @@ export default function DeviceVerifyDialog(props) {
const body = (
- {_t("To verify that this device can be trusted, please contact its " +
+ { _t("To verify that this device can be trusted, please contact its " +
"owner using some other means (e.g. in person or a phone call) " +
"and ask them whether the key they see in their User Settings " +
- "for this device matches the key below:")}
+ "for this device matches the key below:") }
- {_t("Device name")}: { props.device.getDisplayName() }
- {_t("Device ID")}: { props.device.deviceId}
- {_t("Device key")}: { key }
+ { _t("Device name") }: { props.device.getDisplayName() }
+ { _t("Device ID") }: { props.device.deviceId }
+ { _t("Device key") }: { key }
- {_t("If it matches, press the verify button below. " +
+ { _t("If it matches, press the verify button below. " +
"If it doesn't, then someone else is intercepting this device " +
- "and you probably want to press the blacklist button instead.")}
+ "and you probably want to press the blacklist button instead.") }
- {_t("In future this verification process will be more sophisticated.")}
+ { _t("In future this verification process will be more sophisticated.") }
);
diff --git a/src/components/views/dialogs/ErrorDialog.js b/src/components/views/dialogs/ErrorDialog.js
index beca107252..97ed47e10f 100644
--- a/src/components/views/dialogs/ErrorDialog.js
+++ b/src/components/views/dialogs/ErrorDialog.js
@@ -63,11 +63,11 @@ export default React.createClass({
- {this.props.description || _t('An error has occurred.')}
+ { this.props.description || _t('An error has occurred.') }
- {this.props.button || _t('OK')}
+ { this.props.button || _t('OK') }
diff --git a/src/components/views/dialogs/InteractiveAuthDialog.js b/src/components/views/dialogs/InteractiveAuthDialog.js
index 363ce89b57..59de7c7f59 100644
--- a/src/components/views/dialogs/InteractiveAuthDialog.js
+++ b/src/components/views/dialogs/InteractiveAuthDialog.js
@@ -48,7 +48,7 @@ export default React.createClass({
getInitialState: function() {
return {
authError: null,
- }
+ };
},
_onAuthFinished: function(success, result) {
@@ -73,12 +73,12 @@ export default React.createClass({
if (this.state.authError) {
content = (
-
{this.state.authError.message || this.state.authError.toString()}
+
{ this.state.authError.message || this.state.authError.toString() }
- {_t("Dismiss")}
+ { _t("Dismiss") }
);
@@ -100,7 +100,7 @@ export default React.createClass({
onFinished={this.props.onFinished}
title={this.state.authError ? 'Error' : (this.props.title || _t('Authentication'))}
>
- {content}
+ { content }
);
},
diff --git a/src/components/views/dialogs/KeyShareDialog.js b/src/components/views/dialogs/KeyShareDialog.js
index aed8e6a5af..b0dc0a304e 100644
--- a/src/components/views/dialogs/KeyShareDialog.js
+++ b/src/components/views/dialogs/KeyShareDialog.js
@@ -18,7 +18,7 @@ import Modal from '../../../Modal';
import React from 'react';
import sdk from '../../../index';
-import { _t } from '../../../languageHandler';
+import { _t, _td } from '../../../languageHandler';
/**
* Dialog which asks the user whether they want to share their keys with
@@ -116,27 +116,27 @@ export default React.createClass({
let text;
if (this.state.wasNewDevice) {
- text = "You added a new device '%(displayName)s', which is"
- + " requesting encryption keys.";
+ text = _td("You added a new device '%(displayName)s', which is"
+ + " requesting encryption keys.");
} else {
- text = "Your unverified device '%(displayName)s' is requesting"
- + " encryption keys.";
+ text = _td("Your unverified device '%(displayName)s' is requesting"
+ + " encryption keys.");
}
text = _t(text, {displayName: displayName});
return (
-
{text}
+
{ text }
- {_t('Start verification')}
+ { _t('Start verification') }
- {_t('Share without verifying')}
+ { _t('Share without verifying') }
- {_t('Ignore request')}
+ { _t('Ignore request') }
@@ -154,7 +154,7 @@ export default React.createClass({
} else {
content = (
-
{_t('Loading device info...')}
+
{ _t('Loading device info...') }
);
@@ -165,7 +165,7 @@ export default React.createClass({
onFinished={this.props.onFinished}
title={_t('Encryption key request')}
>
- {content}
+ { content }
);
},
diff --git a/src/components/views/dialogs/QuestionDialog.js b/src/components/views/dialogs/QuestionDialog.js
index ec9b95d7f7..339b284e2f 100644
--- a/src/components/views/dialogs/QuestionDialog.js
+++ b/src/components/views/dialogs/QuestionDialog.js
@@ -1,5 +1,6 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
+Copyright 2017 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.
@@ -17,6 +18,7 @@ limitations under the License.
import React from 'react';
import sdk from '../../../index';
import { _t } from '../../../languageHandler';
+import classnames from 'classnames';
export default React.createClass({
displayName: 'QuestionDialog',
@@ -25,6 +27,7 @@ export default React.createClass({
description: React.PropTypes.node,
extraButtons: React.PropTypes.node,
button: React.PropTypes.string,
+ danger: React.PropTypes.bool,
focus: React.PropTypes.bool,
onFinished: React.PropTypes.func.isRequired,
},
@@ -36,6 +39,7 @@ export default React.createClass({
extraButtons: null,
focus: true,
hasCancelButton: true,
+ danger: false,
};
},
@@ -51,23 +55,27 @@ export default React.createClass({
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
const cancelButton = this.props.hasCancelButton ? (
- {_t("Cancel")}
+ { _t("Cancel") }
) : null;
+ const buttonClasses = classnames({
+ mx_Dialog_primary: true,
+ danger: this.props.danger,
+ });
return (
- {this.props.description}
+ { this.props.description }
-
- {this.props.button || _t('OK')}
+
+ { this.props.button || _t('OK') }
- {this.props.extraButtons}
- {cancelButton}
+ { this.props.extraButtons }
+ { cancelButton }
);
diff --git a/src/components/views/dialogs/SessionRestoreErrorDialog.js b/src/components/views/dialogs/SessionRestoreErrorDialog.js
index 010072e8c6..f404bdd975 100644
--- a/src/components/views/dialogs/SessionRestoreErrorDialog.js
+++ b/src/components/views/dialogs/SessionRestoreErrorDialog.js
@@ -45,10 +45,10 @@ export default React.createClass({
if (SdkConfig.get().bug_report_endpoint_url) {
bugreport = (
- {_tJsx(
+ { _tJsx(
"Otherwise, click here to send a bug report.",
- /(.*?)<\/a>/, (sub) => {sub} ,
- )}
+ /(.*?)<\/a>/, (sub) => { sub } ,
+ ) }
);
}
@@ -57,19 +57,19 @@ export default React.createClass({
-
{_t("We encountered an error trying to restore your previous session. If " +
+
{ _t("We encountered an error trying to restore your previous session. If " +
"you continue, you will need to log in again, and encrypted chat " +
- "history will be unreadable.")}
+ "history will be unreadable.") }
-
{_t("If you have previously used a more recent version of Riot, your session " +
+
{ _t("If you have previously used a more recent version of Riot, your session " +
"may be incompatible with this version. Close this window and return " +
- "to the more recent version.")}
+ "to the more recent version.") }
- {bugreport}
+ { bugreport }
- {_t("Continue anyway")}
+ { _t("Continue anyway") }
diff --git a/src/components/views/dialogs/SetEmailDialog.js b/src/components/views/dialogs/SetEmailDialog.js
index ed5cef2f67..2dd996953d 100644
--- a/src/components/views/dialogs/SetEmailDialog.js
+++ b/src/components/views/dialogs/SetEmailDialog.js
@@ -130,10 +130,10 @@ export default React.createClass({
const emailInput = this.state.emailBusy ? : ;
+ blurToCancel={false}
+ onValueChanged={this.onEmailAddressChanged} />;
return (
;
+ usernameBusyIndicator = ;
} else {
const usernameAvailable = this.state.username &&
this.state.usernameCheckSupport && !this.state.usernameError;
@@ -275,17 +275,17 @@ export default React.createClass({
/(.*?)<\/a>/,
],
[
- (sub) => {this.props.homeserverUrl} ,
- (sub) => {sub} ,
+ (sub) => { this.props.homeserverUrl } ,
+ (sub) => { sub } ,
],
- )}
+ ) }
{ _tJsx(
'If you already have a Matrix account you can log in instead.',
/(.*?)<\/a>/,
- [(sub) => {sub} ],
- )}
+ [(sub) => { sub } ],
+ ) }
{ auth }
{ authErrorIndicator }
diff --git a/src/components/views/dialogs/TextInputDialog.js b/src/components/views/dialogs/TextInputDialog.js
index 673be42030..5ea4191e5e 100644
--- a/src/components/views/dialogs/TextInputDialog.js
+++ b/src/components/views/dialogs/TextInputDialog.js
@@ -65,10 +65,10 @@ export default React.createClass({
>
@@ -76,7 +76,7 @@ export default React.createClass({
{ _t("Cancel") }
- {this.props.button}
+ { this.props.button }
diff --git a/src/components/views/dialogs/UnknownDeviceDialog.js b/src/components/views/dialogs/UnknownDeviceDialog.js
index 6ebd0c3efc..ee8f307f76 100644
--- a/src/components/views/dialogs/UnknownDeviceDialog.js
+++ b/src/components/views/dialogs/UnknownDeviceDialog.js
@@ -28,9 +28,9 @@ function DeviceListEntry(props) {
return (
-
+
{ device.deviceId }
-
+
{ device.getDisplayName() }
);
@@ -48,13 +48,13 @@ function UserUnknownDeviceList(props) {
const {userId, userDevices} = props;
const deviceListEntries = Object.keys(userDevices).map((deviceId) =>
- ,
+ ,
);
return (
- {deviceListEntries}
+ { deviceListEntries }
);
}
@@ -71,13 +71,13 @@ function UnknownDeviceList(props) {
const {devices} = props;
const userListEntries = Object.keys(devices).map((userId) =>
-
+
{ userId }:
-
+
,
);
- return ;
+ return ;
}
UnknownDeviceList.propTypes = {
@@ -120,17 +120,17 @@ export default React.createClass({
if (blacklistUnverified) {
warning = (
- {_t("You are currently blacklisting unverified devices; to send " +
- "messages to these devices you must verify them.")}
+ { _t("You are currently blacklisting unverified devices; to send " +
+ "messages to these devices you must verify them.") }
);
} else {
warning = (
- {_t("We recommend you go through the verification process " +
+ { _t("We recommend you go through the verification process " +
"for each device to confirm they belong to their legitimate owner, " +
- "but you can resend the message without verifying if you prefer.")}
+ "but you can resend the message without verifying if you prefer.") }
);
@@ -149,22 +149,22 @@ export default React.createClass({
>
- {_t('"%(RoomName)s" contains devices that you haven\'t seen before.', {RoomName: this.props.room.name})}
+ { _t('"%(RoomName)s" contains devices that you haven\'t seen before.', {RoomName: this.props.room.name}) }
{ warning }
- {_t("Unknown devices")}:
+ { _t("Unknown devices") }:
-
{
this.props.onFinished();
Resend.resendUnsentEvents(this.props.room);
}}>
- {_t("Send anyway")}
+ { _t("Send anyway") }
-
{
// XXX: temporary logging to try to diagnose
// https://github.com/vector-im/riot-web/issues/3148
diff --git a/src/components/views/elements/AccessibleButton.js b/src/components/views/elements/AccessibleButton.js
index ce58b6d5cf..794e0a4dd7 100644
--- a/src/components/views/elements/AccessibleButton.js
+++ b/src/components/views/elements/AccessibleButton.js
@@ -32,7 +32,7 @@ export default function AccessibleButton(props) {
};
restProps.tabIndex = restProps.tabIndex || "0";
restProps.role = "button";
- restProps.className = (restProps.className ? restProps.className + " " : "") +
+ restProps.className = (restProps.className ? restProps.className + " " : "") +
"mx_AccessibleButton";
return React.createElement(element, restProps, children);
}
diff --git a/src/components/views/elements/ActionButton.js b/src/components/views/elements/ActionButton.js
index 08fb6faa1d..cfd31fe213 100644
--- a/src/components/views/elements/ActionButton.js
+++ b/src/components/views/elements/ActionButton.js
@@ -19,6 +19,7 @@ import PropTypes from 'prop-types';
import AccessibleButton from './AccessibleButton';
import dis from '../../../dispatcher';
import sdk from '../../../index';
+import Analytics from '../../../Analytics';
export default React.createClass({
displayName: 'RoleButton',
@@ -47,6 +48,7 @@ export default React.createClass({
_onClick: function(ev) {
ev.stopPropagation();
+ Analytics.trackEvent('Action Button', 'click', this.props.action);
dis.dispatch({action: this.props.action});
},
@@ -77,8 +79,8 @@ export default React.createClass({
onMouseLeave={this._onMouseLeave}
>
- {tooltip}
+ { tooltip }
);
- }
+ },
});
diff --git a/src/components/views/elements/AddressSelector.js b/src/components/views/elements/AddressSelector.js
index 75fe650600..9a8cd34867 100644
--- a/src/components/views/elements/AddressSelector.js
+++ b/src/components/views/elements/AddressSelector.js
@@ -46,8 +46,8 @@ export default React.createClass({
componentWillReceiveProps: function(props) {
// Make sure the selected item isn't outside the list bounds
- var selected = this.state.selected;
- var maxSelected = this._maxSelected(props.addressList);
+ const selected = this.state.selected;
+ const maxSelected = this._maxSelected(props.addressList);
if (selected > maxSelected) {
this.setState({ selected: maxSelected });
}
@@ -57,7 +57,7 @@ export default React.createClass({
// As the user scrolls with the arrow keys keep the selected item
// at the top of the window.
if (this.scrollElement && this.props.addressList.length > 0 && !this.state.hover) {
- var elementHeight = this.addressListElement.getBoundingClientRect().height;
+ const elementHeight = this.addressListElement.getBoundingClientRect().height;
this.scrollElement.scrollTop = (this.state.selected * elementHeight) - elementHeight;
}
},
@@ -75,7 +75,7 @@ export default React.createClass({
if (this.state.selected > 0) {
this.setState({
selected: this.state.selected - 1,
- hover : false,
+ hover: false,
});
}
},
@@ -84,7 +84,7 @@ export default React.createClass({
if (this.state.selected < this._maxSelected(this.props.addressList)) {
this.setState({
selected: this.state.selected + 1,
- hover : false,
+ hover: false,
});
}
},
@@ -105,7 +105,7 @@ export default React.createClass({
},
onMouseLeave: function() {
- this.setState({ hover : false });
+ this.setState({ hover: false });
},
selectAddress: function(index) {
@@ -117,15 +117,15 @@ export default React.createClass({
},
createAddressListTiles: function() {
- var self = this;
- var AddressTile = sdk.getComponent("elements.AddressTile");
- var maxSelected = this._maxSelected(this.props.addressList);
- var addressList = [];
+ const self = this;
+ const AddressTile = sdk.getComponent("elements.AddressTile");
+ const maxSelected = this._maxSelected(this.props.addressList);
+ const addressList = [];
// Only create the address elements if there are address
if (this.props.addressList.length > 0) {
- for (var i = 0; i <= maxSelected; i++) {
- var classes = classNames({
+ for (let i = 0; i <= maxSelected; i++) {
+ const classes = classNames({
"mx_AddressSelector_addressListElement": true,
"mx_AddressSelector_selected": this.state.selected === i,
});
@@ -143,7 +143,7 @@ export default React.createClass({
ref={(ref) => { this.addressListElement = ref; }}
>
-
+ ,
);
}
}
@@ -151,13 +151,13 @@ export default React.createClass({
},
_maxSelected: function(list) {
- var listSize = list.length === 0 ? 0 : list.length - 1;
- var maxSelected = listSize > (this.props.truncateAt - 1) ? (this.props.truncateAt - 1) : listSize;
+ const listSize = list.length === 0 ? 0 : list.length - 1;
+ const maxSelected = listSize > (this.props.truncateAt - 1) ? (this.props.truncateAt - 1) : listSize;
return maxSelected;
},
render: function() {
- var classes = classNames({
+ const classes = classNames({
"mx_AddressSelector": true,
"mx_AddressSelector_empty": this.props.addressList.length === 0,
});
@@ -168,5 +168,5 @@ export default React.createClass({
{ this.createAddressListTiles() }
);
- }
+ },
});
diff --git a/src/components/views/elements/AddressTile.js b/src/components/views/elements/AddressTile.js
index bee02489fe..a61cbbc794 100644
--- a/src/components/views/elements/AddressTile.js
+++ b/src/components/views/elements/AddressTile.js
@@ -45,11 +45,12 @@ export default React.createClass({
const address = this.props.address;
const name = address.displayName || address.address;
- let imgUrls = [];
+ const imgUrls = [];
+ const isMatrixAddress = ['mx-user-id', 'mx-room-id'].includes(address.addressType);
- if (address.addressType === "mx" && address.avatarMxc) {
+ if (isMatrixAddress && address.avatarMxc) {
imgUrls.push(MatrixClientPeg.get().mxcUrlToHttp(
- address.avatarMxc, 25, 25, 'crop'
+ address.avatarMxc, 25, 25, 'crop',
));
} else if (address.addressType === 'email') {
imgUrls.push('img/icon-email-user.svg');
@@ -77,7 +78,7 @@ export default React.createClass({
let info;
let error = false;
- if (address.addressType === "mx" && address.isKnown) {
+ if (isMatrixAddress && address.isKnown) {
const idClasses = classNames({
"mx_AddressTile_id": true,
"mx_AddressTile_justified": this.props.justified,
@@ -89,7 +90,7 @@ export default React.createClass({
{ address.address }
);
- } else if (address.addressType === "mx") {
+ } else if (isMatrixAddress) {
const unknownMxClasses = classNames({
"mx_AddressTile_unknownMx": true,
"mx_AddressTile_justified": this.props.justified,
@@ -106,24 +107,24 @@ export default React.createClass({
let nameNode = null;
if (address.displayName) {
- nameNode =
{ address.displayName }
+ nameNode =
{ address.displayName }
;
}
info = (
{ address.address }
- {nameNode}
+ { nameNode }
);
} else {
error = true;
- var unknownClasses = classNames({
+ const unknownClasses = classNames({
"mx_AddressTile_unknown": true,
"mx_AddressTile_justified": this.props.justified,
});
info = (
-
{_t("Unknown Address")}
+
{ _t("Unknown Address") }
);
}
@@ -150,5 +151,5 @@ export default React.createClass({
{ dismiss }
);
- }
+ },
});
diff --git a/src/components/views/elements/AppPermission.js b/src/components/views/elements/AppPermission.js
index 3dc7e86d75..f1117fd5aa 100644
--- a/src/components/views/elements/AppPermission.js
+++ b/src/components/views/elements/AppPermission.js
@@ -50,16 +50,16 @@ export default class AppPermission extends React.Component {
let e2eWarningText;
if (this.props.isRoomEncrypted) {
e2eWarningText =
- {_t('NOTE: Apps are not end-to-end encrypted')} ;
+ { _t('NOTE: Apps are not end-to-end encrypted') } ;
}
return (
-
+
- {_t('Do you want to load widget from URL:')} {this.state.curlBase}
- {e2eWarningText}
+ { _t('Do you want to load widget from URL:') } { this.state.curlBase }
+ { e2eWarningText }
-
+
);
} else if (this.state.hasPermissionToLoad == true) {
@@ -287,19 +312,19 @@ export default React.createClass({
return (
- {this.formatAppTileName()}
+ { this.formatAppTileName() }
- {/* Edit widget */}
- {showEditButton && }
+ /> }
- {/* Delete widget */}
+ { /* Delete widget */ }
- {appTileBody}
+ { appTileBody }
);
},
diff --git a/src/components/views/elements/AppWarning.js b/src/components/views/elements/AppWarning.js
index 944f1422e6..f4015ae5b7 100644
--- a/src/components/views/elements/AppWarning.js
+++ b/src/components/views/elements/AppWarning.js
@@ -6,10 +6,10 @@ const AppWarning = (props) => {
return (
-
+
- {props.errorMsg}
+ { props.errorMsg }
);
diff --git a/src/components/views/elements/CreateRoomButton.js b/src/components/views/elements/CreateRoomButton.js
index 22d222c6f1..177d033c75 100644
--- a/src/components/views/elements/CreateRoomButton.js
+++ b/src/components/views/elements/CreateRoomButton.js
@@ -24,7 +24,7 @@ const CreateRoomButton = function(props) {
return (
- {_t("Unblacklist")}
+ { _t("Unblacklist") }
);
} else {
blacklistButton = (
- {_t("Blacklist")}
+ { _t("Blacklist") }
);
}
@@ -99,14 +99,14 @@ export default React.createClass({
verifyButton = (
- {_t("Unverify")}
+ { _t("Unverify") }
);
} else {
verifyButton = (
- {_t("Verify...")}
+ { _t("Verify...") }
);
}
diff --git a/src/components/views/elements/DirectorySearchBox.js b/src/components/views/elements/DirectorySearchBox.js
index eaa6ee34ba..7132162d5c 100644
--- a/src/components/views/elements/DirectorySearchBox.js
+++ b/src/components/views/elements/DirectorySearchBox.js
@@ -95,7 +95,7 @@ export default class DirectorySearchBox extends React.Component {
onChange={this._onChange} onKeyUp={this._onKeyUp}
placeholder={this.props.placeholder} autoFocus
/>
- {join_button}
+ { join_button }
diff --git a/src/components/views/elements/Dropdown.js b/src/components/views/elements/Dropdown.js
index c049c38a68..3787523a56 100644
--- a/src/components/views/elements/Dropdown.js
+++ b/src/components/views/elements/Dropdown.js
@@ -26,6 +26,12 @@ class MenuOption extends React.Component {
this._onClick = this._onClick.bind(this);
}
+ getDefaultProps() {
+ return {
+ disabled: false,
+ };
+ }
+
_onMouseEnter() {
this.props.onMouseEnter(this.props.dropdownKey);
}
@@ -46,15 +52,15 @@ class MenuOption extends React.Component {
onClick={this._onClick} onKeyPress={this._onKeyPress}
onMouseEnter={this._onMouseEnter}
>
- {this.props.children}
-
+ { this.props.children }
+ ;
}
-};
+}
MenuOption.propTypes = {
children: React.PropTypes.oneOfType([
React.PropTypes.arrayOf(React.PropTypes.node),
- React.PropTypes.node
+ React.PropTypes.node,
]),
highlighted: React.PropTypes.bool,
dropdownKey: React.PropTypes.string,
@@ -153,6 +159,8 @@ export default class Dropdown extends React.Component {
}
_onInputClick(ev) {
+ if (this.props.disabled) return;
+
if (!this.state.expanded) {
this.setState({
expanded: true,
@@ -250,13 +258,13 @@ export default class Dropdown extends React.Component {
onMouseEnter={this._setHighlightedOption}
onClick={this._onMenuOptionClick}
>
- {child}
+ { child }
);
});
if (options.length === 0) {
return [
- {_t("No results")}
+ { _t("No results") }
];
}
return options;
@@ -279,7 +287,7 @@ export default class Dropdown extends React.Component {
/>;
}
menu =
- {this._getMenuOptions()}
+ { this._getMenuOptions() }
;
}
@@ -288,12 +296,13 @@ export default class Dropdown extends React.Component {
this.props.getShortOption(this.props.value) :
this.childrenByKey[this.props.value];
currentValue =
- {selectedChild}
-
+ { selectedChild }
+ ;
}
const dropdownClasses = {
mx_Dropdown: true,
+ mx_Dropdown_disabled: this.props.disabled,
};
if (this.props.className) {
dropdownClasses[this.props.className] = true;
@@ -303,9 +312,9 @@ export default class Dropdown extends React.Component {
// to the input, but overflows below it. The root contains both.
return
- {currentValue}
+ { currentValue }
- {menu}
+ { menu }
;
}
@@ -329,4 +338,6 @@ Dropdown.propTypes = {
// in the dropped-down menu.
getShortOption: React.PropTypes.func,
value: React.PropTypes.string,
-}
+ // negative for consistency with HTML
+ disabled: React.PropTypes.bool,
+};
diff --git a/src/components/views/elements/EditableItemList.js b/src/components/views/elements/EditableItemList.js
new file mode 100644
index 0000000000..35e207daef
--- /dev/null
+++ b/src/components/views/elements/EditableItemList.js
@@ -0,0 +1,149 @@
+/*
+Copyright 2017 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.js';
+
+const EditableItem = React.createClass({
+ displayName: 'EditableItem',
+
+ propTypes: {
+ initialValue: PropTypes.string,
+ index: PropTypes.number,
+ placeholder: PropTypes.string,
+
+ onChange: PropTypes.func,
+ onRemove: PropTypes.func,
+ onAdd: PropTypes.func,
+
+ addOnChange: PropTypes.bool,
+ },
+
+ onChange: function(value) {
+ this.setState({ value });
+ if (this.props.onChange) this.props.onChange(value, this.props.index);
+ if (this.props.addOnChange && this.props.onAdd) this.props.onAdd(value);
+ },
+
+ onRemove: function() {
+ if (this.props.onRemove) this.props.onRemove(this.props.index);
+ },
+
+ onAdd: function() {
+ if (this.props.onAdd) this.props.onAdd(this.state.value);
+ },
+
+ render: function() {
+ const EditableText = sdk.getComponent('elements.EditableText');
+ return
+
+ { this.props.onAdd ?
+
+
+
+ :
+
+
+
+ }
+
;
+ },
+});
+
+module.exports = React.createClass({
+ displayName: 'EditableItemList',
+
+ propTypes: {
+ items: PropTypes.arrayOf(PropTypes.string).isRequired,
+ onNewItemChanged: PropTypes.func,
+ onItemAdded: PropTypes.func,
+ onItemEdited: PropTypes.func,
+ onItemRemoved: PropTypes. func,
+ },
+
+ getDefaultProps: function() {
+ return {
+ onItemAdded: () => {},
+ onItemEdited: () => {},
+ onItemRemoved: () => {},
+ onNewItemChanged: () => {},
+ };
+ },
+
+ onItemAdded: function(value) {
+ this.props.onItemAdded(value);
+ },
+
+ onItemEdited: function(value, index) {
+ if (value.length === 0) {
+ this.onItemRemoved(index);
+ } else {
+ this.props.onItemEdited(value, index);
+ }
+ },
+
+ onItemRemoved: function(index) {
+ this.props.onItemRemoved(index);
+ },
+
+ onNewItemChanged: function(value) {
+ this.props.onNewItemChanged(value);
+ },
+
+ render: function() {
+ const editableItems = this.props.items.map((item, index) => {
+ return ;
+ });
+
+ const label = this.props.items.length > 0 ?
+ this.props.itemsLabel : this.props.noItemsLabel;
+
+ return (
+
+ { label }
+
+ { editableItems }
+
+
);
+ },
+});
diff --git a/src/components/views/elements/EditableText.js b/src/components/views/elements/EditableText.js
index 3ce8c90447..ac8821a0c2 100644
--- a/src/components/views/elements/EditableText.js
+++ b/src/components/views/elements/EditableText.js
@@ -16,7 +16,7 @@ limitations under the License.
'use strict';
-var React = require('react');
+const React = require('react');
const KEY_TAB = 9;
const KEY_SHIFT = 16;
@@ -65,7 +65,9 @@ module.exports = React.createClass({
},
componentWillReceiveProps: function(nextProps) {
- if (nextProps.initialValue !== this.props.initialValue) {
+ if (nextProps.initialValue !== this.props.initialValue ||
+ nextProps.initialValue !== this.value
+ ) {
this.value = nextProps.initialValue;
if (this.refs.editable_div) {
this.showPlaceholder(!this.value);
@@ -93,8 +95,7 @@ module.exports = React.createClass({
this.refs.editable_div.setAttribute("class", this.props.className + " " + this.props.placeholderClassName);
this.placeholder = true;
this.value = '';
- }
- else {
+ } else {
this.refs.editable_div.textContent = this.value;
this.refs.editable_div.setAttribute("class", this.props.className);
this.placeholder = false;
@@ -150,8 +151,7 @@ module.exports = React.createClass({
if (!ev.target.textContent) {
this.showPlaceholder(true);
- }
- else if (!this.placeholder) {
+ } else if (!this.placeholder) {
this.value = ev.target.textContent;
}
@@ -175,21 +175,21 @@ module.exports = React.createClass({
onFocus: function(ev) {
//ev.target.setSelectionRange(0, ev.target.textContent.length);
- var node = ev.target.childNodes[0];
+ const node = ev.target.childNodes[0];
if (node) {
- var range = document.createRange();
+ const range = document.createRange();
range.setStart(node, 0);
range.setEnd(node, node.length);
- var sel = window.getSelection();
+ const sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
}
},
onFinish: function(ev, shouldSubmit) {
- var self = this;
- var submit = (ev.key === "Enter") || shouldSubmit;
+ const self = this;
+ const submit = (ev.key === "Enter") || shouldSubmit;
this.setState({
phase: this.Phases.Display,
}, function() {
@@ -200,19 +200,16 @@ module.exports = React.createClass({
},
onBlur: function(ev) {
- var sel = window.getSelection();
+ const sel = window.getSelection();
sel.removeAllRanges();
- if (this.props.blurToCancel)
- {this.cancelEdit();}
- else
- {this.onFinish(ev, this.props.blurToSubmit);}
+ if (this.props.blurToCancel) {this.cancelEdit();} else {this.onFinish(ev, this.props.blurToSubmit);}
this.showPlaceholder(!this.value);
},
render: function() {
- var editable_el;
+ let editable_el;
if (!this.props.editable || (this.state.phase == this.Phases.Display && (this.props.label || this.props.labelClassName) && !this.value)) {
// show the label
@@ -224,5 +221,5 @@ module.exports = React.createClass({
}
return editable_el;
- }
+ },
});
diff --git a/src/components/views/elements/EditableTextContainer.js b/src/components/views/elements/EditableTextContainer.js
index aeb32c07ec..0025862967 100644
--- a/src/components/views/elements/EditableTextContainer.js
+++ b/src/components/views/elements/EditableTextContainer.js
@@ -64,7 +64,7 @@ export default class EditableTextContainer extends React.Component {
errorString: error.toString(),
busy: false,
});
- }
+ },
);
}
@@ -96,22 +96,22 @@ export default class EditableTextContainer extends React.Component {
errorString: error.toString(),
busy: false,
});
- }
+ },
);
}
render() {
if (this.state.busy) {
- var Loader = sdk.getComponent("elements.Spinner");
+ const Loader = sdk.getComponent("elements.Spinner");
return (
);
} else if (this.state.errorString) {
return (
- {this.state.errorString}
+ { this.state.errorString }
);
} else {
- var EditableText = sdk.getComponent('elements.EditableText');
+ const EditableText = sdk.getComponent('elements.EditableText');
return (
{}
+// reject: () => {}
+// }
+};
+
+let debounceTimeoutID;
+function getPublicisedGroupsCached(matrixClient, userId) {
+ if (userGroups[userId]) {
+ return Promise.resolve(userGroups[userId]);
+ }
+
+ // Bulk lookup ongoing, return promise to resolve/reject
+ if (usersPending[userId]) {
+ return usersPending[userId].prom;
+ }
+
+ usersPending[userId] = {};
+ usersPending[userId].prom = new Promise((resolve, reject) => {
+ usersPending[userId].resolve = resolve;
+ usersPending[userId].reject = reject;
+ }).then((groups) => {
+ userGroups[userId] = groups;
+ setTimeout(() => {
+ delete userGroups[userId];
+ }, USER_GROUPS_CACHE_BUST_MS);
+ return userGroups[userId];
+ }).catch((err) => {
+ throw err;
+ }).finally(() => {
+ delete usersPending[userId];
+ });
+
+ // This debounce will allow consecutive requests for the public groups of users that
+ // are sent in intervals of < BULK_REQUEST_DEBOUNCE_MS to be batched and only requested
+ // when no more requests are received within the next BULK_REQUEST_DEBOUNCE_MS. The naive
+ // implementation would do a request that only requested the groups for `userId`, leading
+ // to a worst and best case of 1 user per request. This implementation's worst is still
+ // 1 user per request but only if the requests are > BULK_REQUEST_DEBOUNCE_MS apart and the
+ // best case is N users per request.
+ //
+ // This is to reduce the number of requests made whilst trading off latency when viewing
+ // a Flair component.
+ if (debounceTimeoutID) clearTimeout(debounceTimeoutID);
+ debounceTimeoutID = setTimeout(() => {
+ batchedGetPublicGroups(matrixClient);
+ }, BULK_REQUEST_DEBOUNCE_MS);
+
+ return usersPending[userId].prom;
+}
+
+async function batchedGetPublicGroups(matrixClient) {
+ // Take the userIds from the keys of usersPending
+ const usersInFlight = Object.keys(usersPending);
+ let resp = {
+ users: [],
+ };
+ try {
+ resp = await matrixClient.getPublicisedGroups(usersInFlight);
+ } catch (err) {
+ // Propagate the same error to all usersInFlight
+ usersInFlight.forEach((userId) => {
+ usersPending[userId].reject(err);
+ });
+ return;
+ }
+ const updatedUserGroups = resp.users;
+ usersInFlight.forEach((userId) => {
+ usersPending[userId].resolve(updatedUserGroups[userId] || []);
+ });
+}
+
+async function getGroupProfileCached(matrixClient, groupId) {
+ if (groupProfiles[groupId]) {
+ return groupProfiles[groupId];
+ }
+
+ const profile = await matrixClient.getGroupProfile(groupId);
+ groupProfiles[groupId] = {
+ groupId,
+ avatarUrl: profile.avatar_url,
+ };
+ setTimeout(() => {
+ delete groupProfiles[groupId];
+ }, GROUP_PROFILES_CACHE_BUST_MS);
+
+ return groupProfiles[groupId];
+}
+
+class FlairAvatar extends React.Component {
+ constructor() {
+ super();
+ this.onClick = this.onClick.bind(this);
+ }
+
+ onClick(ev) {
+ ev.preventDefault();
+ // Don't trigger onClick of parent element
+ ev.stopPropagation();
+ dis.dispatch({
+ action: 'view_group',
+ group_id: this.props.groupProfile.groupId,
+ });
+ }
+
+ render() {
+ const httpUrl = this.context.matrixClient.mxcUrlToHttp(
+ this.props.groupProfile.avatarUrl, 14, 14, 'scale', false);
+ return ;
+ }
+}
+
+FlairAvatar.propTypes = {
+ groupProfile: PropTypes.shape({
+ groupId: PropTypes.string.isRequired,
+ avatarUrl: PropTypes.string.isRequired,
+ }),
+};
+
+FlairAvatar.contextTypes = {
+ matrixClient: React.PropTypes.instanceOf(MatrixClient).isRequired,
+};
+
+export default class Flair extends React.Component {
+ constructor() {
+ super();
+ this.state = {
+ profiles: [],
+ };
+ this.onRoomStateEvents = this.onRoomStateEvents.bind(this);
+ }
+
+ componentWillUnmount() {
+ this._unmounted = true;
+ this.context.matrixClient.removeListener('RoomState.events', this.onRoomStateEvents);
+ }
+
+ componentWillMount() {
+ this._unmounted = false;
+ if (UserSettingsStore.isFeatureEnabled('feature_groups') && groupSupport) {
+ this._generateAvatars();
+ }
+ this.context.matrixClient.on('RoomState.events', this.onRoomStateEvents);
+ }
+
+ onRoomStateEvents(event) {
+ if (event.getType() === 'm.room.related_groups' && groupSupport) {
+ this._generateAvatars();
+ }
+ }
+
+ async _getGroupProfiles(groups) {
+ const profiles = [];
+ for (const groupId of groups) {
+ let groupProfile = null;
+ try {
+ groupProfile = await getGroupProfileCached(this.context.matrixClient, groupId);
+ } catch (err) {
+ console.error('Could not get profile for group', groupId, err);
+ }
+ profiles.push(groupProfile);
+ }
+ return profiles.filter((p) => p !== null);
+ }
+
+ async _generateAvatars() {
+ let groups;
+ try {
+ groups = await getPublicisedGroupsCached(this.context.matrixClient, this.props.userId);
+ } catch (err) {
+ // Indicate whether the homeserver supports groups
+ if (err.errcode === 'M_UNRECOGNIZED') {
+ console.warn('Cannot display flair, server does not support groups');
+ groupSupport = false;
+ // Return silently to avoid spamming for non-supporting servers
+ return;
+ }
+ console.error('Could not get groups for user', this.props.userId, err);
+ }
+ if (this.props.roomId && this.props.showRelated) {
+ const relatedGroupsEvent = this.context.matrixClient
+ .getRoom(this.props.roomId)
+ .currentState
+ .getStateEvents('m.room.related_groups', '');
+ const relatedGroups = relatedGroupsEvent ?
+ relatedGroupsEvent.getContent().groups || [] : [];
+ if (relatedGroups && relatedGroups.length > 0) {
+ groups = groups.filter((groupId) => {
+ return relatedGroups.includes(groupId);
+ });
+ } else {
+ groups = [];
+ }
+ }
+ if (!groups || groups.length === 0) {
+ return;
+ }
+ const profiles = await this._getGroupProfiles(groups);
+ if (!this.unmounted) {
+ this.setState({profiles});
+ }
+ }
+
+ render() {
+ if (this.state.profiles.length === 0) {
+ return
;
+ }
+ const avatars = this.state.profiles.map((profile, index) => {
+ return ;
+ });
+ return (
+
+ { avatars }
+
+ );
+ }
+}
+
+Flair.propTypes = {
+ userId: PropTypes.string,
+
+ // Whether to show only the flair associated with related groups of the given room,
+ // or all flair associated with a user.
+ showRelated: PropTypes.bool,
+ // The room that this flair will be displayed in. Optional. Only applies when showRelated = true.
+ roomId: PropTypes.string,
+};
+
+// TODO: We've decided that all components should follow this pattern, which means removing withMatrixClient and using
+// this.context.matrixClient everywhere instead of this.props.matrixClient.
+// See https://github.com/vector-im/riot-web/issues/4951.
+Flair.contextTypes = {
+ matrixClient: React.PropTypes.instanceOf(MatrixClient).isRequired,
+};
diff --git a/src/components/views/elements/GroupsButton.js b/src/components/views/elements/GroupsButton.js
new file mode 100644
index 0000000000..f973fda74a
--- /dev/null
+++ b/src/components/views/elements/GroupsButton.js
@@ -0,0 +1,38 @@
+/*
+Copyright 2017 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 sdk from '../../../index';
+import PropTypes from 'prop-types';
+import { _t } from '../../../languageHandler';
+
+const GroupsButton = function(props) {
+ const ActionButton = sdk.getComponent('elements.ActionButton');
+ return (
+
+ );
+};
+
+GroupsButton.propTypes = {
+ size: PropTypes.string,
+ tooltip: PropTypes.bool,
+};
+
+export default GroupsButton;
diff --git a/src/components/views/elements/HomeButton.js b/src/components/views/elements/HomeButton.js
index 4877f5dd43..05e21487eb 100644
--- a/src/components/views/elements/HomeButton.js
+++ b/src/components/views/elements/HomeButton.js
@@ -23,7 +23,7 @@ const HomeButton = function(props) {
const ActionButton = sdk.getComponent('elements.ActionButton');
return (
{
- langs.sort(function(a, b){
+ langs.sort(function(a, b) {
if(a.label < b.label) return -1;
if(a.label > b.label) return 1;
return 0;
@@ -89,7 +89,7 @@ export default class LanguageDropdown extends React.Component {
const options = displayedLanguages.map((language) => {
return
- {language.label}
+ { language.label }
;
});
@@ -108,8 +108,8 @@ export default class LanguageDropdown extends React.Component {
onOptionChange={this.props.onOptionChange} onSearchChange={this._onSearchChange}
searchEnabled={true} value={value}
>
- {options}
-
+ { options }
+ ;
}
}
diff --git a/src/components/views/elements/ManageIntegsButton.js b/src/components/views/elements/ManageIntegsButton.js
index ac562a7c20..17dbbeee62 100644
--- a/src/components/views/elements/ManageIntegsButton.js
+++ b/src/components/views/elements/ManageIntegsButton.js
@@ -17,6 +17,7 @@ limitations under the License.
import React from 'react';
import PropTypes from 'prop-types';
import sdk from '../../../index';
+import classNames from 'classnames';
import SdkConfig from '../../../SdkConfig';
import ScalarAuthClient from '../../../ScalarAuthClient';
import ScalarMessaging from '../../../ScalarMessaging';
@@ -31,11 +32,9 @@ export default class ManageIntegsButton extends React.Component {
this.state = {
scalarError: null,
- showIntegrationsError: false,
};
this.onManageIntegrations = this.onManageIntegrations.bind(this);
- this.onShowIntegrationsError = this.onShowIntegrationsError.bind(this);
}
componentWillMount() {
@@ -48,7 +47,7 @@ export default class ManageIntegsButton extends React.Component {
this.forceUpdate();
}, (err) => {
this.setState({ scalarError: err});
- console.error(err);
+ console.error('Error whilst initialising scalarClient for ManageIntegsButton', err);
});
}
}
@@ -59,6 +58,9 @@ export default class ManageIntegsButton extends React.Component {
onManageIntegrations(ev) {
ev.preventDefault();
+ if (this.state.scalarError && !this.scalarClient.hasCredentials()) {
+ return;
+ }
const IntegrationsManager = sdk.getComponent("views.settings.IntegrationsManager");
Modal.createDialog(IntegrationsManager, {
src: (this.scalarClient !== null && this.scalarClient.hasCredentials()) ?
@@ -67,45 +69,33 @@ export default class ManageIntegsButton extends React.Component {
}, "mx_IntegrationsManager");
}
- onShowIntegrationsError(ev) {
- ev.preventDefault();
- this.setState({
- showIntegrationsError: !this.state.showIntegrationsError,
- });
- }
-
render() {
let integrationsButton =
;
- let integrationsError;
+ let integrationsWarningTriangle =
;
+ let integrationsErrorPopup =
;
if (this.scalarClient !== null) {
- if (this.state.showIntegrationsError && this.state.scalarError) {
- integrationsError = (
+ const integrationsButtonClasses = classNames({
+ mx_RoomHeader_button: true,
+ mx_RoomSettings_integrationsButton_error: !!this.state.scalarError,
+ });
+
+ if (this.state.scalarError && !this.scalarClient.hasCredentials()) {
+ integrationsWarningTriangle = ;
+ // Popup shown when hovering over integrationsButton_error (via CSS)
+ integrationsErrorPopup = (
{ _t('Could not connect to the integration server') }
);
}
- if (this.scalarClient.hasCredentials()) {
- integrationsButton = (
-
-
-
- );
- } else if (this.state.scalarError) {
- integrationsButton = (
-
-
- { integrationsError }
-
- );
- } else {
- integrationsButton = (
-
-
-
- );
- }
+ integrationsButton = (
+
+
+ { integrationsWarningTriangle }
+ { integrationsErrorPopup }
+
+ );
}
return integrationsButton;
diff --git a/src/components/views/elements/MemberEventListSummary.js b/src/components/views/elements/MemberEventListSummary.js
index 842b44b793..596838febe 100644
--- a/src/components/views/elements/MemberEventListSummary.js
+++ b/src/components/views/elements/MemberEventListSummary.js
@@ -34,11 +34,13 @@ module.exports = React.createClass({
threshold: React.PropTypes.number,
// Called when the MELS expansion is toggled
onToggle: React.PropTypes.func,
+ // Whether or not to begin with state.expanded=true
+ startExpanded: React.PropTypes.bool,
},
getInitialState: function() {
return {
- expanded: false,
+ expanded: Boolean(this.props.startExpanded),
};
},
@@ -94,12 +96,12 @@ module.exports = React.createClass({
// Transform into consecutive repetitions of the same transition (like 5
// consecutive 'joined_and_left's)
const coalescedTransitions = this._coalesceRepeatedTransitions(
- canonicalTransitions
+ canonicalTransitions,
);
const descs = coalescedTransitions.map((t) => {
return this._getDescriptionForTransition(
- t.transitionType, plural, t.repeats
+ t.transitionType, plural, t.repeats,
);
});
@@ -117,7 +119,7 @@ module.exports = React.createClass({
return (
- {summaries.join(", ")}
+ { summaries.join(", ") }
);
@@ -368,7 +370,7 @@ module.exports = React.createClass({
*/
_renderCommaSeparatedList(items, itemLimit) {
const remaining = itemLimit === undefined ? 0 : Math.max(
- items.length - itemLimit, 0
+ items.length - itemLimit, 0,
);
if (items.length === 0) {
return "";
@@ -376,7 +378,7 @@ module.exports = React.createClass({
return items[0];
} else if (remaining) {
items = items.slice(0, itemLimit);
- return (remaining > 1)
+ return (remaining > 1)
? _t("%(items)s and %(remaining)s others", { items: items.join(', '), remaining: remaining } )
: _t("%(items)s and one other", { items: items.join(', ') });
} else {
@@ -392,8 +394,8 @@ module.exports = React.createClass({
);
});
return (
-
- {avatars}
+
+ { avatars }
);
},
@@ -417,19 +419,15 @@ module.exports = React.createClass({
case 'join':
if (e.mxEvent.getPrevContent().membership === 'join') {
if (e.mxEvent.getContent().displayname !==
- e.mxEvent.getPrevContent().displayname)
- {
+ e.mxEvent.getPrevContent().displayname) {
return 'changed_name';
- }
- else if (e.mxEvent.getContent().avatar_url !==
- e.mxEvent.getPrevContent().avatar_url)
- {
+ } else if (e.mxEvent.getContent().avatar_url !==
+ e.mxEvent.getPrevContent().avatar_url) {
return 'changed_avatar';
}
// console.log("MELS ignoring duplicate membership join event");
return null;
- }
- else {
+ } else {
return 'joined';
}
case 'leave':
@@ -481,7 +479,7 @@ module.exports = React.createClass({
firstEvent.index < aggregateIndices[seq]) {
aggregateIndices[seq] = firstEvent.index;
}
- }
+ },
);
return {
@@ -492,7 +490,7 @@ module.exports = React.createClass({
render: function() {
const eventsToRender = this.props.events;
- const eventIds = eventsToRender.map(e => e.getId()).join(',');
+ const eventIds = eventsToRender.map((e) => e.getId()).join(',');
const fewEvents = eventsToRender.length < this.props.threshold;
const expanded = this.state.expanded || fewEvents;
@@ -504,7 +502,7 @@ module.exports = React.createClass({
if (fewEvents) {
return (
- {expandedEvents}
+ { expandedEvents }
);
}
@@ -540,7 +538,7 @@ module.exports = React.createClass({
// Sort types by order of lowest event index within sequence
const orderedTransitionSequences = Object.keys(aggregate.names).sort(
- (seq1, seq2) => aggregate.indices[seq1] > aggregate.indices[seq2]
+ (seq1, seq2) => aggregate.indices[seq1] > aggregate.indices[seq2],
);
let summaryContainer = null;
@@ -548,24 +546,24 @@ module.exports = React.createClass({
summaryContainer = (
- {this._renderAvatars(avatarMembers)}
- {this._renderSummary(aggregate.names, orderedTransitionSequences)}
+ { this._renderAvatars(avatarMembers) }
+ { this._renderSummary(aggregate.names, orderedTransitionSequences) }
);
}
const toggleButton = (
- {expanded ? 'collapse' : 'expand'}
+ { expanded ? 'collapse' : 'expand' }
);
return (
- {toggleButton}
- {summaryContainer}
- {expanded ?
: null}
- {expandedEvents}
+ { toggleButton }
+ { summaryContainer }
+ { expanded ?
: null }
+ { expandedEvents }
);
},
diff --git a/src/components/views/elements/MessageSpinner.js b/src/components/views/elements/MessageSpinner.js
index bc0a326338..500c919d45 100644
--- a/src/components/views/elements/MessageSpinner.js
+++ b/src/components/views/elements/MessageSpinner.js
@@ -26,8 +26,8 @@ module.exports = React.createClass({
const msg = this.props.msg || "Loading...";
return (
-
{msg}
-
+
{ msg }
+
);
},
diff --git a/src/components/views/elements/Pill.js b/src/components/views/elements/Pill.js
index 214abc2329..51ae85ba5a 100644
--- a/src/components/views/elements/Pill.js
+++ b/src/components/views/elements/Pill.js
@@ -167,7 +167,7 @@ const Pill = React.createClass({
userId = member.userId;
linkText = member.rawDisplayName.replace(' (IRC)', ''); // FIXME when groups are done
if (this.props.shouldShowPillAvatar) {
- avatar = ;
+ avatar = ;
}
pillClass = 'mx_UserPill';
href = null;
@@ -180,7 +180,7 @@ const Pill = React.createClass({
if (room) {
linkText = (room ? getDisplayAliasForRoom(room) : null) || resource;
if (this.props.shouldShowPillAvatar) {
- avatar = ;
+ avatar = ;
}
pillClass = 'mx_RoomPill';
}
@@ -195,12 +195,12 @@ const Pill = React.createClass({
if (this.state.pillType) {
return this.props.inMessage ?
- {avatar}
- {linkText}
+ { avatar }
+ { linkText }
:
- {avatar}
- {linkText}
+ { avatar }
+ { linkText }
;
} else {
// Deliberately render nothing if the URL isn't recognised
diff --git a/src/components/views/elements/PowerSelector.js b/src/components/views/elements/PowerSelector.js
index efeb81fe2d..a0aaa12ff1 100644
--- a/src/components/views/elements/PowerSelector.js
+++ b/src/components/views/elements/PowerSelector.js
@@ -20,8 +20,8 @@ import React from 'react';
import * as Roles from '../../../Roles';
import { _t } from '../../../languageHandler';
-var LEVEL_ROLE_MAP = {};
-var reverseRoles = {};
+let LEVEL_ROLE_MAP = {};
+const reverseRoles = {};
module.exports = React.createClass({
displayName: 'PowerSelector',
@@ -46,7 +46,7 @@ module.exports = React.createClass({
custom: (LEVEL_ROLE_MAP[this.props.value] === undefined),
};
},
-
+
componentWillMount: function() {
LEVEL_ROLE_MAP = Roles.levelRoleMap();
Object.keys(LEVEL_ROLE_MAP).forEach(function(key) {
@@ -72,7 +72,7 @@ module.exports = React.createClass({
},
getValue: function() {
- var value;
+ let value;
if (this.refs.select) {
value = reverseRoles[this.refs.select.value];
if (this.refs.custom) {
@@ -83,30 +83,27 @@ module.exports = React.createClass({
},
render: function() {
- var customPicker;
+ let customPicker;
if (this.state.custom) {
- var input;
+ let input;
if (this.props.disabled) {
input = { this.props.value } ;
- }
- else {
- input = ;
+ } else {
+ input = ;
}
customPicker = of { input } ;
}
- var selectValue;
+ let selectValue;
if (this.state.custom) {
selectValue = "Custom";
- }
- else {
+ } else {
selectValue = LEVEL_ROLE_MAP[this.props.value] || "Custom";
}
- var select;
+ let select;
if (this.props.disabled) {
select = { selectValue } ;
- }
- else {
+ } else {
// Each level must have a definition in LEVEL_ROLE_MAP
const levels = [0, 50, 100];
let options = levels.map((level) => {
@@ -115,18 +112,18 @@ module.exports = React.createClass({
// Give a userDefault (users_default in the power event) of 0 but
// because level !== undefined, this should never be used.
text: Roles.textualPowerLevel(level, 0),
- }
+ };
});
options.push({ value: "Custom", text: _t("Custom level") });
options = options.map((op) => {
- return {op.text} ;
+ return { op.text } ;
});
select =
+ value={this.props.controlled ? selectValue : undefined}
+ defaultValue={!this.props.controlled ? selectValue : undefined}
+ onChange={this.onSelectChange}>
{ options }
;
}
@@ -137,5 +134,5 @@ module.exports = React.createClass({
{ customPicker }
);
- }
+ },
});
diff --git a/src/components/views/elements/ProgressBar.js b/src/components/views/elements/ProgressBar.js
index a39e8e48f9..861bfb1335 100644
--- a/src/components/views/elements/ProgressBar.js
+++ b/src/components/views/elements/ProgressBar.js
@@ -16,23 +16,23 @@ limitations under the License.
'use strict';
-var React = require('react');
+const React = require('react');
module.exports = React.createClass({
displayName: 'ProgressBar',
propTypes: {
value: React.PropTypes.number,
- max: React.PropTypes.number
+ max: React.PropTypes.number,
},
render: function() {
// Would use an HTML5 progress tag but if that doesn't animate if you
// use the HTML attributes rather than styles
- var progressStyle = {
- width: ((this.props.value / this.props.max) * 100)+"%"
+ const progressStyle = {
+ width: ((this.props.value / this.props.max) * 100)+"%",
};
return (
);
- }
+ },
});
diff --git a/src/components/views/elements/RoomDirectoryButton.js b/src/components/views/elements/RoomDirectoryButton.js
index d964d9e5bc..d8f88034e3 100644
--- a/src/components/views/elements/RoomDirectoryButton.js
+++ b/src/components/views/elements/RoomDirectoryButton.js
@@ -24,7 +24,7 @@ const RoomDirectoryButton = function(props) {
return (
);
- }
+ },
});
// Register with the Tinter so that we will be told if the tint changes
diff --git a/src/components/views/elements/TruncatedList.js b/src/components/views/elements/TruncatedList.js
index d6a8e1fb7e..1a674eef65 100644
--- a/src/components/views/elements/TruncatedList.js
+++ b/src/components/views/elements/TruncatedList.js
@@ -1,5 +1,6 @@
/*
Copyright 2016 OpenMarket Ltd
+Copyright 2017 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.
@@ -13,7 +14,9 @@ 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');
+
+import React from 'react';
+import PropTypes from 'prop-types';
import { _t } from '../../../languageHandler';
module.exports = React.createClass({
@@ -21,12 +24,21 @@ module.exports = React.createClass({
propTypes: {
// The number of elements to show before truncating. If negative, no truncation is done.
- truncateAt: React.PropTypes.number,
+ truncateAt: PropTypes.number,
// The className to apply to the wrapping div
- className: React.PropTypes.string,
+ className: PropTypes.string,
+ // A function that returns the children to be rendered into the element.
+ // function getChildren(start: number, end: number): Array
+ // The start element is included, the end is not (as in `slice`).
+ // If omitted, the React child elements will be used. This parameter can be used
+ // to avoid creating unnecessary React elements.
+ getChildren: PropTypes.func,
+ // A function that should return the total number of child element available.
+ // Required if getChildren is supplied.
+ getChildCount: PropTypes.func,
// A function which will be invoked when an overflow element is required.
// This will be inserted after the children.
- createOverflowElement: React.PropTypes.func
+ createOverflowElement: PropTypes.func,
},
getDefaultProps: function() {
@@ -34,40 +46,56 @@ module.exports = React.createClass({
truncateAt: 2,
createOverflowElement: function(overflowCount, totalCount) {
return (
- {_t("And %(count)s more...", {count: overflowCount})}
+ { _t("And %(count)s more...", {count: overflowCount}) }
);
- }
+ },
};
},
+ _getChildren: function(start, end) {
+ if (this.props.getChildren && this.props.getChildCount) {
+ return this.props.getChildren(start, end);
+ } else {
+ // XXX: I'm not sure why anything would pass null into this, it seems
+ // like a bizzare case to handle, but I'm preserving the behaviour.
+ // (see commit 38d5c7d5c5d5a34dc16ef5d46278315f5c57f542)
+ return React.Children.toArray(this.props.children).filter((c) => {
+ return c != null;
+ }).slice(start, end);
+ }
+ },
+
+ _getChildCount: function() {
+ if (this.props.getChildren && this.props.getChildCount) {
+ return this.props.getChildCount();
+ } else {
+ return React.Children.toArray(this.props.children).filter((c) => {
+ return c != null;
+ }).length;
+ }
+ },
+
render: function() {
- var childsJsx = this.props.children;
- var overflowJsx;
- var childArray = React.Children.toArray(this.props.children).filter((c) => {
- return c != null;
- });
-
- var childCount = childArray.length;
+ let overflowNode = null;
+ const totalChildren = this._getChildCount();
+ let upperBound = totalChildren;
if (this.props.truncateAt >= 0) {
- var overflowCount = childCount - this.props.truncateAt;
-
+ const overflowCount = totalChildren - this.props.truncateAt;
if (overflowCount > 1) {
- overflowJsx = this.props.createOverflowElement(
- overflowCount, childCount
+ overflowNode = this.props.createOverflowElement(
+ overflowCount, totalChildren,
);
-
- // cut out the overflow elements
- childArray.splice(childCount - overflowCount, overflowCount);
- childsJsx = childArray; // use what is left
+ upperBound = this.props.truncateAt;
}
}
+ const childNodes = this._getChildren(0, upperBound);
return (
- {childsJsx}
- {overflowJsx}
+ { childNodes }
+ { overflowNode }
);
- }
+ },
});
diff --git a/src/components/views/elements/UserSelector.js b/src/components/views/elements/UserSelector.js
index 955903aac0..a523c57d9f 100644
--- a/src/components/views/elements/UserSelector.js
+++ b/src/components/views/elements/UserSelector.js
@@ -52,19 +52,19 @@ module.exports = React.createClass({
},
render: function() {
- var self = this;
+ const self = this;
return (
);
- }
+ },
});
diff --git a/src/components/views/groups/GroupInviteTile.js b/src/components/views/groups/GroupInviteTile.js
new file mode 100644
index 0000000000..07c0bcf6b9
--- /dev/null
+++ b/src/components/views/groups/GroupInviteTile.js
@@ -0,0 +1,70 @@
+/*
+Copyright 2017 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 dis from '../../../dispatcher';
+import AccessibleButton from '../elements/AccessibleButton';
+
+export default React.createClass({
+ displayName: 'GroupInviteTile',
+
+ propTypes: {
+ group: PropTypes.object.isRequired,
+ },
+
+ onClick: function(e) {
+ dis.dispatch({
+ action: 'view_group',
+ group_id: this.props.group.groupId,
+ });
+ },
+
+ render: function() {
+ const BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
+ const EmojiText = sdk.getComponent('elements.EmojiText');
+
+ const av = (
+
+ );
+
+ const label =
+ { this.props.group.name }
+ ;
+
+ const badge = !
;
+
+ return (
+
+
+ { av }
+
+
+ { label }
+ { badge }
+
+
+ );
+ },
+});
diff --git a/src/components/views/groups/GroupMemberInfo.js b/src/components/views/groups/GroupMemberInfo.js
new file mode 100644
index 0000000000..6f1a370f26
--- /dev/null
+++ b/src/components/views/groups/GroupMemberInfo.js
@@ -0,0 +1,195 @@
+/*
+Copyright 2017 Vector Creations Ltd
+Copyright 2017 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 PropTypes from 'prop-types';
+import React from 'react';
+import dis from '../../../dispatcher';
+import Modal from '../../../Modal';
+import sdk from '../../../index';
+import { _t } from '../../../languageHandler';
+import { GroupMemberType } from '../../../groups';
+import { groupMemberFromApiObject } from '../../../groups';
+import withMatrixClient from '../../../wrappers/withMatrixClient';
+import AccessibleButton from '../elements/AccessibleButton';
+import GeminiScrollbar from 'react-gemini-scrollbar';
+
+
+module.exports = withMatrixClient(React.createClass({
+ displayName: 'GroupMemberInfo',
+
+ propTypes: {
+ matrixClient: PropTypes.object.isRequired,
+ groupId: PropTypes.string,
+ groupMember: GroupMemberType,
+ },
+
+ getInitialState: function() {
+ return {
+ fetching: false,
+ removingUser: false,
+ groupMembers: null,
+ };
+ },
+
+ componentWillMount: function() {
+ this._fetchMembers();
+ },
+
+ _fetchMembers: function() {
+ this.setState({fetching: true});
+ this.props.matrixClient.getGroupUsers(this.props.groupId).then((result) => {
+ this.setState({
+ groupMembers: result.chunk.map((apiMember) => {
+ return groupMemberFromApiObject(apiMember);
+ }),
+ fetching: false,
+ });
+ }).catch((e) => {
+ this.setState({fetching: false});
+ console.error("Failed to get group groupMember list: ", e);
+ });
+ },
+
+ _onKick: function() {
+ const ConfirmUserActionDialog = sdk.getComponent("dialogs.ConfirmUserActionDialog");
+ Modal.createDialog(ConfirmUserActionDialog, {
+ groupMember: this.props.groupMember,
+ action: _t('Remove from group'),
+ danger: true,
+ onFinished: (proceed) => {
+ if (!proceed) return;
+
+ this.setState({removingUser: true});
+ this.props.matrixClient.removeUserFromGroup(
+ this.props.groupId, this.props.groupMember.userId,
+ ).then(() => {
+ // return to the user list
+ dis.dispatch({
+ action: "view_user",
+ member: null,
+ });
+ }).catch((e) => {
+ const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
+ Modal.createTrackedDialog('Failed to remove user from group', '', ErrorDialog, {
+ title: _t('Error'),
+ description: _t('Failed to remove user from group'),
+ });
+ }).finally(() => {
+ this.setState({removingUser: false});
+ });
+ },
+ });
+ },
+
+ _onCancel: function(e) {
+ // Go back to the user list
+ dis.dispatch({
+ action: "view_user",
+ member: null,
+ });
+ },
+
+ onRoomTileClick(roomId) {
+ dis.dispatch({
+ action: 'view_room',
+ room_id: roomId,
+ });
+ },
+
+ render: function() {
+ if (this.state.fetching || this.state.removingUser) {
+ const Spinner = sdk.getComponent("elements.Spinner");
+ return ;
+ }
+ if (!this.state.groupMembers) return null;
+
+ const targetIsInGroup = this.state.groupMembers.some((m) => {
+ return m.userId === this.props.groupMember.userId;
+ });
+
+ let kickButton;
+ let adminButton;
+
+ if (targetIsInGroup) {
+ kickButton = (
+
+ { _t('Remove from group') }
+
+ );
+
+ // No make/revoke admin API yet
+ /*const opLabel = this.state.isTargetMod ? _t("Revoke Moderator") : _t("Make Moderator");
+ giveModButton =
+ {giveOpLabel}
+ ;*/
+ }
+
+ let adminTools;
+ if (kickButton || adminButton) {
+ adminTools =
+
+
{ _t("Admin Tools") }
+
+
+ { kickButton }
+ { adminButton }
+
+
;
+ }
+
+ const avatarUrl = this.props.matrixClient.mxcUrlToHttp(
+ this.props.groupMember.avatarUrl,
+ 36, 36, 'crop',
+ );
+
+ const BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
+ const avatar = (
+
+ );
+
+ const groupMemberName = (
+ this.props.groupMember.displayname || this.props.groupMember.userId
+ );
+
+ const EmojiText = sdk.getComponent('elements.EmojiText');
+ return (
+
+
+
+
+
+
+ { avatar }
+
+
+ { groupMemberName }
+
+
+
+ { this.props.groupMember.userId }
+
+
+
+ { adminTools }
+
+
+ );
+ },
+}));
diff --git a/src/components/views/groups/GroupMemberList.js b/src/components/views/groups/GroupMemberList.js
new file mode 100644
index 0000000000..6a257259f9
--- /dev/null
+++ b/src/components/views/groups/GroupMemberList.js
@@ -0,0 +1,155 @@
+/*
+Copyright 2017 Vector Creations Ltd.
+Copyright 2017 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 { _t } from '../../../languageHandler';
+import sdk from '../../../index';
+import { groupMemberFromApiObject } from '../../../groups';
+import GeminiScrollbar from 'react-gemini-scrollbar';
+import PropTypes from 'prop-types';
+import withMatrixClient from '../../../wrappers/withMatrixClient';
+
+const INITIAL_LOAD_NUM_MEMBERS = 30;
+
+export default withMatrixClient(React.createClass({
+ displayName: 'GroupMemberList',
+
+ propTypes: {
+ matrixClient: PropTypes.object.isRequired,
+ groupId: PropTypes.string.isRequired,
+ },
+
+ getInitialState: function() {
+ return {
+ fetching: false,
+ members: null,
+ truncateAt: INITIAL_LOAD_NUM_MEMBERS,
+ };
+ },
+
+ componentWillMount: function() {
+ this._unmounted = false;
+ this._fetchMembers();
+ },
+
+ _fetchMembers: function() {
+ this.setState({fetching: true});
+ this.props.matrixClient.getGroupUsers(this.props.groupId).then((result) => {
+ this.setState({
+ members: result.chunk.map((apiMember) => {
+ return groupMemberFromApiObject(apiMember);
+ }),
+ fetching: false,
+ });
+ }).catch((e) => {
+ this.setState({fetching: false});
+ console.error("Failed to get group member list: " + e);
+ });
+ },
+
+ _createOverflowTile: function(overflowCount, totalCount) {
+ // For now we'll pretend this is any entity. It should probably be a separate tile.
+ const EntityTile = sdk.getComponent("rooms.EntityTile");
+ const BaseAvatar = sdk.getComponent("avatars.BaseAvatar");
+ const text = _t("and %(count)s others...", { count: overflowCount });
+ return (
+
+ } name={text} presenceState="online" suppressOnHover={true}
+ onClick={this._showFullMemberList} />
+ );
+ },
+
+ _showFullMemberList: function() {
+ this.setState({
+ truncateAt: -1,
+ });
+ },
+
+ onSearchQueryChanged: function(ev) {
+ this.setState({ searchQuery: ev.target.value });
+ },
+
+ makeGroupMemberTiles: function(query) {
+ const GroupMemberTile = sdk.getComponent("groups.GroupMemberTile");
+ query = (query || "").toLowerCase();
+
+ let memberList = this.state.members;
+ if (query) {
+ memberList = memberList.filter((m) => {
+ const matchesName = m.displayname.toLowerCase().indexOf(query) !== -1;
+ const matchesId = m.userId.toLowerCase().includes(query);
+
+ if (!matchesName && !matchesId) {
+ return false;
+ }
+
+ return true;
+ });
+ }
+
+ memberList = memberList.map((m) => {
+ return (
+
+ );
+ });
+
+ memberList.sort((a, b) => {
+ // TODO: should put admins at the top: we don't yet have that info
+ if (a < b) {
+ return -1;
+ } else if (a > b) {
+ return 1;
+ } else {
+ return 0;
+ }
+ });
+
+ return memberList;
+ },
+
+ render: function() {
+ if (this.state.fetching) {
+ const Spinner = sdk.getComponent("elements.Spinner");
+ return (
+
+
);
+ } else if (this.state.members === null) {
+ return null;
+ }
+
+ const inputBox = (
+
+ );
+
+ const TruncatedList = sdk.getComponent("elements.TruncatedList");
+ return (
+
+ { inputBox }
+
+
+ { this.makeGroupMemberTiles(this.state.searchQuery) }
+
+
+
+ );
+ },
+}));
diff --git a/src/components/views/groups/GroupMemberTile.js b/src/components/views/groups/GroupMemberTile.js
new file mode 100644
index 0000000000..f40c7ed1c5
--- /dev/null
+++ b/src/components/views/groups/GroupMemberTile.js
@@ -0,0 +1,70 @@
+/*
+Copyright 2017 Vector Creations Ltd
+Copyright 2017 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 dis from '../../../dispatcher';
+import { GroupMemberType } from '../../../groups';
+import withMatrixClient from '../../../wrappers/withMatrixClient';
+
+export default withMatrixClient(React.createClass({
+ displayName: 'GroupMemberTile',
+
+ propTypes: {
+ matrixClient: PropTypes.object,
+ groupId: PropTypes.string.isRequired,
+ member: GroupMemberType.isRequired,
+ },
+
+ getInitialState: function() {
+ return {};
+ },
+
+ onClick: function(e) {
+ dis.dispatch({
+ action: 'view_group_user',
+ member: this.props.member,
+ groupId: this.props.groupId,
+ });
+ },
+
+ render: function() {
+ const BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
+ const EntityTile = sdk.getComponent('rooms.EntityTile');
+
+ const name = this.props.member.displayname || this.props.member.userId;
+ const avatarUrl = this.props.matrixClient.mxcUrlToHttp(
+ this.props.member.avatarUrl,
+ 36, 36, 'crop',
+ );
+
+ const av = (
+
+ );
+
+ return (
+
+ );
+ },
+}));
diff --git a/src/components/views/groups/GroupRoomList.js b/src/components/views/groups/GroupRoomList.js
new file mode 100644
index 0000000000..4ff68a7f4d
--- /dev/null
+++ b/src/components/views/groups/GroupRoomList.js
@@ -0,0 +1,146 @@
+/*
+Copyright 2017 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 { _t } from '../../../languageHandler';
+import sdk from '../../../index';
+import { groupRoomFromApiObject } from '../../../groups';
+import GroupStoreCache from '../../../stores/GroupStoreCache';
+import GeminiScrollbar from 'react-gemini-scrollbar';
+import PropTypes from 'prop-types';
+import {MatrixClient} from 'matrix-js-sdk';
+
+const INITIAL_LOAD_NUM_ROOMS = 30;
+
+export default React.createClass({
+ contextTypes: {
+ matrixClient: React.PropTypes.instanceOf(MatrixClient).isRequired,
+ },
+
+ propTypes: {
+ groupId: PropTypes.string.isRequired,
+ },
+
+ getInitialState: function() {
+ return {
+ rooms: null,
+ truncateAt: INITIAL_LOAD_NUM_ROOMS,
+ searchQuery: "",
+ };
+ },
+
+ componentWillMount: function() {
+ this._unmounted = false;
+ this._initGroupStore(this.props.groupId);
+ },
+
+ _initGroupStore: function(groupId) {
+ this._groupStore = GroupStoreCache.getGroupStore(this.context.matrixClient, groupId);
+ this._groupStore.on('update', () => {
+ this._fetchRooms();
+ });
+ this._groupStore.on('error', (err) => {
+ console.error('Error in group store (listened to by GroupRoomList)', err);
+ this.setState({
+ rooms: null,
+ });
+ });
+ this._fetchRooms();
+ },
+
+ _fetchRooms: function() {
+ if (this._unmounted) return;
+ this.setState({
+ rooms: this._groupStore.getGroupRooms().map((apiRoom) => {
+ return groupRoomFromApiObject(apiRoom);
+ }),
+ });
+ },
+
+ _createOverflowTile: function(overflowCount, totalCount) {
+ // For now we'll pretend this is any entity. It should probably be a separate tile.
+ const EntityTile = sdk.getComponent("rooms.EntityTile");
+ const BaseAvatar = sdk.getComponent("avatars.BaseAvatar");
+ const text = _t("and %(count)s others...", { count: overflowCount });
+ return (
+
+ } name={text} presenceState="online" suppressOnHover={true}
+ onClick={this._showFullRoomList} />
+ );
+ },
+
+ _showFullRoomList: function() {
+ this.setState({
+ truncateAt: -1,
+ });
+ },
+
+ onSearchQueryChanged: function(ev) {
+ this.setState({ searchQuery: ev.target.value });
+ },
+
+ makeGroupRoomTiles: function(query) {
+ const GroupRoomTile = sdk.getComponent("groups.GroupRoomTile");
+ query = (query || "").toLowerCase();
+
+ let roomList = this.state.rooms;
+ if (query) {
+ roomList = roomList.filter((room) => {
+ const matchesName = (room.name || "").toLowerCase().include(query);
+ const matchesAlias = (room.canonicalAlias || "").toLowerCase().includes(query);
+ return matchesName || matchesAlias;
+ });
+ }
+
+ roomList = roomList.map((groupRoom, index) => {
+ return (
+
+ );
+ });
+
+ return roomList;
+ },
+
+ render: function() {
+ if (this.state.rooms === null) {
+ return null;
+ }
+
+ const inputBox = (
+
+ );
+
+ const TruncatedList = sdk.getComponent("elements.TruncatedList");
+ return (
+
+ { inputBox }
+
+
+ { this.makeGroupRoomTiles(this.state.searchQuery) }
+
+
+
+ );
+ },
+});
diff --git a/src/components/views/groups/GroupRoomTile.js b/src/components/views/groups/GroupRoomTile.js
new file mode 100644
index 0000000000..bb0fdb03f4
--- /dev/null
+++ b/src/components/views/groups/GroupRoomTile.js
@@ -0,0 +1,136 @@
+/*
+Copyright 2017 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 {MatrixClient} from 'matrix-js-sdk';
+import { _t } from '../../../languageHandler';
+import PropTypes from 'prop-types';
+import sdk from '../../../index';
+import dis from '../../../dispatcher';
+import { GroupRoomType } from '../../../groups';
+import GroupStoreCache from '../../../stores/GroupStoreCache';
+import Modal from '../../../Modal';
+
+const GroupRoomTile = React.createClass({
+ displayName: 'GroupRoomTile',
+
+ propTypes: {
+ groupId: PropTypes.string.isRequired,
+ groupRoom: GroupRoomType.isRequired,
+ },
+
+ getInitialState: function() {
+ return {
+ name: this.calculateRoomName(this.props.groupRoom),
+ };
+ },
+
+ componentWillReceiveProps: function(newProps) {
+ this.setState({
+ name: this.calculateRoomName(newProps.groupRoom),
+ });
+ },
+
+ calculateRoomName: function(groupRoom) {
+ return groupRoom.name || groupRoom.canonicalAlias || _t("Unnamed Room");
+ },
+
+ removeRoomFromGroup: function() {
+ const groupId = this.props.groupId;
+ const groupStore = GroupStoreCache.getGroupStore(this.context.matrixClient, groupId);
+ const roomName = this.state.name;
+ const roomId = this.props.groupRoom.roomId;
+ groupStore.removeRoomFromGroup(roomId)
+ .catch((err) => {
+ console.error(`Error whilst removing ${roomId} from ${groupId}`, err);
+ const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
+ Modal.createTrackedDialog('Failed to remove room from group', '', ErrorDialog, {
+ title: _t("Failed to remove room from group"),
+ description: _t("Failed to remove '%(roomName)s' from %(groupId)s", {groupId, roomName}),
+ });
+ });
+ },
+
+ onClick: function(e) {
+ let roomId;
+ let roomAlias;
+ if (this.props.groupRoom.canonicalAlias) {
+ roomAlias = this.props.groupRoom.canonicalAlias;
+ } else {
+ roomId = this.props.groupRoom.roomId;
+ }
+ dis.dispatch({
+ action: 'view_room',
+ room_id: roomId,
+ room_alias: roomAlias,
+ });
+ },
+
+ onDeleteClick: function(e) {
+ const groupId = this.props.groupId;
+ const roomName = this.state.name;
+ e.preventDefault();
+ e.stopPropagation();
+ const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
+ Modal.createTrackedDialog('Confirm removal of group from room', '', QuestionDialog, {
+ title: _t("Are you sure you want to remove '%(roomName)s' from %(groupId)s?", {roomName, groupId}),
+ description: _t("Removing a room from the group will also remove it from the group page."),
+ button: _t("Remove"),
+ onFinished: (success) => {
+ if (success) {
+ this.removeRoomFromGroup();
+ }
+ },
+ });
+ },
+
+ render: function() {
+ const BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
+ const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
+ const avatarUrl = this.context.matrixClient.mxcUrlToHttp(
+ this.props.groupRoom.avatarUrl,
+ 36, 36, 'crop',
+ );
+
+ const av = (
+
+ );
+
+ return (
+
+
+ { av }
+
+
+ { this.state.name }
+
+
+
+
+
+ );
+ },
+});
+
+GroupRoomTile.contextTypes = {
+ matrixClient: React.PropTypes.instanceOf(MatrixClient).isRequired,
+};
+
+
+export default GroupRoomTile;
diff --git a/src/components/views/login/CaptchaForm.js b/src/components/views/login/CaptchaForm.js
index d24990f94d..cf814b0a6e 100644
--- a/src/components/views/login/CaptchaForm.js
+++ b/src/components/views/login/CaptchaForm.js
@@ -20,7 +20,7 @@ import React from 'react';
import ReactDOM from 'react-dom';
import { _t, _tJsx } from '../../../languageHandler';
-var DIV_ID = 'mx_recaptcha';
+const DIV_ID = 'mx_recaptcha';
/**
* A pure UI component which displays a captcha form.
@@ -60,9 +60,9 @@ module.exports = React.createClass({
} else {
console.log("Loading recaptcha script...");
window.mx_on_recaptcha_loaded = () => {this._onCaptchaLoaded();};
- var protocol = global.location.protocol;
+ const protocol = global.location.protocol;
if (protocol === "file:") {
- var warning = document.createElement('div');
+ const warning = document.createElement('div');
// XXX: fix hardcoded app URL. Better solutions include:
// * jumping straight to a hosted captcha page (but we don't support that yet)
// * embedding the captcha in an iframe (if that works)
@@ -72,11 +72,10 @@ module.exports = React.createClass({
/(.*?)<\/a>/,
(sub) => { return { sub } ; }), warning);
this.refs.recaptchaContainer.appendChild(warning);
- }
- else {
- var scriptTag = document.createElement('script');
+ } else {
+ const scriptTag = document.createElement('script');
scriptTag.setAttribute(
- 'src', protocol+"//www.google.com/recaptcha/api.js?onload=mx_on_recaptcha_loaded&render=explicit"
+ 'src', protocol+"//www.google.com/recaptcha/api.js?onload=mx_on_recaptcha_loaded&render=explicit",
);
this.refs.recaptchaContainer.appendChild(scriptTag);
}
@@ -93,7 +92,7 @@ module.exports = React.createClass({
throw new Error("Recaptcha did not load successfully");
}
- var publicKey = this.props.sitePublicKey;
+ const publicKey = this.props.sitePublicKey;
if (!publicKey) {
console.error("No public key for recaptcha!");
throw new Error(
@@ -130,18 +129,18 @@ module.exports = React.createClass({
if (this.state.errorText) {
error = (
- {this.state.errorText}
+ { this.state.errorText }
);
}
return (
- {_t("This Home Server would like to make sure you are not a robot")}
-
+ { _t("This Home Server would like to make sure you are not a robot") }
+
- {error}
+ { error }
);
- }
+ },
});
diff --git a/src/components/views/login/CasLogin.js b/src/components/views/login/CasLogin.js
index 96e37875be..49d121687d 100644
--- a/src/components/views/login/CasLogin.js
+++ b/src/components/views/login/CasLogin.js
@@ -29,9 +29,9 @@ module.exports = React.createClass({
render: function() {
return (
- {_t("Sign in with CAS")}
+ { _t("Sign in with CAS") }
);
- }
+ },
});
diff --git a/src/components/views/login/CountryDropdown.js b/src/components/views/login/CountryDropdown.js
index 7024db339c..d8695bb1c1 100644
--- a/src/components/views/login/CountryDropdown.js
+++ b/src/components/views/login/CountryDropdown.js
@@ -69,7 +69,7 @@ export default class CountryDropdown extends React.Component {
}
_flagImgForIso2(iso2) {
- return ;
+ return ;
}
_getShortOption(iso2) {
@@ -111,8 +111,8 @@ export default class CountryDropdown extends React.Component {
const options = displayedCountries.map((country) => {
return
- {this._flagImgForIso2(country.iso2)}
- {country.name} (+{country.prefix})
+ { this._flagImgForIso2(country.iso2) }
+ { country.name } (+{ country.prefix })
;
});
@@ -123,9 +123,9 @@ export default class CountryDropdown extends React.Component {
return
- {options}
+ { options }
;
}
}
@@ -137,4 +137,5 @@ CountryDropdown.propTypes = {
showPrefix: React.PropTypes.bool,
onOptionChange: React.PropTypes.func.isRequired,
value: React.PropTypes.string,
+ disabled: React.PropTypes.bool,
};
diff --git a/src/components/views/login/CustomServerDialog.js b/src/components/views/login/CustomServerDialog.js
index f5c5c84e63..474a4097d1 100644
--- a/src/components/views/login/CustomServerDialog.js
+++ b/src/components/views/login/CustomServerDialog.js
@@ -24,27 +24,27 @@ module.exports = React.createClass({
return (
- {_t("Custom Server Options")}
+ { _t("Custom Server Options") }
- {_t("You can use the custom server options to sign into other Matrix " +
- "servers by specifying a different Home server URL.")}
-
- {_t("This allows you to use this app with an existing Matrix account on " +
- "a different home server.")}
-
-
- {_t("You can also set a custom identity server but this will typically prevent " +
- "interaction with users based on email address.")}
+ { _t("You can use the custom server options to sign into other Matrix " +
+ "servers by specifying a different Home server URL.") }
+
+ { _t("This allows you to use this app with an existing Matrix account on " +
+ "a different home server.") }
+
+
+ { _t("You can also set a custom identity server but this will typically prevent " +
+ "interaction with users based on email address.") }
- {_t("Dismiss")}
+ { _t("Dismiss") }
);
- }
+ },
});
diff --git a/src/components/views/login/InteractiveAuthEntryComponents.js b/src/components/views/login/InteractiveAuthEntryComponents.js
index ae8a087fdd..4c53c23f76 100644
--- a/src/components/views/login/InteractiveAuthEntryComponents.js
+++ b/src/components/views/login/InteractiveAuthEntryComponents.js
@@ -129,8 +129,8 @@ export const PasswordAuthEntry = React.createClass({
return (
-
{_t("To continue, please enter your password.")}
-
{_t("Password:")}
+
{ _t("To continue, please enter your password.") }
+
{ _t("Password:") }
- {this.props.errorText}
+ { this.props.errorText }
);
@@ -178,14 +178,14 @@ export const RecaptchaAuthEntry = React.createClass({
}
const CaptchaForm = sdk.getComponent("views.login.CaptchaForm");
- var sitePublicKey = this.props.stageParams.public_key;
+ const sitePublicKey = this.props.stageParams.public_key;
return (
- {this.props.errorText}
+ { this.props.errorText }
);
@@ -256,8 +256,8 @@ export const EmailIdentityAuthEntry = React.createClass({
} else {
return (
-
{_t("An email has been sent to")} {this.props.inputs.emailAddress}
-
{_t("Please check your email to continue registration.")}
+
{ _t("An email has been sent to") } { this.props.inputs.emailAddress }
+
{ _t("Please check your email to continue registration.") }
);
}
@@ -333,12 +333,12 @@ export const MsisdnAuthEntry = React.createClass({
});
this.props.matrixClient.submitMsisdnToken(
- this._sid, this.props.clientSecret, this.state.token
+ this._sid, this.props.clientSecret, this.state.token,
).then((result) => {
if (result.success) {
const idServerParsedUrl = url.parse(
this.props.matrixClient.getIdentityServerUrl(),
- )
+ );
this.props.submitAuthDict({
type: MsisdnAuthEntry.LOGIN_TYPE,
threepid_creds: {
@@ -370,8 +370,8 @@ export const MsisdnAuthEntry = React.createClass({
});
return (
-
{_t("A text message has been sent to")} +{this._msisdn}
-
{_t("Please enter the code it contains:")}
+
{ _t("A text message has been sent to") } +{ this._msisdn }
+
{ _t("Please enter the code it contains:") }
- {this.state.errorText}
+ { this.state.errorText }
@@ -421,9 +421,9 @@ export const FallbackAuthEntry = React.createClass({
},
_onShowFallbackClick: function() {
- var url = this.props.matrixClient.getFallbackAuthUrl(
+ const url = this.props.matrixClient.getFallbackAuthUrl(
this.props.loginType,
- this.props.authSessionId
+ this.props.authSessionId,
);
this._popupWindow = window.open(url);
},
@@ -440,9 +440,9 @@ export const FallbackAuthEntry = React.createClass({
render: function() {
return (
);
@@ -457,7 +457,7 @@ const AuthEntryComponents = [
];
export function getEntryComponentForLoginType(loginType) {
- for (var c of AuthEntryComponents) {
+ for (const c of AuthEntryComponents) {
if (c.LOGIN_TYPE == loginType) {
return c;
}
diff --git a/src/components/views/login/LoginFooter.js b/src/components/views/login/LoginFooter.js
index 8bdec71685..392d36e288 100644
--- a/src/components/views/login/LoginFooter.js
+++ b/src/components/views/login/LoginFooter.js
@@ -25,7 +25,7 @@ module.exports = React.createClass({
render: function() {
return (
);
},
diff --git a/src/components/views/login/LoginHeader.js b/src/components/views/login/LoginHeader.js
index 3ee3cbea2e..cd1f9c6a28 100644
--- a/src/components/views/login/LoginHeader.js
+++ b/src/components/views/login/LoginHeader.js
@@ -16,7 +16,7 @@ limitations under the License.
'use strict';
-var React = require('react');
+const React = require('react');
module.exports = React.createClass({
displayName: 'LoginHeader',
@@ -27,5 +27,5 @@ module.exports = React.createClass({
Matrix
);
- }
+ },
});
diff --git a/src/components/views/login/PasswordLogin.js b/src/components/views/login/PasswordLogin.js
index 9f855616fc..484ee01f4e 100644
--- a/src/components/views/login/PasswordLogin.js
+++ b/src/components/views/login/PasswordLogin.js
@@ -94,7 +94,7 @@ class PasswordLogin extends React.Component {
onLoginTypeChange(loginType) {
this.setState({
loginType: loginType,
- username: "" // Reset because email and username use the same state
+ username: "", // Reset because email and username use the same state
});
}
@@ -116,11 +116,17 @@ class PasswordLogin extends React.Component {
this.props.onPasswordChanged(ev.target.value);
}
- renderLoginField(loginType) {
+ renderLoginField(loginType, disabled) {
+ const classes = {
+ mx_Login_field: true,
+ mx_Login_field_disabled: disabled,
+ };
+
switch(loginType) {
case PasswordLogin.LOGIN_FIELD_EMAIL:
+ classes.mx_Login_email = true;
return ;
case PasswordLogin.LOGIN_FIELD_MXID:
+ classes.mx_Login_username = true;
return ;
case PasswordLogin.LOGIN_FIELD_PHONE:
const CountryDropdown = sdk.getComponent('views.login.CountryDropdown');
+ classes.mx_Login_phoneNumberField = true;
+ classes.mx_Login_field_has_prefix = true;
return
;
}
}
render() {
- var forgotPasswordJsx;
+ let forgotPasswordJsx;
if (this.props.onForgotPasswordClick) {
forgotPasswordJsx = (
@@ -177,14 +190,25 @@ class PasswordLogin extends React.Component {
);
}
+ let matrixIdText = '';
+ if (this.props.hsUrl) {
+ try {
+ const parsedHsUrl = new URL(this.props.hsUrl);
+ matrixIdText = _t('%(serverName)s Matrix ID', {serverName: parsedHsUrl.hostname});
+ } catch (e) {
+ // pass
+ }
+ }
+
const pwFieldClass = classNames({
mx_Login_field: true,
+ mx_Login_field_disabled: matrixIdText === '',
error: this.props.loginIncorrect,
});
const Dropdown = sdk.getComponent('elements.Dropdown');
- const loginField = this.renderLoginField(this.state.loginType);
+ const loginField = this.renderLoginField(this.state.loginType, matrixIdText === '');
return (
@@ -194,20 +218,23 @@ class PasswordLogin extends React.Component {
- { _t('my Matrix ID') }
+ { matrixIdText }
{ _t('Email address') }
{ _t('Phone') }
- {loginField}
+ { loginField }
{this._passwordField = e;}} type="password"
name="password"
value={this.state.password} onChange={this.onPasswordChanged}
- placeholder={ _t('Password') } />
+ placeholder={_t('Password')}
+ disabled={matrixIdText === ''}
+ />
- {forgotPasswordJsx}
-
+ { forgotPasswordJsx }
+
);
diff --git a/src/components/views/login/RegistrationForm.js b/src/components/views/login/RegistrationForm.js
index d5b7bcf46a..9c7c75b125 100644
--- a/src/components/views/login/RegistrationForm.js
+++ b/src/components/views/login/RegistrationForm.js
@@ -64,7 +64,7 @@ module.exports = React.createClass({
minPasswordLength: 6,
onError: function(e) {
console.error(e);
- }
+ },
};
},
@@ -91,16 +91,16 @@ module.exports = React.createClass({
this.validateField(FIELD_PHONE_NUMBER);
this.validateField(FIELD_EMAIL);
- var self = this;
+ const self = this;
if (this.allFieldsValid()) {
if (this.refs.email.value == '') {
- var QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
+ const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
Modal.createTrackedDialog('If you don\'t specify an email address...', '', QuestionDialog, {
title: _t("Warning!"),
description:
- {_t("If you don't specify an email address, you won't be able to reset your password. " +
- "Are you sure?")}
+ { _t("If you don't specify an email address, you won't be able to reset your password. " +
+ "Are you sure?") }
,
button: _t("Continue"),
onFinished: function(confirmed) {
@@ -116,8 +116,8 @@ module.exports = React.createClass({
},
_doSubmit: function(ev) {
- let email = this.refs.email.value.trim();
- var promise = this.props.onRegisterClick({
+ const email = this.refs.email.value.trim();
+ const promise = this.props.onRegisterClick({
username: this.refs.username.value.trim(),
password: this.refs.password.value.trim(),
email: email,
@@ -138,8 +138,8 @@ module.exports = React.createClass({
* they were validated.
*/
allFieldsValid: function() {
- var keys = Object.keys(this.state.fieldValid);
- for (var i = 0; i < keys.length; ++i) {
+ const keys = Object.keys(this.state.fieldValid);
+ for (let i = 0; i < keys.length; ++i) {
if (this.state.fieldValid[keys[i]] == false) {
return false;
}
@@ -152,8 +152,8 @@ module.exports = React.createClass({
},
validateField: function(field_id) {
- var pwd1 = this.refs.password.value.trim();
- var pwd2 = this.refs.passwordConfirm.value.trim();
+ const pwd1 = this.refs.password.value.trim();
+ const pwd2 = this.refs.passwordConfirm.value.trim();
switch (field_id) {
case FIELD_EMAIL:
@@ -162,7 +162,7 @@ module.exports = React.createClass({
const matchingTeam = this.props.teamsConfig.teams.find(
(team) => {
return email.split('@').pop() === team.domain;
- }
+ },
) || null;
this.setState({
selectedTeam: matchingTeam,
@@ -191,13 +191,13 @@ module.exports = React.createClass({
this.markFieldValid(
field_id,
false,
- "RegistrationForm.ERR_USERNAME_INVALID"
+ "RegistrationForm.ERR_USERNAME_INVALID",
);
} else if (username == '') {
this.markFieldValid(
field_id,
false,
- "RegistrationForm.ERR_USERNAME_BLANK"
+ "RegistrationForm.ERR_USERNAME_BLANK",
);
} else {
this.markFieldValid(field_id, true);
@@ -208,13 +208,13 @@ module.exports = React.createClass({
this.markFieldValid(
field_id,
false,
- "RegistrationForm.ERR_PASSWORD_MISSING"
+ "RegistrationForm.ERR_PASSWORD_MISSING",
);
} else if (pwd1.length < this.props.minPasswordLength) {
this.markFieldValid(
field_id,
false,
- "RegistrationForm.ERR_PASSWORD_LENGTH"
+ "RegistrationForm.ERR_PASSWORD_LENGTH",
);
} else {
this.markFieldValid(field_id, true);
@@ -223,14 +223,14 @@ module.exports = React.createClass({
case FIELD_PASSWORD_CONFIRM:
this.markFieldValid(
field_id, pwd1 == pwd2,
- "RegistrationForm.ERR_PASSWORD_MISMATCH"
+ "RegistrationForm.ERR_PASSWORD_MISMATCH",
);
break;
}
},
markFieldValid: function(field_id, val, error_code) {
- var fieldValid = this.state.fieldValid;
+ const fieldValid = this.state.fieldValid;
fieldValid[field_id] = val;
this.setState({fieldValid: fieldValid});
if (!val) {
@@ -271,7 +271,7 @@ module.exports = React.createClass({
},
render: function() {
- var self = this;
+ const self = this;
const emailSection = (
@@ -280,7 +280,7 @@ module.exports = React.createClass({
defaultValue={this.props.defaultEmail}
className={this._classForField(FIELD_EMAIL, 'mx_Login_field')}
onBlur={function() {self.validateField(FIELD_EMAIL);}}
- value={self.state.email}/>
+ value={self.state.email} />
);
let belowEmailSection;
@@ -291,7 +291,7 @@ module.exports = React.createClass({
Sorry, but your university is not registered with us just yet.
Email us on
- {this.props.teamsConfig.supportEmail}
+ { this.props.teamsConfig.supportEmail }
to get your university signed up. Or continue to register with Riot to enjoy our open source platform.
@@ -299,7 +299,7 @@ module.exports = React.createClass({
} else if (this.state.selectedTeam) {
belowEmailSection = (
- {_t("You are registering with %(SelectedTeamName)s", {SelectedTeamName: this.state.selectedTeam.name})}
+ { _t("You are registering with %(SelectedTeamName)s", {SelectedTeamName: this.state.selectedTeam.name}) }
);
}
@@ -321,7 +321,7 @@ module.exports = React.createClass({
FIELD_PHONE_NUMBER,
'mx_Login_phoneNumberField',
'mx_Login_field',
- 'mx_Login_field_has_prefix'
+ 'mx_Login_field_has_prefix',
)}
onBlur={function() {self.validateField(FIELD_PHONE_NUMBER);}}
value={self.state.phoneNumber}
@@ -333,16 +333,16 @@ module.exports = React.createClass({
);
- let placeholderUserName = _t("User name");
+ const placeholderUserName = _t("User name");
return (
);
- }
+ },
});
diff --git a/src/components/views/login/ServerConfig.js b/src/components/views/login/ServerConfig.js
index 0042ab5e9f..d53c7238b1 100644
--- a/src/components/views/login/ServerConfig.js
+++ b/src/components/views/login/ServerConfig.js
@@ -16,9 +16,9 @@ limitations under the License.
'use strict';
-var React = require('react');
-var Modal = require('../../../Modal');
-var sdk = require('../../../index');
+const React = require('react');
+const Modal = require('../../../Modal');
+const sdk = require('../../../index');
import { _t } from '../../../languageHandler';
/**
@@ -45,7 +45,7 @@ module.exports = React.createClass({
customIsUrl: React.PropTypes.string,
withToggleButton: React.PropTypes.bool,
- delayTimeMs: React.PropTypes.number // time to wait before invoking onChanged
+ delayTimeMs: React.PropTypes.number, // time to wait before invoking onChanged
},
getDefaultProps: function() {
@@ -54,7 +54,7 @@ module.exports = React.createClass({
customHsUrl: "",
customIsUrl: "",
withToggleButton: false,
- delayTimeMs: 0
+ delayTimeMs: 0,
};
},
@@ -65,18 +65,18 @@ module.exports = React.createClass({
// if withToggleButton is false, then show the config all the time given we have no way otherwise of making it visible
configVisible: !this.props.withToggleButton ||
(this.props.customHsUrl !== this.props.defaultHsUrl) ||
- (this.props.customIsUrl !== this.props.defaultIsUrl)
+ (this.props.customIsUrl !== this.props.defaultIsUrl),
};
},
onHomeserverChanged: function(ev) {
this.setState({hs_url: ev.target.value}, function() {
this._hsTimeoutId = this._waitThenInvoke(this._hsTimeoutId, function() {
- var hsUrl = this.state.hs_url.trim().replace(/\/$/, "");
+ let hsUrl = this.state.hs_url.trim().replace(/\/$/, "");
if (hsUrl === "") hsUrl = this.props.defaultHsUrl;
this.props.onServerConfigChange({
- hsUrl : this.state.hs_url,
- isUrl : this.state.is_url,
+ hsUrl: this.state.hs_url,
+ isUrl: this.state.is_url,
});
});
});
@@ -85,11 +85,11 @@ module.exports = React.createClass({
onIdentityServerChanged: function(ev) {
this.setState({is_url: ev.target.value}, function() {
this._isTimeoutId = this._waitThenInvoke(this._isTimeoutId, function() {
- var isUrl = this.state.is_url.trim().replace(/\/$/, "");
+ let isUrl = this.state.is_url.trim().replace(/\/$/, "");
if (isUrl === "") isUrl = this.props.defaultIsUrl;
this.props.onServerConfigChange({
- hsUrl : this.state.hs_url,
- isUrl : this.state.is_url,
+ hsUrl: this.state.hs_url,
+ isUrl: this.state.is_url,
});
});
});
@@ -104,32 +104,31 @@ module.exports = React.createClass({
onServerConfigVisibleChange: function(visible, ev) {
this.setState({
- configVisible: visible
+ configVisible: visible,
});
if (!visible) {
this.props.onServerConfigChange({
- hsUrl : this.props.defaultHsUrl,
- isUrl : this.props.defaultIsUrl,
+ hsUrl: this.props.defaultHsUrl,
+ isUrl: this.props.defaultIsUrl,
});
- }
- else {
+ } else {
this.props.onServerConfigChange({
- hsUrl : this.state.hs_url,
- isUrl : this.state.is_url,
+ hsUrl: this.state.hs_url,
+ isUrl: this.state.is_url,
});
}
},
showHelpPopup: function() {
- var CustomServerDialog = sdk.getComponent('login.CustomServerDialog');
+ const CustomServerDialog = sdk.getComponent('login.CustomServerDialog');
Modal.createTrackedDialog('Custom Server Dialog', '', CustomServerDialog);
},
render: function() {
- var serverConfigStyle = {};
+ const serverConfigStyle = {};
serverConfigStyle.display = this.state.configVisible ? 'block' : 'none';
- var toggleButton;
+ let toggleButton;
if (this.props.withToggleButton) {
toggleButton = (
@@ -137,14 +136,14 @@ module.exports = React.createClass({
checked={!this.state.configVisible}
onChange={this.onServerConfigVisibleChange.bind(this, false)} />
- {_t("Default server")}
+ { _t("Default server") }
- {_t("Custom server")}
+ { _t("Custom server") }
);
@@ -152,11 +151,11 @@ module.exports = React.createClass({
return (
- {toggleButton}
+ { toggleButton }
);
- }
+ },
});
diff --git a/src/components/views/messages/MAudioBody.js b/src/components/views/messages/MAudioBody.js
index 52c1341e60..ab53918987 100644
--- a/src/components/views/messages/MAudioBody.js
+++ b/src/components/views/messages/MAudioBody.js
@@ -35,7 +35,7 @@ export default class MAudioBody extends React.Component {
}
onPlayToggle() {
this.setState({
- playing: !this.state.playing
+ playing: !this.state.playing,
});
}
@@ -49,9 +49,9 @@ export default class MAudioBody extends React.Component {
}
componentDidMount() {
- var content = this.props.mxEvent.getContent();
+ const content = this.props.mxEvent.getContent();
if (content.file !== undefined && this.state.decryptedUrl === null) {
- var decryptedBlob;
+ let decryptedBlob;
decryptFile(content.file).then(function(blob) {
decryptedBlob = blob;
return readBlobAsDataUri(decryptedBlob);
@@ -70,14 +70,13 @@ export default class MAudioBody extends React.Component {
}
render() {
-
const content = this.props.mxEvent.getContent();
if (this.state.error !== null) {
return (
-
- {_t("Error decrypting audio")}
+
+ { _t("Error decrypting audio") }
);
}
@@ -89,7 +88,7 @@ export default class MAudioBody extends React.Component {
// Not sure how tall the audio player is so not sure how tall it should actually be.
return (
-
+
);
}
diff --git a/src/components/views/messages/MFileBody.js b/src/components/views/messages/MFileBody.js
index 53c36f234c..8eedf4e1fe 100644
--- a/src/components/views/messages/MFileBody.js
+++ b/src/components/views/messages/MFileBody.js
@@ -28,10 +28,10 @@ import Modal from '../../../Modal';
// A cached tinted copy of "img/download.svg"
-var tintedDownloadImageURL;
+let tintedDownloadImageURL;
// Track a list of mounted MFileBody instances so that we can update
// the "img/download.svg" when the tint changes.
-var nextMountId = 0;
+let nextMountId = 0;
const mounts = {};
/**
@@ -169,11 +169,11 @@ function computedStyle(element) {
return "";
}
const style = window.getComputedStyle(element, null);
- var cssText = style.cssText;
+ let cssText = style.cssText;
if (cssText == "") {
// Firefox doesn't implement ".cssText" for computed styles.
// https://bugzilla.mozilla.org/show_bug.cgi?id=137687
- for (var i = 0; i < style.length; i++) {
+ for (let i = 0; i < style.length; i++) {
cssText += style[i] + ":";
cssText += style.getPropertyValue(style[i]) + ";";
}
@@ -202,7 +202,7 @@ module.exports = React.createClass({
* @return {string} the human readable link text for the attachment.
*/
presentableTextForFile: function(content) {
- var linkText = _t("Attachment");
+ let linkText = _t("Attachment");
if (content.body && content.body.length > 0) {
// The content body should be the name of the file including a
// file extension.
@@ -270,7 +270,7 @@ module.exports = React.createClass({
// Need to decrypt the attachment
// Wait for the user to click on the link before downloading
// and decrypting the attachment.
- var decrypting = false;
+ let decrypting = false;
const decrypt = () => {
if (decrypting) {
return false;
@@ -328,14 +328,14 @@ module.exports = React.createClass({
- {/*
+ { /*
* Add dummy copy of the "a" tag
* We'll use it to learn how the download link
* would have been styled if it was rendered inline.
- */}
-
+ */ }
+
-
+
);
@@ -356,13 +356,12 @@ module.exports = React.createClass({
);
- }
- else {
+ } else {
return (
@@ -370,7 +369,7 @@ module.exports = React.createClass({
);
}
} else {
- var extra = text ? (': ' + text) : '';
+ const extra = text ? (': ' + text) : '';
return
{ _t("Invalid file%(extra)s", { extra: extra }) }
;
diff --git a/src/components/views/messages/MImageBody.js b/src/components/views/messages/MImageBody.js
index dcc073c80e..255f562fcd 100644
--- a/src/components/views/messages/MImageBody.js
+++ b/src/components/views/messages/MImageBody.js
@@ -191,8 +191,8 @@ module.exports = React.createClass({
if (this.state.error !== null) {
return (
-
- {_t("Error decrypting image")}
+
+ { _t("Error decrypting image") }
);
}
@@ -210,7 +210,7 @@ module.exports = React.createClass({
}}>
+ }} />
);
@@ -227,7 +227,7 @@ module.exports = React.createClass({
if (thumbUrl) {
return (
-
+
- {_t("Image '%(Body)s' cannot be displayed.", {Body: content.body})}
+ { _t("Image '%(Body)s' cannot be displayed.", {Body: content.body}) }
);
} else {
return (
- {_t("This image cannot be displayed.")}
+ { _t("This image cannot be displayed.") }
);
}
diff --git a/src/components/views/messages/MVideoBody.js b/src/components/views/messages/MVideoBody.js
index 06c233e012..d2205f5c78 100644
--- a/src/components/views/messages/MVideoBody.js
+++ b/src/components/views/messages/MVideoBody.js
@@ -54,13 +54,12 @@ module.exports = React.createClass({
// no scaling needs to be applied
return 1;
}
- var widthMulti = thumbWidth / fullWidth;
- var heightMulti = thumbHeight / fullHeight;
+ const widthMulti = thumbWidth / fullWidth;
+ const heightMulti = thumbHeight / fullHeight;
if (widthMulti < heightMulti) {
// width is the dominant dimension so scaling will be fixed on that
return widthMulti;
- }
- else {
+ } else {
// height is the dominant dimension so scaling will be fixed on that
return heightMulti;
}
@@ -89,15 +88,15 @@ module.exports = React.createClass({
componentDidMount: function() {
const content = this.props.mxEvent.getContent();
if (content.file !== undefined && this.state.decryptedUrl === null) {
- var thumbnailPromise = Promise.resolve(null);
+ let thumbnailPromise = Promise.resolve(null);
if (content.info.thumbnail_file) {
thumbnailPromise = decryptFile(
- content.info.thumbnail_file
+ content.info.thumbnail_file,
).then(function(blob) {
return readBlobAsDataUri(blob);
});
}
- var decryptedBlob;
+ let decryptedBlob;
thumbnailPromise.then((thumbnailUrl) => {
return decryptFile(content.file).then(function(blob) {
decryptedBlob = blob;
@@ -126,8 +125,8 @@ module.exports = React.createClass({
if (this.state.error !== null) {
return (
-
- {_t("Error decrypting video")}
+
+ { _t("Error decrypting video") }
);
}
@@ -144,7 +143,7 @@ module.exports = React.createClass({
"justify-items": "center",
"width": "100%",
}}>
-
+
);
diff --git a/src/components/views/messages/MessageEvent.js b/src/components/views/messages/MessageEvent.js
index 4ac0c0dabb..deda1d8d20 100644
--- a/src/components/views/messages/MessageEvent.js
+++ b/src/components/views/messages/MessageEvent.js
@@ -16,8 +16,8 @@ limitations under the License.
'use strict';
-var React = require('react');
-var sdk = require('../../../index');
+const React = require('react');
+const sdk = require('../../../index');
module.exports = React.createClass({
displayName: 'MessageEvent',
@@ -47,21 +47,21 @@ module.exports = React.createClass({
},
render: function() {
- var UnknownBody = sdk.getComponent('messages.UnknownBody');
+ const UnknownBody = sdk.getComponent('messages.UnknownBody');
- var bodyTypes = {
+ const bodyTypes = {
'm.text': sdk.getComponent('messages.TextualBody'),
'm.notice': sdk.getComponent('messages.TextualBody'),
'm.emote': sdk.getComponent('messages.TextualBody'),
'm.image': sdk.getComponent('messages.MImageBody'),
'm.file': sdk.getComponent('messages.MFileBody'),
'm.audio': sdk.getComponent('messages.MAudioBody'),
- 'm.video': sdk.getComponent('messages.MVideoBody')
+ 'm.video': sdk.getComponent('messages.MVideoBody'),
};
- var content = this.props.mxEvent.getContent();
- var msgtype = content.msgtype;
- var BodyType = UnknownBody;
+ const content = this.props.mxEvent.getContent();
+ const msgtype = content.msgtype;
+ let BodyType = UnknownBody;
if (msgtype && bodyTypes[msgtype]) {
BodyType = bodyTypes[msgtype];
} else if (content.url) {
diff --git a/src/components/views/messages/RoomAvatarEvent.js b/src/components/views/messages/RoomAvatarEvent.js
index ed790953dc..453394249f 100644
--- a/src/components/views/messages/RoomAvatarEvent.js
+++ b/src/components/views/messages/RoomAvatarEvent.js
@@ -31,9 +31,9 @@ module.exports = React.createClass({
},
onAvatarClick: function(name) {
- var httpUrl = MatrixClientPeg.get().mxcUrlToHttp(this.props.mxEvent.getContent().url);
- var ImageView = sdk.getComponent("elements.ImageView");
- var params = {
+ const httpUrl = MatrixClientPeg.get().mxcUrlToHttp(this.props.mxEvent.getContent().url);
+ const ImageView = sdk.getComponent("elements.ImageView");
+ const params = {
src: httpUrl,
name: name,
};
@@ -41,12 +41,12 @@ module.exports = React.createClass({
},
render: function() {
- var ev = this.props.mxEvent;
- var senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
- var BaseAvatar = sdk.getComponent("avatars.BaseAvatar");
+ const ev = this.props.mxEvent;
+ const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
+ const BaseAvatar = sdk.getComponent("avatars.BaseAvatar");
- var room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId());
- var name = _t('%(senderDisplayName)s changed the avatar for %(roomName)s', {
+ const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId());
+ const name = _t('%(senderDisplayName)s changed the avatar for %(roomName)s', {
senderDisplayName: senderDisplayName,
roomName: room ? room.name : '',
});
@@ -59,12 +59,12 @@ module.exports = React.createClass({
);
}
- var url = ContentRepo.getHttpUriForMxc(
+ const url = ContentRepo.getHttpUriForMxc(
MatrixClientPeg.get().getHomeserverUrl(),
ev.getContent().url,
Math.ceil(14 * window.devicePixelRatio),
Math.ceil(14 * window.devicePixelRatio),
- 'crop'
+ 'crop',
);
// it sucks that _tJsx doesn't support normal _t substitutions :((
@@ -79,11 +79,11 @@ module.exports = React.createClass({
(sub) => senderDisplayName,
(sub) =>
-
+ onClick={this.onAvatarClick.bind(this, name)}>
+
,
- ]
+ ],
)
}
diff --git a/src/components/views/messages/SenderProfile.js b/src/components/views/messages/SenderProfile.js
index e224714a27..63e3144115 100644
--- a/src/components/views/messages/SenderProfile.js
+++ b/src/components/views/messages/SenderProfile.js
@@ -18,6 +18,7 @@
import React from 'react';
import sdk from '../../../index';
+import Flair from '../elements/Flair.js';
export default function SenderProfile(props) {
const EmojiText = sdk.getComponent('elements.EmojiText');
@@ -30,8 +31,17 @@ export default function SenderProfile(props) {
}
return (
- {`${name || ''} ${props.aux || ''}`}
+
+ { name || '' }
+ { props.enableFlair ?
+
+ : null
+ }
+ { props.aux ? { props.aux } : null }
+
);
}
diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js
index aae46d343c..c0468c38c2 100644
--- a/src/components/views/messages/TextualBody.js
+++ b/src/components/views/messages/TextualBody.js
@@ -104,10 +104,10 @@ module.exports = React.createClass({
if (this._unmounted) return;
for (let i = 0; i < blocks.length; i++) {
if (UserSettingsStore.getSyncedSetting("enableSyntaxHighlightLanguageDetection", false)) {
- highlight.highlightBlock(blocks[i])
+ highlight.highlightBlock(blocks[i]);
} else {
// Only syntax highlight if there's a class starting with language-
- let classes = blocks[i].className.split(/\s+/).filter(function (cl) {
+ const classes = blocks[i].className.split(/\s+/).filter(function(cl) {
return cl.startsWith('language-');
});
@@ -146,7 +146,7 @@ module.exports = React.createClass({
//console.log("calculateUrlPreview: ShowUrlPreview for %s is %s", this.props.mxEvent.getId(), this.props.showUrlPreview);
if (this.props.showUrlPreview && !this.state.links.length) {
- var links = this.findLinks(this.refs.content.children);
+ let links = this.findLinks(this.refs.content.children);
if (links.length) {
// de-dup the links (but preserve ordering)
const seen = new Set();
@@ -160,7 +160,7 @@ module.exports = React.createClass({
// lazy-load the hidden state of the preview widget from localstorage
if (global.localStorage) {
- var hidden = global.localStorage.getItem("hide_preview_" + this.props.mxEvent.getId());
+ const hidden = global.localStorage.getItem("hide_preview_" + this.props.mxEvent.getId());
this.setState({ widgetHidden: hidden });
}
}
@@ -197,21 +197,18 @@ module.exports = React.createClass({
},
findLinks: function(nodes) {
- var links = [];
+ let links = [];
- for (var i = 0; i < nodes.length; i++) {
- var node = nodes[i];
- if (node.tagName === "A" && node.getAttribute("href"))
- {
+ for (let i = 0; i < nodes.length; i++) {
+ const node = nodes[i];
+ if (node.tagName === "A" && node.getAttribute("href")) {
if (this.isLinkPreviewable(node)) {
links.push(node.getAttribute("href"));
}
- }
- else if (node.tagName === "PRE" || node.tagName === "CODE" ||
+ } else if (node.tagName === "PRE" || node.tagName === "CODE" ||
node.tagName === "BLOCKQUOTE") {
continue;
- }
- else if (node.children && node.children.length) {
+ } else if (node.children && node.children.length) {
links = links.concat(this.findLinks(node.children));
}
}
@@ -221,8 +218,7 @@ module.exports = React.createClass({
isLinkPreviewable: function(node) {
// don't try to preview relative links
if (!node.getAttribute("href").startsWith("http://") &&
- !node.getAttribute("href").startsWith("https://"))
- {
+ !node.getAttribute("href").startsWith("https://")) {
return false;
}
@@ -231,13 +227,11 @@ module.exports = React.createClass({
// or from a full foo.bar/baz style schemeless URL) - or be a markdown-style
// link, in which case we check the target text differs from the link value.
// TODO: make this configurable?
- if (node.textContent.indexOf("/") > -1)
- {
+ if (node.textContent.indexOf("/") > -1) {
return true;
- }
- else {
- var url = node.getAttribute("href");
- var host = url.match(/^https?:\/\/(.*?)(\/|$)/)[1];
+ } else {
+ const url = node.getAttribute("href");
+ const host = url.match(/^https?:\/\/(.*?)(\/|$)/)[1];
// never preview matrix.to links (if anything we should give a smart
// preview of the room/user they point to: nobody needs to be reminded
@@ -247,8 +241,7 @@ module.exports = React.createClass({
if (node.textContent.toLowerCase().trim().startsWith(host.toLowerCase())) {
// it's a "foo.pl" style link
return false;
- }
- else {
+ } else {
// it's a [foo bar](http://foo.com) style link
return true;
}
@@ -263,7 +256,7 @@ module.exports = React.createClass({
button.onclick = (e) => {
const copyCode = button.parentNode.getElementsByTagName("code")[0];
const successful = this.copyToClipboard(copyCode.textContent);
-
+
const GenericTextContextMenu = sdk.getComponent('context_menus.GenericTextContextMenu');
const buttonRect = e.target.getBoundingClientRect();
@@ -314,7 +307,7 @@ module.exports = React.createClass({
getInnerText: () => {
return this.refs.content.innerText;
- }
+ },
};
},
@@ -328,28 +321,28 @@ module.exports = React.createClass({
// the window.open command occurs in the same stack frame as the onClick callback.
// Go fetch a scalar token
- let scalarClient = new ScalarAuthClient();
+ const scalarClient = new ScalarAuthClient();
scalarClient.connect().then(() => {
- let completeUrl = scalarClient.getStarterLink(starterLink);
- let QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
- let integrationsUrl = SdkConfig.get().integrations_ui_url;
+ const completeUrl = scalarClient.getStarterLink(starterLink);
+ const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
+ const integrationsUrl = SdkConfig.get().integrations_ui_url;
Modal.createTrackedDialog('Add an integration', '', QuestionDialog, {
title: _t("Add an Integration"),
description:
- {_t("You are about to be taken to a third-party site so you can " +
+ { _t("You are about to be taken to a third-party site so you can " +
"authenticate your account for use with %(integrationsUrl)s. " +
- "Do you wish to continue?", { integrationsUrl: integrationsUrl })}
+ "Do you wish to continue?", { integrationsUrl: integrationsUrl }) }
,
button: _t("Continue"),
onFinished: function(confirmed) {
if (!confirmed) {
return;
}
- let width = window.screen.width > 1024 ? 1024 : window.screen.width;
- let height = window.screen.height > 800 ? 800 : window.screen.height;
- let left = (window.screen.width - width) / 2;
- let top = (window.screen.height - height) / 2;
+ const width = window.screen.width > 1024 ? 1024 : window.screen.width;
+ const height = window.screen.height > 800 ? 800 : window.screen.height;
+ const left = (window.screen.width - width) / 2;
+ const top = (window.screen.height - height) / 2;
window.open(completeUrl, '_blank', `height=${height}, width=${width}, top=${top}, left=${left},`);
},
});
@@ -358,28 +351,27 @@ module.exports = React.createClass({
render: function() {
const EmojiText = sdk.getComponent('elements.EmojiText');
- var mxEvent = this.props.mxEvent;
- var content = mxEvent.getContent();
+ const mxEvent = this.props.mxEvent;
+ const content = mxEvent.getContent();
- var body = HtmlUtils.bodyToHtml(content, this.props.highlights, {});
+ let body = HtmlUtils.bodyToHtml(content, this.props.highlights, {});
if (this.props.highlightLink) {
- body = { body } ;
- }
- else if (content.data && typeof content.data["org.matrix.neb.starter_link"] === "string") {
- body = { body } ;
+ body = { body } ;
+ } else if (content.data && typeof content.data["org.matrix.neb.starter_link"] === "string") {
+ body = { body } ;
}
- var widgets;
+ let widgets;
if (this.state.links.length && !this.state.widgetHidden && this.props.showUrlPreview) {
- var LinkPreviewWidget = sdk.getComponent('rooms.LinkPreviewWidget');
+ const LinkPreviewWidget = sdk.getComponent('rooms.LinkPreviewWidget');
widgets = this.state.links.map((link)=>{
return ;
+ key={link}
+ link={link}
+ mxEvent={this.props.mxEvent}
+ onCancelClick={this.onCancelClick}
+ onWidgetLoad={this.props.onWidgetLoad} />;
});
}
@@ -393,7 +385,7 @@ module.exports = React.createClass({
className="mx_MEmoteBody_sender"
onClick={this.onEmoteSenderClick}
>
- {name}
+ { name }
{ body }
diff --git a/src/components/views/messages/TextualEvent.js b/src/components/views/messages/TextualEvent.js
index 088f7cbbc6..be0962a868 100644
--- a/src/components/views/messages/TextualEvent.js
+++ b/src/components/views/messages/TextualEvent.js
@@ -16,9 +16,9 @@ limitations under the License.
'use strict';
-var React = require('react');
+const React = require('react');
-var TextForEvent = require('../../../TextForEvent');
+const TextForEvent = require('../../../TextForEvent');
import sdk from '../../../index';
module.exports = React.createClass({
@@ -28,13 +28,13 @@ module.exports = React.createClass({
/* the MatrixEvent to show */
mxEvent: React.PropTypes.object.isRequired,
},
-
+
render: function() {
const EmojiText = sdk.getComponent('elements.EmojiText');
- var text = TextForEvent.textForEvent(this.props.mxEvent);
+ const text = TextForEvent.textForEvent(this.props.mxEvent);
if (text == null || text.length === 0) return null;
return (
- {text}
+ { text }
);
},
});
diff --git a/src/components/views/messages/UnknownBody.js b/src/components/views/messages/UnknownBody.js
index 1b6f6426e5..083d7ac12e 100644
--- a/src/components/views/messages/UnknownBody.js
+++ b/src/components/views/messages/UnknownBody.js
@@ -23,10 +23,15 @@ module.exports = React.createClass({
displayName: 'UnknownBody',
render: function() {
+ let tooltip = _t("Removed or unknown message type");
+ if (this.props.mxEvent.isRedacted()) {
+ tooltip = _t("Message removed by %(userId)s", {userId: this.props.mxEvent.getSender()});
+ }
+
const text = this.props.mxEvent.getContent().body;
return (
-
- {text}
+
+ { text }
);
},
diff --git a/src/components/views/room_settings/AliasSettings.js b/src/components/views/room_settings/AliasSettings.js
index f37bd4271a..c64e876dbe 100644
--- a/src/components/views/room_settings/AliasSettings.js
+++ b/src/components/views/room_settings/AliasSettings.js
@@ -15,12 +15,12 @@ limitations under the License.
*/
import Promise from 'bluebird';
-var React = require('react');
-var ObjectUtils = require("../../../ObjectUtils");
-var MatrixClientPeg = require('../../../MatrixClientPeg');
-var sdk = require("../../../index");
+const React = require('react');
+const ObjectUtils = require("../../../ObjectUtils");
+const MatrixClientPeg = require('../../../MatrixClientPeg');
+const sdk = require("../../../index");
import { _t } from '../../../languageHandler';
-var Modal = require("../../../Modal");
+const Modal = require("../../../Modal");
module.exports = React.createClass({
displayName: 'AliasSettings',
@@ -30,14 +30,14 @@ module.exports = React.createClass({
canSetCanonicalAlias: React.PropTypes.bool.isRequired,
canSetAliases: React.PropTypes.bool.isRequired,
aliasEvents: React.PropTypes.array, // [MatrixEvent]
- canonicalAliasEvent: React.PropTypes.object // MatrixEvent
+ canonicalAliasEvent: React.PropTypes.object, // MatrixEvent
},
getDefaultProps: function() {
return {
canSetAliases: false,
canSetCanonicalAlias: false,
- aliasEvents: []
+ aliasEvents: [],
};
},
@@ -48,12 +48,12 @@ module.exports = React.createClass({
recalculateState: function(aliasEvents, canonicalAliasEvent) {
aliasEvents = aliasEvents || [];
- var state = {
+ const state = {
domainToAliases: {}, // { domain.com => [#alias1:domain.com, #alias2:domain.com] }
remoteDomains: [], // [ domain.com, foobar.com ]
- canonicalAlias: null // #canonical:domain.com
+ canonicalAlias: null, // #canonical:domain.com
};
- var localDomain = MatrixClientPeg.get().getDomain();
+ const localDomain = MatrixClientPeg.get().getDomain();
state.domainToAliases = this.aliasEventsToDictionary(aliasEvents);
@@ -69,26 +69,26 @@ module.exports = React.createClass({
},
saveSettings: function() {
- var promises = [];
+ let promises = [];
// save new aliases for m.room.aliases
- var aliasOperations = this.getAliasOperations();
- for (var i = 0; i < aliasOperations.length; i++) {
- var alias_operation = aliasOperations[i];
+ const aliasOperations = this.getAliasOperations();
+ for (let i = 0; i < aliasOperations.length; i++) {
+ const alias_operation = aliasOperations[i];
console.log("alias %s %s", alias_operation.place, alias_operation.val);
switch (alias_operation.place) {
case 'add':
promises.push(
MatrixClientPeg.get().createAlias(
- alias_operation.val, this.props.roomId
- )
+ alias_operation.val, this.props.roomId,
+ ),
);
break;
case 'del':
promises.push(
MatrixClientPeg.get().deleteAlias(
- alias_operation.val
- )
+ alias_operation.val,
+ ),
);
break;
default:
@@ -98,7 +98,7 @@ module.exports = React.createClass({
// save new canonical alias
- var oldCanonicalAlias = null;
+ let oldCanonicalAlias = null;
if (this.props.canonicalAliasEvent) {
oldCanonicalAlias = this.props.canonicalAliasEvent.getContent().alias;
}
@@ -107,9 +107,9 @@ module.exports = React.createClass({
promises = [Promise.all(promises).then(
MatrixClientPeg.get().sendStateEvent(
this.props.roomId, "m.room.canonical_alias", {
- alias: this.state.canonicalAlias
- }, ""
- )
+ alias: this.state.canonicalAlias,
+ }, "",
+ ),
)];
}
@@ -117,7 +117,7 @@ module.exports = React.createClass({
},
aliasEventsToDictionary: function(aliasEvents) { // m.room.alias events
- var dict = {};
+ const dict = {};
aliasEvents.forEach((event) => {
dict[event.getStateKey()] = (
(event.getContent().aliases || []).slice() // shallow-copy
@@ -132,28 +132,29 @@ module.exports = React.createClass({
},
getAliasOperations: function() {
- var oldAliases = this.aliasEventsToDictionary(this.props.aliasEvents);
+ const oldAliases = this.aliasEventsToDictionary(this.props.aliasEvents);
return ObjectUtils.getKeyValueArrayDiffs(oldAliases, this.state.domainToAliases);
},
- onAliasAdded: function(alias) {
+ onNewAliasChanged: function(value) {
+ this.setState({newAlias: value});
+ },
+
+ onLocalAliasAdded: function(alias) {
if (!alias || alias.length === 0) return; // ignore attempts to create blank aliases
- if (this.isAliasValid(alias)) {
- // add this alias to the domain to aliases dict
- var domain = alias.replace(/^.*?:/, '');
- // XXX: do we need to deep copy aliases before editing it?
- this.state.domainToAliases[domain] = this.state.domainToAliases[domain] || [];
- this.state.domainToAliases[domain].push(alias);
- this.setState({
- domainToAliases: this.state.domainToAliases
- });
+ const localDomain = MatrixClientPeg.get().getDomain();
+ if (this.isAliasValid(alias) && alias.endsWith(localDomain)) {
+ this.state.domainToAliases[localDomain] = this.state.domainToAliases[localDomain] || [];
+ this.state.domainToAliases[localDomain].push(alias);
- // reset the add field
- this.refs.add_alias.setValue(''); // FIXME
- }
- else {
- var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
+ this.setState({
+ domainToAliases: this.state.domainToAliases,
+ // Reset the add field
+ newAlias: "",
+ });
+ } else {
+ const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog('Invalid alias format', '', ErrorDialog, {
title: _t('Invalid alias format'),
description: _t('\'%(alias)s\' is not a valid format for an alias', { alias: alias }),
@@ -161,15 +162,13 @@ module.exports = React.createClass({
}
},
- onAliasChanged: function(domain, index, alias) {
+ onLocalAliasChanged: function(alias, index) {
if (alias === "") return; // hit the delete button to delete please
- var oldAlias;
- if (this.isAliasValid(alias)) {
- oldAlias = this.state.domainToAliases[domain][index];
- this.state.domainToAliases[domain][index] = alias;
- }
- else {
- var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
+ const localDomain = MatrixClientPeg.get().getDomain();
+ if (this.isAliasValid(alias) && alias.endsWith(localDomain)) {
+ this.state.domainToAliases[localDomain][index] = alias;
+ } else {
+ const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog('Invalid address format', '', ErrorDialog, {
title: _t('Invalid address format'),
description: _t('\'%(alias)s\' is not a valid format for an address', { alias: alias }),
@@ -177,39 +176,41 @@ module.exports = React.createClass({
}
},
- onAliasDeleted: function(domain, index) {
+ onLocalAliasDeleted: function(index) {
+ const localDomain = MatrixClientPeg.get().getDomain();
// It's a bit naughty to directly manipulate this.state, and React would
// normally whine at you, but it can't see us doing the splice. Given we
// promptly setState anyway, it's just about acceptable. The alternative
// would be to arbitrarily deepcopy to a temp variable and then setState
// that, but why bother when we can cut this corner.
- var alias = this.state.domainToAliases[domain].splice(index, 1);
+ this.state.domainToAliases[localDomain].splice(index, 1);
this.setState({
- domainToAliases: this.state.domainToAliases
+ domainToAliases: this.state.domainToAliases,
});
},
onCanonicalAliasChange: function(event) {
this.setState({
- canonicalAlias: event.target.value
+ canonicalAlias: event.target.value,
});
},
render: function() {
- var self = this;
- var EditableText = sdk.getComponent("elements.EditableText");
- var localDomain = MatrixClientPeg.get().getDomain();
+ const self = this;
+ const EditableText = sdk.getComponent("elements.EditableText");
+ const EditableItemList = sdk.getComponent("elements.EditableItemList");
+ const localDomain = MatrixClientPeg.get().getDomain();
- var canonical_alias_section;
+ let canonical_alias_section;
if (this.props.canSetCanonicalAlias) {
canonical_alias_section = (
-
+
{ _t('not specified') }
{
Object.keys(self.state.domainToAliases).map(function(domain, i) {
return self.state.domainToAliases[domain].map(function(alias, j) {
return (
-
+
{ alias }
);
@@ -218,34 +219,33 @@ module.exports = React.createClass({
}
);
- }
- else {
+ } else {
canonical_alias_section = (
{ this.state.canonicalAlias || _t('not set') }
);
}
- var remote_aliases_section;
+ let remote_aliases_section;
if (this.state.remoteDomains.length) {
remote_aliases_section = (
- {_t("Remote addresses for this room:")}
+ { _t("Remote addresses for this room:") }
{ this.state.remoteDomains.map((domain, i) => {
return this.state.domainToAliases[domain].map(function(alias, j) {
return (
-
+
+ blurToCancel={false}
+ editable={false}
+ initialValue={alias} />
);
});
- })}
+ }) }
);
@@ -257,58 +257,24 @@ module.exports = React.createClass({
{ _t('The main address for this room is') }: { canonical_alias_section }
-
- { (this.state.domainToAliases[localDomain] &&
- this.state.domainToAliases[localDomain].length > 0)
- ? _t('Local addresses for this room:')
- : _t('This room has no local addresses') }
-
-
- { (this.state.domainToAliases[localDomain] || []).map((alias, i) => {
- var deleteButton;
- if (this.props.canSetAliases) {
- deleteButton = (
-
- );
- }
- return (
-
-
-
- { deleteButton }
-
-
- );
- })}
-
- { this.props.canSetAliases ?
-
-
-
-
-
-
: ""
- }
-
+
{ remote_aliases_section }
);
- }
+ },
});
diff --git a/src/components/views/room_settings/ColorSettings.js b/src/components/views/room_settings/ColorSettings.js
index b37802d304..d914390bd8 100644
--- a/src/components/views/room_settings/ColorSettings.js
+++ b/src/components/views/room_settings/ColorSettings.js
@@ -14,16 +14,16 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import Promise from 'bluebird';
-var React = require('react');
+const React = require('react');
-var sdk = require('../../../index');
-var Tinter = require('../../../Tinter');
-var MatrixClientPeg = require("../../../MatrixClientPeg");
-var Modal = require("../../../Modal");
+const sdk = require('../../../index');
+const Tinter = require('../../../Tinter');
+const MatrixClientPeg = require("../../../MatrixClientPeg");
+const Modal = require("../../../Modal");
import dis from '../../../dispatcher';
-var ROOM_COLORS = [
+const ROOM_COLORS = [
// magic room default values courtesy of Ribot
["#76cfa6", "#eaf5f0"],
["#81bddb", "#eaf1f4"],
@@ -41,21 +41,21 @@ module.exports = React.createClass({
displayName: 'ColorSettings',
propTypes: {
- room: React.PropTypes.object.isRequired
+ room: React.PropTypes.object.isRequired,
},
getInitialState: function() {
- var data = {
+ const data = {
index: 0,
primary_color: ROOM_COLORS[0].primary_color,
secondary_color: ROOM_COLORS[0].secondary_color,
- hasChanged: false
+ hasChanged: false,
};
- var event = this.props.room.getAccountData("org.matrix.room.color_scheme");
+ const event = this.props.room.getAccountData("org.matrix.room.color_scheme");
if (!event) {
return data;
}
- var scheme = event.getContent();
+ const scheme = event.getContent();
data.primary_color = scheme.primary_color;
data.secondary_color = scheme.secondary_color;
data.index = this._getColorIndex(data);
@@ -64,7 +64,7 @@ module.exports = React.createClass({
// append the unrecognised colours to our palette
data.index = ROOM_COLORS.length;
ROOM_COLORS.push([
- scheme.primary_color, scheme.secondary_color
+ scheme.primary_color, scheme.secondary_color,
]);
}
return data;
@@ -74,7 +74,7 @@ module.exports = React.createClass({
if (!this.state.hasChanged) {
return Promise.resolve(); // They didn't explicitly give a color to save.
}
- var originalState = this.getInitialState();
+ const originalState = this.getInitialState();
if (originalState.primary_color !== this.state.primary_color ||
originalState.secondary_color !== this.state.secondary_color) {
console.log("ColorSettings: Saving new color");
@@ -84,8 +84,8 @@ module.exports = React.createClass({
return MatrixClientPeg.get().setRoomAccountData(
this.props.room.roomId, "org.matrix.room.color_scheme", {
primary_color: this.state.primary_color,
- secondary_color: this.state.secondary_color
- }
+ secondary_color: this.state.secondary_color,
+ },
).catch(function(err) {
if (err.errcode == 'M_GUEST_ACCESS_FORBIDDEN') {
dis.dispatch({action: 'view_set_mxid'});
@@ -100,8 +100,8 @@ module.exports = React.createClass({
return -1;
}
// XXX: we should validate these values
- for (var i = 0; i < ROOM_COLORS.length; i++) {
- var room_color = ROOM_COLORS[i];
+ for (let i = 0; i < ROOM_COLORS.length; i++) {
+ const room_color = ROOM_COLORS[i];
if (room_color[0] === String(scheme.primary_color).toLowerCase() &&
room_color[1] === String(scheme.secondary_color).toLowerCase()) {
return i;
@@ -117,34 +117,34 @@ module.exports = React.createClass({
index: index,
primary_color: ROOM_COLORS[index][0],
secondary_color: ROOM_COLORS[index][1],
- hasChanged: true
+ hasChanged: true,
});
},
render: function() {
return (
- {ROOM_COLORS.map((room_color, i) => {
- var selected;
+ { ROOM_COLORS.map((room_color, i) => {
+ let selected;
if (i === this.state.index) {
selected = (
-
+
);
}
- var boundClick = this._onColorSchemeChanged.bind(this, i);
+ const boundClick = this._onColorSchemeChanged.bind(this, i);
return (
+ onClick={boundClick}>
{ selected }
);
- })}
+ }) }
);
- }
+ },
});
diff --git a/src/components/views/room_settings/RelatedGroupSettings.js b/src/components/views/room_settings/RelatedGroupSettings.js
new file mode 100644
index 0000000000..60bdbf1481
--- /dev/null
+++ b/src/components/views/room_settings/RelatedGroupSettings.js
@@ -0,0 +1,125 @@
+/*
+Copyright 2017 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 {MatrixEvent, MatrixClient} from 'matrix-js-sdk';
+import sdk from '../../../index';
+import { _t } from '../../../languageHandler';
+import Modal from '../../../Modal';
+
+const GROUP_ID_REGEX = /\+\S+\:\S+/;
+
+module.exports = React.createClass({
+ displayName: 'RelatedGroupSettings',
+
+ propTypes: {
+ roomId: React.PropTypes.string.isRequired,
+ canSetRelatedRooms: React.PropTypes.bool.isRequired,
+ relatedGroupsEvent: React.PropTypes.instanceOf(MatrixEvent),
+ },
+
+ contextTypes: {
+ matrixClient: React.PropTypes.instanceOf(MatrixClient),
+ },
+
+ getDefaultProps: function() {
+ return {
+ canSetRelatedRooms: false,
+ };
+ },
+
+ getInitialState: function() {
+ return {
+ newGroupsList: this.props.relatedGroupsEvent ?
+ (this.props.relatedGroupsEvent.getContent().groups || []) : [],
+ newGroupId: null,
+ };
+ },
+
+ saveSettings: function() {
+ return this.context.matrixClient.sendStateEvent(
+ this.props.roomId,
+ 'm.room.related_groups',
+ {
+ groups: this.state.newGroupsList,
+ },
+ '',
+ );
+ },
+
+ validateGroupId: function(groupId) {
+ if (!GROUP_ID_REGEX.test(groupId)) {
+ const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
+ Modal.createTrackedDialog('Invalid related group ID', '', ErrorDialog, {
+ title: _t('Invalid group ID'),
+ description: _t('\'%(groupId)s\' is not a valid group ID', { groupId }),
+ });
+ return false;
+ }
+ return true;
+ },
+
+ onNewGroupChanged: function(newGroupId) {
+ this.setState({ newGroupId });
+ },
+
+ onGroupAdded: function(groupId) {
+ if (groupId.length === 0 || !this.validateGroupId(groupId)) {
+ return;
+ }
+ this.setState({
+ newGroupsList: this.state.newGroupsList.concat([groupId]),
+ newGroupId: '',
+ });
+ },
+
+ onGroupEdited: function(groupId, index) {
+ if (groupId.length === 0 || !this.validateGroupId(groupId)) {
+ return;
+ }
+ this.setState({
+ newGroupsList: Object.assign(this.state.newGroupsList, {[index]: groupId}),
+ });
+ },
+
+ onGroupDeleted: function(index) {
+ const newGroupsList = this.state.newGroupsList.slice();
+ newGroupsList.splice(index, 1),
+ this.setState({ newGroupsList });
+ },
+
+ render: function() {
+ const localDomain = this.context.matrixClient.getDomain();
+ const EditableItemList = sdk.getComponent('elements.EditableItemList');
+ return (
+
{ _t('Related Groups') }
+
+ );
+ },
+});
diff --git a/src/components/views/room_settings/UrlPreviewSettings.js b/src/components/views/room_settings/UrlPreviewSettings.js
index 5ee18badaa..56ae24e2f8 100644
--- a/src/components/views/room_settings/UrlPreviewSettings.js
+++ b/src/components/views/room_settings/UrlPreviewSettings.js
@@ -15,11 +15,11 @@ limitations under the License.
*/
import Promise from 'bluebird';
-var React = require('react');
-var MatrixClientPeg = require('../../../MatrixClientPeg');
-var sdk = require("../../../index");
-var Modal = require("../../../Modal");
-var UserSettingsStore = require('../../../UserSettingsStore');
+const React = require('react');
+const MatrixClientPeg = require('../../../MatrixClientPeg');
+const sdk = require("../../../index");
+const Modal = require("../../../Modal");
+const UserSettingsStore = require('../../../UserSettingsStore');
import { _t, _tJsx } from '../../../languageHandler';
@@ -31,11 +31,11 @@ module.exports = React.createClass({
},
getInitialState: function() {
- var cli = MatrixClientPeg.get();
- var roomState = this.props.room.currentState;
+ const cli = MatrixClientPeg.get();
+ const roomState = this.props.room.currentState;
- var roomPreviewUrls = this.props.room.currentState.getStateEvents('org.matrix.room.preview_urls', '');
- var userPreviewUrls = this.props.room.getAccountData("org.matrix.room.preview_urls");
+ const roomPreviewUrls = this.props.room.currentState.getStateEvents('org.matrix.room.preview_urls', '');
+ const userPreviewUrls = this.props.room.getAccountData("org.matrix.room.preview_urls");
return {
globalDisableUrlPreview: (roomPreviewUrls && roomPreviewUrls.getContent().disable) || false,
@@ -49,37 +49,37 @@ module.exports = React.createClass({
},
saveSettings: function() {
- var promises = [];
+ const promises = [];
if (this.state.globalDisableUrlPreview !== this.originalState.globalDisableUrlPreview) {
console.log("UrlPreviewSettings: Updating room's preview_urls state event");
promises.push(
MatrixClientPeg.get().sendStateEvent(
this.props.room.roomId, "org.matrix.room.preview_urls", {
- disable: this.state.globalDisableUrlPreview
- }, ""
- )
+ disable: this.state.globalDisableUrlPreview,
+ }, "",
+ ),
);
}
- var content = undefined;
+ let content = undefined;
if (this.state.userDisableUrlPreview !== this.originalState.userDisableUrlPreview) {
console.log("UrlPreviewSettings: Disabling user's per-room preview_urls");
- content = this.state.userDisableUrlPreview ? { disable : true } : {};
+ content = this.state.userDisableUrlPreview ? { disable: true } : {};
}
if (this.state.userEnableUrlPreview !== this.originalState.userEnableUrlPreview) {
console.log("UrlPreviewSettings: Enabling user's per-room preview_urls");
if (!content || content.disable === undefined) {
- content = this.state.userEnableUrlPreview ? { disable : false } : {};
+ content = this.state.userEnableUrlPreview ? { disable: false } : {};
}
}
if (content) {
promises.push(
MatrixClientPeg.get().setRoomAccountData(
- this.props.room.roomId, "org.matrix.room.preview_urls", content
- )
+ this.props.room.roomId, "org.matrix.room.preview_urls", content,
+ ),
);
}
@@ -109,62 +109,59 @@ module.exports = React.createClass({
},
render: function() {
- var self = this;
- var roomState = this.props.room.currentState;
- var cli = MatrixClientPeg.get();
+ const self = this;
+ const roomState = this.props.room.currentState;
+ const cli = MatrixClientPeg.get();
- var maySetRoomPreviewUrls = roomState.mayClientSendStateEvent('org.matrix.room.preview_urls', cli);
- var disableRoomPreviewUrls;
+ const maySetRoomPreviewUrls = roomState.mayClientSendStateEvent('org.matrix.room.preview_urls', cli);
+ let disableRoomPreviewUrls;
if (maySetRoomPreviewUrls) {
disableRoomPreviewUrls =
- {_t("Disable URL previews by default for participants in this room")}
+ onChange={this.onGlobalDisableUrlPreviewChange}
+ checked={this.state.globalDisableUrlPreview} />
+ { _t("Disable URL previews by default for participants in this room") }
;
- }
- else {
+ } else {
disableRoomPreviewUrls =
- {_t("URL previews are %(globalDisableUrlPreview)s by default for participants in this room.", {globalDisableUrlPreview: this.state.globalDisableUrlPreview ? _t("disabled") : _t("enabled")})}
+ { _t("URL previews are %(globalDisableUrlPreview)s by default for participants in this room.", {globalDisableUrlPreview: this.state.globalDisableUrlPreview ? _t("disabled") : _t("enabled")}) }
;
}
let urlPreviewText = null;
if (UserSettingsStore.getUrlPreviewsDisabled()) {
urlPreviewText = (
- _tJsx("You have disabled URL previews by default.", /(.*?)<\/a>/, (sub)=> {sub} )
+ _tJsx("You have disabled URL previews by default.", /(.*?)<\/a>/, (sub)=> { sub } )
);
- }
- else {
+ } else {
urlPreviewText = (
- _tJsx("You have enabled URL previews by default.", /(.*?)<\/a>/, (sub)=> {sub} )
+ _tJsx("You have enabled URL previews by default.", /(.*?)<\/a>/, (sub)=> { sub } )
);
}
return (
-
{_t("URL Previews")}
+ { _t("URL Previews") }
- {urlPreviewText}
+ { urlPreviewText }
{ disableRoomPreviewUrls }
- {_t("Enable URL previews for this room (affects only you)")}
+ onChange={this.onUserEnableUrlPreviewChange}
+ checked={this.state.userEnableUrlPreview} />
+ { _t("Enable URL previews for this room (affects only you)") }
- {_t("Disable URL previews for this room (affects only you)")}
+ onChange={this.onUserDisableUrlPreviewChange}
+ checked={this.state.userDisableUrlPreview} />
+ { _t("Disable URL previews for this room (affects only you)") }
);
-
- }
+ },
});
diff --git a/src/components/views/rooms/AppsDrawer.js b/src/components/views/rooms/AppsDrawer.js
index 536259ae91..1c9296228d 100644
--- a/src/components/views/rooms/AppsDrawer.js
+++ b/src/components/views/rooms/AppsDrawer.js
@@ -231,16 +231,16 @@ module.exports = React.createClass({
"mx_AddWidget_button"
}
title={_t('Add a widget')}>
- [+] {_t('Add a widget')}
+ [+] { _t('Add a widget') }
;
}
return (
- {apps}
+ { apps }
- {this._canUserModify() && addWidget}
+ { this._canUserModify() && addWidget }
);
},
diff --git a/src/components/views/rooms/Autocomplete.js b/src/components/views/rooms/Autocomplete.js
index 7706cc7ebd..ecc908a02c 100644
--- a/src/components/views/rooms/Autocomplete.js
+++ b/src/components/views/rooms/Autocomplete.js
@@ -233,7 +233,7 @@ export default class Autocomplete extends React.Component {
const componentPosition = position;
position++;
- const onMouseOver = () => this.setSelection(componentPosition);
+ const onMouseMove = () => this.setSelection(componentPosition);
const onClick = () => {
this.setSelection(componentPosition);
this.onCompletionClicked();
@@ -243,7 +243,7 @@ export default class Autocomplete extends React.Component {
key: i,
ref: `completion${position - 1}`,
className,
- onMouseOver,
+ onMouseMove,
onClick,
});
});
@@ -251,15 +251,15 @@ export default class Autocomplete extends React.Component {
return completions.length > 0 ? (
- {completionResult.provider.getName()}
- {completionResult.provider.renderCompletions(completions)}
+ { completionResult.provider.getName() }
+ { completionResult.provider.renderCompletions(completions) }
) : null;
}).filter((completion) => !!completion);
return !this.state.hide && renderedCompletions.length > 0 ? (
this.container = e}>
- {renderedCompletions}
+ { renderedCompletions }
) : null;
}
diff --git a/src/components/views/rooms/AuxPanel.js b/src/components/views/rooms/AuxPanel.js
index 65dedad61d..726ec7ff1d 100644
--- a/src/components/views/rooms/AuxPanel.js
+++ b/src/components/views/rooms/AuxPanel.js
@@ -83,9 +83,9 @@ module.exports = React.createClass({
-
-
- {_t("Drop file here to upload")}
+
+
+ { _t("Drop file here to upload") }
);
@@ -99,23 +99,23 @@ module.exports = React.createClass({
supportedText = _t(" (unsupported)");
} else {
joinNode = (
- {_tJsx(
+ { _tJsx(
"Join as voice or video .",
[/(.*?)<\/voiceText>/, /(.*?)<\/videoText>/],
[
- (sub) => { this.onConferenceNotificationClick(event, 'voice');}} href="#">{sub} ,
- (sub) => { this.onConferenceNotificationClick(event, 'video');}} href="#">{sub} ,
- ]
- )}
+ (sub) => { this.onConferenceNotificationClick(event, 'voice');}} href="#">{ sub } ,
+ (sub) => { this.onConferenceNotificationClick(event, 'video');}} href="#">{ sub } ,
+ ],
+ ) }
);
}
// XXX: the translation here isn't great: appending ' (unsupported)' is likely to not make sense in many languages,
// but there are translations for this in the languages we do have so I'm leaving it for now.
conferenceCallNotification = (
- {_t("Ongoing conference call%(supportedText)s.", {supportedText: supportedText})}
+ { _t("Ongoing conference call%(supportedText)s.", {supportedText: supportedText}) }
- {joinNode}
+ { joinNode }
);
}
diff --git a/src/components/views/rooms/EntityTile.js b/src/components/views/rooms/EntityTile.js
index 7002e45c3b..64934f3cca 100644
--- a/src/components/views/rooms/EntityTile.js
+++ b/src/components/views/rooms/EntityTile.js
@@ -16,18 +16,18 @@ limitations under the License.
'use strict';
-var React = require('react');
+const React = require('react');
-var MatrixClientPeg = require('../../../MatrixClientPeg');
-var sdk = require('../../../index');
+const MatrixClientPeg = require('../../../MatrixClientPeg');
+const sdk = require('../../../index');
import AccessibleButton from '../elements/AccessibleButton';
import { _t } from '../../../languageHandler';
-var PRESENCE_CLASS = {
+const PRESENCE_CLASS = {
"offline": "mx_EntityTile_offline",
"online": "mx_EntityTile_online",
- "unavailable": "mx_EntityTile_unavailable"
+ "unavailable": "mx_EntityTile_unavailable",
};
@@ -62,7 +62,7 @@ module.exports = React.createClass({
showInviteButton: React.PropTypes.bool,
shouldComponentUpdate: React.PropTypes.func,
onClick: React.PropTypes.func,
- suppressOnHover: React.PropTypes.bool
+ suppressOnHover: React.PropTypes.bool,
},
getDefaultProps: function() {
@@ -73,13 +73,13 @@ module.exports = React.createClass({
presenceLastActiveAgo: 0,
presenceLastTs: 0,
showInviteButton: false,
- suppressOnHover: false
+ suppressOnHover: false,
};
},
getInitialState: function() {
return {
- hover: false
+ hover: false,
};
},
@@ -98,38 +98,37 @@ module.exports = React.createClass({
render: function() {
const presenceClass = presenceClassForMember(
- this.props.presenceState, this.props.presenceLastActiveAgo
+ this.props.presenceState, this.props.presenceLastActiveAgo,
);
- var mainClassName = "mx_EntityTile ";
+ let mainClassName = "mx_EntityTile ";
mainClassName += presenceClass + (this.props.className ? (" " + this.props.className) : "");
- var nameEl;
+ let nameEl;
const {name} = this.props;
const EmojiText = sdk.getComponent('elements.EmojiText');
if (this.state.hover && !this.props.suppressOnHover) {
- var activeAgo = this.props.presenceLastActiveAgo ?
+ const activeAgo = this.props.presenceLastActiveAgo ?
(Date.now() - (this.props.presenceLastTs - this.props.presenceLastActiveAgo)) : -1;
mainClassName += " mx_EntityTile_hover";
- var PresenceLabel = sdk.getComponent("rooms.PresenceLabel");
+ const PresenceLabel = sdk.getComponent("rooms.PresenceLabel");
nameEl = (
-
-
{name}
-
+
{ name }
+
);
- }
- else {
+ } else {
nameEl = (
- {name}
+ { name }
);
}
- var inviteButton;
+ let inviteButton;
if (this.props.showInviteButton) {
inviteButton = (
@@ -138,25 +137,25 @@ module.exports = React.createClass({
);
}
- var power;
- var powerLevel = this.props.powerLevel;
+ let power;
+ const powerLevel = this.props.powerLevel;
if (powerLevel >= 50 && powerLevel < 99) {
- power =
;
+ power =
;
}
if (powerLevel >= 99) {
- power =
;
+ power =
;
}
- var MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
- var BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
+ const MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
+ const BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
- var av = this.props.avatarJsx ||
;
+ const av = this.props.avatarJsx ||
;
return (
-
+
{ av }
{ power }
@@ -165,5 +164,5 @@ module.exports = React.createClass({
{ inviteButton }
);
- }
+ },
});
diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js
index a6f8ed5542..f92dc0b97a 100644
--- a/src/components/views/rooms/EventTile.js
+++ b/src/components/views/rooms/EventTile.js
@@ -17,38 +17,38 @@ limitations under the License.
'use strict';
-var React = require('react');
-var classNames = require("classnames");
+const React = require('react');
+const classNames = require("classnames");
import { _t } from '../../../languageHandler';
-var Modal = require('../../../Modal');
+const Modal = require('../../../Modal');
-var sdk = require('../../../index');
-var TextForEvent = require('../../../TextForEvent');
+const sdk = require('../../../index');
+const TextForEvent = require('../../../TextForEvent');
import withMatrixClient from '../../../wrappers/withMatrixClient';
-var ContextualMenu = require('../../structures/ContextualMenu');
+const ContextualMenu = require('../../structures/ContextualMenu');
import dis from '../../../dispatcher';
-var ObjectUtils = require('../../../ObjectUtils');
+const ObjectUtils = require('../../../ObjectUtils');
-var eventTileTypes = {
+const eventTileTypes = {
'm.room.message': 'messages.MessageEvent',
- 'm.room.member' : 'messages.TextualEvent',
- 'm.call.invite' : 'messages.TextualEvent',
- 'm.call.answer' : 'messages.TextualEvent',
- 'm.call.hangup' : 'messages.TextualEvent',
- 'm.room.name' : 'messages.TextualEvent',
- 'm.room.avatar' : 'messages.RoomAvatarEvent',
- 'm.room.topic' : 'messages.TextualEvent',
- 'm.room.third_party_invite' : 'messages.TextualEvent',
- 'm.room.history_visibility' : 'messages.TextualEvent',
- 'm.room.encryption' : 'messages.TextualEvent',
- 'm.room.power_levels' : 'messages.TextualEvent',
+ 'm.room.member': 'messages.TextualEvent',
+ 'm.call.invite': 'messages.TextualEvent',
+ 'm.call.answer': 'messages.TextualEvent',
+ 'm.call.hangup': 'messages.TextualEvent',
+ 'm.room.name': 'messages.TextualEvent',
+ 'm.room.avatar': 'messages.RoomAvatarEvent',
+ 'm.room.topic': 'messages.TextualEvent',
+ 'm.room.third_party_invite': 'messages.TextualEvent',
+ 'm.room.history_visibility': 'messages.TextualEvent',
+ 'm.room.encryption': 'messages.TextualEvent',
+ 'm.room.power_levels': 'messages.TextualEvent',
'im.vector.modular.widgets': 'messages.TextualEvent',
};
-var MAX_READ_AVATARS = 5;
+const MAX_READ_AVATARS = 5;
// Our component structure for EventTiles on the timeline is:
//
@@ -177,7 +177,7 @@ module.exports = withMatrixClient(React.createClass({
},
componentWillUnmount: function() {
- var client = this.props.matrixClient;
+ const client = this.props.matrixClient;
client.removeListener("deviceVerificationChanged",
this.onDeviceVerificationChanged);
this.props.mxEvent.removeListener("Event.decrypted", this._onDecrypted);
@@ -204,20 +204,20 @@ module.exports = withMatrixClient(React.createClass({
const verified = await this.props.matrixClient.isEventSenderVerified(mxEvent);
this.setState({
- verified: verified
+ verified: verified,
});
},
_propsEqual: function(objA, objB) {
- var keysA = Object.keys(objA);
- var keysB = Object.keys(objB);
+ const keysA = Object.keys(objA);
+ const keysB = Object.keys(objB);
if (keysA.length !== keysB.length) {
return false;
}
- for (var i = 0; i < keysA.length; i++) {
- var key = keysA[i];
+ for (let i = 0; i < keysA.length; i++) {
+ const key = keysA[i];
if (!objB.hasOwnProperty(key)) {
return false;
@@ -225,8 +225,8 @@ module.exports = withMatrixClient(React.createClass({
// need to deep-compare readReceipts
if (key == 'readReceipts') {
- var rA = objA[key];
- var rB = objB[key];
+ const rA = objA[key];
+ const rB = objB[key];
if (rA === rB) {
continue;
}
@@ -238,7 +238,7 @@ module.exports = withMatrixClient(React.createClass({
if (rA.length !== rB.length) {
return false;
}
- for (var j = 0; j < rA.length; j++) {
+ for (let j = 0; j < rA.length; j++) {
if (rA[j].roomMember.userId !== rB[j].roomMember.userId) {
return false;
}
@@ -253,12 +253,11 @@ module.exports = withMatrixClient(React.createClass({
},
shouldHighlight: function() {
- var actions = this.props.matrixClient.getPushActionsForEvent(this.props.mxEvent);
+ const actions = this.props.matrixClient.getPushActionsForEvent(this.props.mxEvent);
if (!actions || !actions.tweaks) { return false; }
// don't show self-highlights from another of our clients
- if (this.props.mxEvent.getSender() === this.props.matrixClient.credentials.userId)
- {
+ if (this.props.mxEvent.getSender() === this.props.matrixClient.credentials.userId) {
return false;
}
@@ -266,13 +265,13 @@ module.exports = withMatrixClient(React.createClass({
},
onEditClicked: function(e) {
- var MessageContextMenu = sdk.getComponent('context_menus.MessageContextMenu');
- var buttonRect = e.target.getBoundingClientRect();
+ const MessageContextMenu = sdk.getComponent('context_menus.MessageContextMenu');
+ const buttonRect = e.target.getBoundingClientRect();
// The window X and Y offsets are to adjust position when zoomed in to page
- var x = buttonRect.right + window.pageXOffset;
- var y = (buttonRect.top + (buttonRect.height / 2) + window.pageYOffset) - 19;
- var self = this;
+ const x = buttonRect.right + window.pageXOffset;
+ const y = (buttonRect.top + (buttonRect.height / 2) + window.pageYOffset) - 19;
+ const self = this;
ContextualMenu.createMenu(MessageContextMenu, {
chevronOffset: 10,
mxEvent: this.props.mxEvent,
@@ -281,19 +280,18 @@ module.exports = withMatrixClient(React.createClass({
eventTileOps: this.refs.tile && this.refs.tile.getEventTileOps ? this.refs.tile.getEventTileOps() : undefined,
onFinished: function() {
self.setState({menu: false});
- }
+ },
});
this.setState({menu: true});
},
toggleAllReadAvatars: function() {
this.setState({
- allReadAvatars: !this.state.allReadAvatars
+ allReadAvatars: !this.state.allReadAvatars,
});
},
getReadAvatars: function() {
-
// return early if there are no read receipts
if (!this.props.readReceipts || this.props.readReceipts.length === 0) {
return (
);
@@ -304,11 +302,11 @@ module.exports = withMatrixClient(React.createClass({
const receiptOffset = 15;
let left = 0;
- var receipts = this.props.readReceipts || [];
- for (var i = 0; i < receipts.length; ++i) {
- var receipt = receipts[i];
+ const receipts = this.props.readReceipts || [];
+ for (let i = 0; i < receipts.length; ++i) {
+ const receipt = receipts[i];
- var hidden = true;
+ let hidden = true;
if ((i < MAX_READ_AVATARS) || this.state.allReadAvatars) {
hidden = false;
}
@@ -319,7 +317,7 @@ module.exports = withMatrixClient(React.createClass({
// else set it proportional to index
left = (hidden ? MAX_READ_AVATARS - 1 : i) * -receiptOffset;
- var userId = receipt.roomMember.userId;
+ const userId = receipt.roomMember.userId;
var readReceiptInfo;
if (this.props.readReceiptMap) {
@@ -340,12 +338,12 @@ module.exports = withMatrixClient(React.createClass({
onClick={this.toggleAllReadAvatars}
timestamp={receipt.ts}
showTwelveHour={this.props.isTwelveHour}
- />
+ />,
);
}
- var remText;
+ let remText;
if (!this.state.allReadAvatars) {
- var remainder = receipts.length - MAX_READ_AVATARS;
+ const remainder = receipts.length - MAX_READ_AVATARS;
if (remainder > 0) {
remText =
{
require(['../../../async-components/views/dialogs/EncryptedEventDialog'], cb);
@@ -396,12 +394,12 @@ module.exports = withMatrixClient(React.createClass({
if (ev.getContent().msgtype === 'm.bad.encrypted') {
- return ;
+ return ;
} else if (ev.isEncrypted()) {
if (this.state.verified) {
- return ;
+ return ;
} else {
- return ;
+ return ;
}
} else {
// XXX: if the event is being encrypted (ie eventSendStatus ===
@@ -412,7 +410,7 @@ module.exports = withMatrixClient(React.createClass({
// open padlock
const e2eEnabled = this.props.matrixClient.isRoomEncrypted(ev.getRoomId());
if (e2eEnabled) {
- return ;
+ return ;
}
}
@@ -421,27 +419,27 @@ module.exports = withMatrixClient(React.createClass({
},
render: function() {
- var MessageTimestamp = sdk.getComponent('messages.MessageTimestamp');
- var SenderProfile = sdk.getComponent('messages.SenderProfile');
- var MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
+ const MessageTimestamp = sdk.getComponent('messages.MessageTimestamp');
+ const SenderProfile = sdk.getComponent('messages.SenderProfile');
+ const MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
//console.log("EventTile showUrlPreview for %s is %s", this.props.mxEvent.getId(), this.props.showUrlPreview);
- var content = this.props.mxEvent.getContent();
- var msgtype = content.msgtype;
- var eventType = this.props.mxEvent.getType();
+ const content = this.props.mxEvent.getContent();
+ const msgtype = content.msgtype;
+ const eventType = this.props.mxEvent.getType();
// Info messages are basically information about commands processed on a room
- var isInfoMessage = (eventType !== 'm.room.message');
+ const isInfoMessage = (eventType !== 'm.room.message');
- var EventTileType = sdk.getComponent(eventTileTypes[eventType]);
+ const EventTileType = sdk.getComponent(eventTileTypes[eventType]);
// This shouldn't happen: the caller should check we support this type
// before trying to instantiate us
if (!EventTileType) {
throw new Error("Event type not supported");
}
- var isSending = (['sending', 'queued', 'encrypting'].indexOf(this.props.eventSendStatus) !== -1);
+ const isSending = (['sending', 'queued', 'encrypting'].indexOf(this.props.eventSendStatus) !== -1);
const isRedacted = (eventType === 'm.room.message') && this.props.isRedacted;
const classes = classNames({
@@ -468,9 +466,9 @@ module.exports = withMatrixClient(React.createClass({
this.props.mxEvent.getRoomId() + "/" +
this.props.mxEvent.getId();
- var readAvatars = this.getReadAvatars();
+ const readAvatars = this.getReadAvatars();
- var avatar, sender;
+ let avatar, sender;
let avatarSize;
let needsSenderProfile;
@@ -508,15 +506,14 @@ module.exports = withMatrixClient(React.createClass({
if (msgtype === 'm.image') aux = _t('sent an image');
else if (msgtype === 'm.video') aux = _t('sent a video');
else if (msgtype === 'm.file') aux = _t('uploaded a file');
- sender = ;
- }
- else {
- sender = ;
+ sender = ;
+ } else {
+ sender = ;
}
}
const editButton = (
-
+
);
const timestamp = this.props.mxEvent.getTs() ?
@@ -527,13 +524,13 @@ module.exports = withMatrixClient(React.createClass({
return (
);
- }
- else if (this.props.tileShape === "file_grid") {
+ } else if (this.props.tileShape === "file_grid") {
return (